"unrecognized selector sent to class" when calling category method from a library

cocoa, extension-methods, foundation, objective-c, unrecognized-selector

Solution

EDIT: Fixed broken link

I suspect you have run into a known problem of categories embedded in static libraries.

If I am right, try to compile with the linker flags `-ObjC` (and if that is not enough, also `-all_load`) and your problem should disappear.

Problem

Problem This question may seem a bit long, but I try to give as much information as possible, since I am really staggered by this. I am currently working an a library which should automate XML document parsing. But I am running into a problem now testing the library for the first time. I have a library class called `CSXDocumentLayout` which represents the layout of a document. This class contains the private method `- (NSError *)readLayoutDocument:(NSString *)fpath` called from an init method. ``` /* MARK: Reading in Layouts */ - (NSError *)readLayoutDocument:(NSString *)fpath { CSXDocumentLayout *layout; CSXXMLParser *parser; BOOL state; layout = [CSXDocumentLayout layoutDocumentLayout]; parser = [[CSXXMLParser alloc] initWithDocumentLayouts: [NSArray arrayWithObject:layout]]; parser.file = fpath; state = [parser parse]; if(state == NO || parser.error != nil) { return parser.error; } return nil; } ``` This method will read in an XML document representing the layout of an other XML document. It is parsed by the class `CSXXMLParser`, which I want to test. I create an object representing a layout document with `+[CSXDocumentLayout layoutDocumentLayout]`. This method is implemented in the category `CSXDocumentLayout (CSXLayoutObject)`. Below is my test file: ``` #import <Foundation/Foundation.h> #import <CeasyXML.h> int main(int argc, const char **argv) { NSAutoreleasePool *pool = [NSAutoreleasePool new]; NSString *file; file = [NSString stringWithUTF8String:__FILE__]; file = [file stringByDeletingLastPathComponent]; file = [file stringByAppendingPathComponent:@"Layout.xml"]; CSXDocumentLayout *layout; NSError *error; layout = [[CSXDocumentLayout alloc] initWithLayoutDocument:file error:&error]; if(layout == nil) { NSLog(@"Could not create layout: %@", error); exit(0); } NSLog(@"Layout:\n%@", layout); [pool release]; return 0; } ``` This file compiles to a separate executable linked to my static library libceasyxml.a. Everything compiles just fine without any warnings. But when I run it I get a unrecognized selector sent to class exception: ``` 2012-05-02 16:59:47.620 TestApp[1887:a0f] +[CSXDocumentLayout layoutDocumentLayout]: unrecognized selector sent to class 0x1000064c8 2012-05-02 16:59:47.791 TestApp[1887:a0f] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[CSXDocumentLayout layoutDocumentLayout]: unrecognized selector sent to class 0x1000064c8' *** Call stack at first throw: ( 0 CoreFoundation 0x00007fff83e47784 __exceptionPreprocess + 180 1 libobjc.A.dylib 0x00007fff84604f03 objc_exception_throw + 45 2 CoreFoundation 0x00007fff83ea11a0 __CFFullMethodName + 0 3 CoreFoundation 0x00007fff83e198ef ___forwarding___ + 751 4 CoreFoundation 0x00007fff83e15a38 _CF_forwarding_prep_0 + 232 5 TestApp 0x0000000100001512 -[CSXDocumentLayout(Private) readLayoutDocument:] + 49 6 TestApp 0x00000001000010d4 -[CSXDocumentLayout initWithLayoutDocument:error:] + 96 7 TestApp 0x0000000100001017 main + 179 8 TestApp 0x0000000100000f5c start + 52 9 ??? 0x0000000000000001 0x0 + 1 ) ``` I find it very disturbing that I cannot call the class method `+[CSXDocumentLayout(CSXLayoutObject) layoutDocumentLayout]`, though I can call both `-[CSXDocumentLayout initWithLayoutDocument:error:]`, and `-[CSXDocumentLayout(Private) readLayoutDocument:]`. Research I checked if the method is defined in the output files by running `nm file` and it is, well partly: In libceasyxml.a, it is defined (nm libceasyxml.a) ``` ... libceasyxml.a(CSXDocumentLayout+CSXLayoutObject.o): 0000000000000100 t +[CSXDocumentLayout(CSXLayoutObject) classAttributeLayout] 00000000000020e0 s +[CSXDocumentLayout(CSXLayoutObject) classAttributeLayout].eh 000000000000056b t +[CSXDocumentLayout(CSXLayoutObject) documentElementLayout] 0000000000002180 s +[CSXDocumentLayout(CSXLayoutObject) documentElementLayout].eh 0000000000000402 t +[CSXDocumentLayout(CSXLayoutObject) layoutDocumentLayout] 0000000000002148 s +[CSXDocumentLayout(CSXLayoutObject) layoutDocumentLayout].eh 0000000000000200 t +[CSXDocumentLayout(CSXLayoutObject) layoutElementLayout] 0000000000002110 s +[CSXDocumentLayout(CSXLayoutObject) layoutElementLayout].eh 0000000000000000 t +[CSXDocumentLayout(CSXLayoutObject) nameAttributeLayout] 00000000000020b0 s +[CSXDocumentLayout(CSXLayoutObject) nameAttributeLayout].eh 0000000000002098 s EH_frame1 0000000000001c49 s LC0 ... ``` In TestApp, it is NOT defined (nm TestApp), actually I can't find any method with the category name `CSXLayoutObject`. ``` ... 0000000100001271 t -[CSXDocumentLayout setDocumentClassString:] 00000001000013a8 t -[CSXDocumentLayout setElements:] 0000000100001490 t -[CSXDocumentLayout setName:] 00000001000014db t -[CSXDocumentLayout(Private) readLayoutDocument:] 0000000100004060 t -[CSXElementList dealloc] 00000001000040b0 t -[CSXElementList elements] ... ```

Original source