2

I am new to Objective-C programming and Xcode. I inherited an old version (3.2.5) of a project, and I recently converted it to the newest version (4.5.2). I converted this project to ARC, and I am having issues with an objective-c object found within a typedef struct:

typedef struct _BitmapFontChar {
    int charID;
    int x;
    int y;
    int width;
    int height;
    int xOffset;
    int yOffset;
    int xAdvance;
    Image * image; // Objective-C object in struct forbidden in ARC
    float scale;
} BitmapFontChar;

When I try to use __unsafe __unretained_ it compiles fine in ARC, but the project doesn't work anymore. The *image object is not retained, of course, and my project crashes.

The struct is used as follows:

@interface BitmapFont : NSObject {

    GameController * sharedGameController;
    Image * image;
    BitmapFontChar * charsArray; // typedef struct
    int commonHeight;
    Color4f fontColor;
}​
...

charsArray = calloc(kMaxCharsInFont, sizeof(BitmapFontChar));

How do I convert this code to something that will retain the *image object, but will also work in ARC?

EDIT: Okay, I used Jano's suggestion using CFTypeRef. I'm still getting the same crash (EXC_BAD_ACCESS) at the same line of code. I think I'm not using the CFBridgingRetain properly. Below is my .h file:

#import "Global.h"

@class Image;
@class GameController;

#define kMaxCharsInFont 223


typedef struct _BitmapFontChar {
    int charID;
    int x;
    int y;
    int width;
    int height;
    int xOffset;
    int yOffset;
    int xAdvance;
    CFTypeRef image;
    float scale;
} BitmapFontChar;

enum {
    BitmapFontJustification_TopCentered,
    BitmapFontJustification_MiddleCentered,
    BitmapFontJustification_BottomCentered,
    BitmapFontJustification_TopRight,
    BitmapFontJustification_MiddleRight,
    BitmapFontJustification_BottomRight,
    BitmapFontJustification_TopLeft,
    BitmapFontJustification_MiddleLeft,
    BitmapFontJustification_BottomLeft
};

@interface BitmapFont : NSObject {

    GameController *sharedGameController;
    Image *image;
    BitmapFontChar *charsArray;
    int commonHeight;
    Color4f fontColor;
}

@property(nonatomic, strong) Image *image;
@property(nonatomic, assign) Color4f fontColor;

- (id)initWithFontImageNamed:(NSString*)aFileName ofType:(NSString*)aFileType 
  controlFile:(NSString*)aControlFile scale:(Scale2f)aScale filter:(GLenum)aFilter;

- (id)initWithImage:(Image *)aImage controlFile:(NSString *)aControlFile 
  scale:(Scale2f)aScale filter:(GLenum)aFilter;

-(void)renderStringAt:(CGPoint)aPoint text:(NSString*)aText;

-(void)renderStringJustifiedInFrame:(CGRect)aRect justification:(int)aJustification 
  text:(NSString*)aText;

-(int)getWidthForString:(NSString*)string;
-(int)getHeightForString:(NSString*)string;

@end

And below is part of my .m file with the method and line of code where the crash happens:

-(void)renderStringAt:(CGPoint)aPoint text:(NSString*)aText {
    float xScale = image.scale.x;
    float yScale = image.scale.y;

    for(int i=0; i<[aText length]; i++) {

        unichar charID = [aText characterAtIndex:i] - 32;

        int y = aPoint.y + (commonHeight * yScale) - (charsArray[charID].height 
            + charsArray[charID].yOffset) * yScale;
        int x = aPoint.x + charsArray[charID].xOffset;
        CGPoint renderPoint = CGPointMake(x, y);

        CFTypeRef vpImage = CFBridgingRetain(image); //???
        ((__bridge Image *)(charsArray[charID].image)).color = fontColor;//CRASH EXC_BAD_ACCESS

        [((__bridge Image *)(charsArray[charID].image)) renderAtPoint:renderPoint];

        aPoint.x += charsArray[charID].xAdvance * xScale;
    }
}

Thank you again for your suggestions!

user1873837
  • 23
  • 1
  • 4

3 Answers3

1

Use __unsafe_unretained, and reintroduce the explicit retain/releases of that particular struct field. Presumably this worked before, so there's no reason it can't work today.

Of course, ARC doesn't let you call -retain and -release. But it does let you call CFRetain() and CFRelease(), which do the same thing when called on an obj-c object.

Alternatively, if you want to convert your code to Obj-C++, you can embed __strong objects in C++ structs and the compiler will generate a correct destructor for you.

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
  • Sorry, I don't believe I have the option of converting the project to Obj-C++. I could be wrong, but I thought apple only takes Obj-C projects. Sorry, for not stating this before, but this project is an apple application. – user1873837 Dec 04 '12 at 19:45
  • 1
    @user1873837: You are misinformed. Apple doesn't care if you use Obj-C++, or C++, or even write everything in Haskell. – Lily Ballard Dec 04 '12 at 19:49
0

How do I convert this code to something that will retain the *image object, but will also work in ARC?

One way would be to convert struct _BitmapFontChar to a proper Objective-C class. Then it'd be fine to refer to other Obj-C objects.

Caleb
  • 124,013
  • 19
  • 183
  • 272
-1

What Kevin and Caleb said.

Since I wanted to try this myself here is a small example for CodeRunner:

#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
    @autoreleasepool {

        typedef struct _person {
            char name[25];
            CFTypeRef avatar;
        } person;

        NSObject *avatar = [NSObject new];
        CFTypeRef vpAvatar = CFBridgingRetain(avatar);
        person carol = { "Carol", vpAvatar }; 

        CFBridgingRelease(carol.avatar);
    }
}

CFRefType is just a void* which is the alternative to __unsafe_unretained, and the CFBridging functions cast an Objc pointer to Core Foundation (CFBridgingRetain) and back (CFBridgingRelease).

The easiest thing seems to be creating a lightweight object and let ARC handle it.

Jano
  • 62,815
  • 21
  • 164
  • 192
  • What does the code look like for a "lightweight object?" I would prefer to let ARC handle all of my objects. – user1873837 Dec 04 '12 at 05:20
  • One with just the variables you need. – Jano Dec 04 '12 at 14:21
  • I'm having issues implementing your suggestion to use CFTypeRef. I am getting an error with these lines of code: charsArray[charID].image.color = fontColor; [charsArray[charID].image renderAtPoint:renderPoint]; The error is: Member reference base type 'CFTypeRef' (aka 'const void *') is not a structure or union. ?? – user1873837 Dec 05 '12 at 22:02
  • Sounds like you are calling the method on a `void*`. You need to bring the object back with `(__bridge type*)`. Try `[((__bridge Image*)(charsArray[charID].image)) renderAtPoint:renderPoint];` – Jano Dec 06 '12 at 04:44
  • Thanks Jano! That worked great! The errors are gone, but my project still crashes at the same spot with the same EXC_BAD_ACCESS. I think I am not using the CFBridgingRetain properly. I am going to add my .h and part of my .m file to this thread. I should have done it initially. Maybe someone can see what I am doing wrong. – user1873837 Dec 06 '12 at 15:16