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

2

10

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]
...
Doorway answered 2/5, 2012 at 15:24 Comment(0)
T
28

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.

Terena answered 2/5, 2012 at 15:33 Comment(3)
For future readers, I had to add the flag to 'other linking flags' under the 'linking' menu. (ios 6.1, xcode 4.3)Manteau
While this answer is often the solution, the other one, provided by Ace, is a great complimentary one, and covers another possible case.Hamid
The link is broken now, is there an archive of it?Outspeak
B
3

Even though the answer is already selected, I wish the solution to my issue was here, as simple as it may be. Be sure to check that your class is properly checked in the Target Membership field under the File Inspector pane. If you fail to do so then it's only natural to receive the unrecognized selector sent to class error.

Bandage answered 29/5, 2015 at 20:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.