views:

16257

answers:

5

In the AppDelegate, I'm alloc'ing an instance defined in a static library. This instance has an NSString property set a "copy". When I access the string property on this instance, the app crashes with 'unrecognized selector sent to instance'. Xcode provides a code hint for the property, which means it is known in the calling app. The particular class is compiled into the static library target. What am I missing?

Adding some code.

//static library 
//ClassA.h
@interface ClassA : NSObject {
...
NSString *downloadUrl;
}
@property(nonatomic, copy) NSString *downloadUrl;

//ClassA.m
@synthesize downloadUrl;

In the calling app's appDelegate.

//app delegate header file
@interface myApp : NSObject <UIApplicationDelegate> {
ClassA *classA;
}
@property (nonatomic, retain) ClassA *classA;

//app delegate .m file
@synthesize classA;

- (void)applicationDidFinishLaunching:(UIApplication *)application {
classA = [[ClassA alloc] init];
//exception occurs here.  downloadUrl is of type NSCFNumber
classA.downloadUrl = @"http://www.abc.com/";
...}

Other classes in the app will get a reference to the delegate and call classA.downloadUrl.

A: 

In the code you posted, you're sending the setDownloadURL: setter to ClassA — that is, the class itself. You want to set the property of an instance.

Chuck
In the static class I have @property(nonatomic, copy) NSString *theString and theString is @synthesize. Is there something else?
4thSpace
I do notice when I mouse over theString on the caller side, its type is NSCFNumber. Should be NSString.
4thSpace
how do you access it?
stefanB
Yes - that was an error in the post, which I have corrected.
4thSpace
A: 

How are you importing ClassA into your AppDelegate Class? Did you include the .h file in the main project? I had this problem for a while because I didn't copy the header file into the main project as well as the normal #include "ClassA.h."

Copying, or creating the .h solved it for me.

Heat Miser
In the library, I have a .h that references all the other .h files. So, the calling app only needs to reference that particular .h file to get a reference to everything in the library. I can drag the ClassA header file into the app project and reference it from the .m file. Still get the same exception though.
4thSpace
Another thing you may want to try is doing self.classA.downloadUrl. That error is usually what you get when a variable is initialized to nothing, such as NSString *m. It gets set to some seemingly random int. You might try overriding the init method in the class and setting it to nil in there.
Heat Miser
I've tried overriding init but my breakpoint in there is never hit. However, if I Jump to Definition on ClassA where the alloc is, it goes to the correct file and I see my custom init.
4thSpace
I see below that you solved it. I think there is something strange about the linker with Objective-C classes vs normal C++ classes. Typically even though they are compiled in, I like to keep the header files in the main project, whenever I don't I seem to have problems like this. I'm glad you solved it!
Heat Miser
+3  A: 

1) Is the synthesize within @implementation block?

2) Should you refer to self.classA = [[ClassA alloc] init]; and self.classA.downloadUrl = @"..." instead of plain classA?

3) In your myApp.m file you need to import ClassA.h, when it's missing it will default to a number, or pointer? (in C variables default to int if not found by compiler):

#import "ClassA.h".

stefanB
Have already tried that. Still get exception. I have several other similar classes in the same library. None of them are having this issue.
4thSpace
Have you tried [classA setDownloadUrl: @"http://www.abc.com/"]; ....?
stefanB
... Of course the 'www' was shortened to web link in previous comment
stefanB
Yes - same outcome with unrecognized selector.
4thSpace
I have some NSInteger properties in ClassA that are all declared the same way. I can set only one of them. The others throw the same type of exception.
4thSpace
Is the synthesize within @implementation block? should you refer to self.classA = [[ClassA alloc] init]; and self.classA.downloadUrl = @"..." ?
stefanB
I've added a test string property to another class in the static library. I get the same results. Somewhere, the linking up must have changed.
4thSpace
so have you tried self.classA = ... instead of just classA ?
stefanB
Yes - but still same exception. Yes to #1 above. For #3, I put imports in the PCH file so each classes doesn't have to keep importing.
4thSpace
If I open the ClassA.o file in the calling app's build folder, I see getter and setter methods for downloadUrl.
4thSpace
Finally I have resolved it. I removed ClassA.h from the .h file in the library which references all .h files. I then dragged ClassA.h into the app project and added the imports. I was trying to follow the technique mentioned here github.com/joehewitt/three20/tree/master with his common.h. The weird thing is I still have lots of classes referenced through my common.h, which still work in the app project. I didn't have to drag their header files over
4thSpace
A: 

You should note that this is not necessarily the best design pattern. From the looks of it, you are essentially using your App Delegate to store what amounts to a global variable.

Matt Gallagher covered the issue of globals well in his Cocoa with Love article at http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html. In all likelyhood, your ClassA should be a singleton rather than a global in the AppDelegate, although its possible you intent ClassA to be more general purpose and not simply a singleton. In that case, you'd probably be better off with either a class method to return a pre-configured instance of Class A, something like:

+ (ClassA*) applicationClassA
{
    static ClassA* appClassA = nil;
    if ( !appClassA ) {
        appClassA = [[ClassA alloc] init];
        appClassA.downloadURL = @"http://www.abc.com/";
    }
    return appClassA;
}

Or alternatively (since that would add application specific stuff to what is possibly a general purpose class), create a new class whose sole purpose is to contain that class method.

The point is that application globals do not need to be part of the AppDelegate. Just because the AppDelegate is a known singleton, does not mean every other app global should be mixed in with it even if they have nothing conceptually to do with handling the NSApplication delegate methods.

Peter N Lewis
Irrelevant to issue at hand.
Justicle
A: 

Set flag -ObjC in Other linker Flag in your Project setting... (Not in the static library project but the project you that is using static library...) And make sure that in Project setting Configuration is set to All Configuration

mihirpmehta