Pergunta

edit: By adding QCAnnonce* a = [[QCAnnonce alloc] init];In my code somewhere before the function call, I solved the problem, so I guess that I "introduced the class to the runtime". But I have now a warning saying that "a" is unused, so is there a runtime function I can use to "introduce the class to the runtime"?

I am trying to create a client-server application. The objects are archived and unarchived for transmission using NSKeyed(Un)Archiver. Most of my objects are transmitted without any problem, but one of them raise an NSInvalidUnarchiveOperationException.

The exception is called before the breakpoint I placed in initWithCoder:.

I have tried to archive/unarchive before sending and this works well, so it shouldn't be an issue with initWithCoder.

I have tried to create a client in my server app (not in a separate app as my client) and he can decode the object. I can't see any difference between my client and this new client except that they are not in the same app.

The best guess I have is this part of the apple doc:

The delegate may, for example, load some code to introduce the class to the runtime and return the class, or substitute a different class object. If the delegate returns nil, unarchiving aborts and the method raises an NSInvalidUnarchiveOperationException.

But I have no idea what "introduce the class to the runtime" means.

Here are this object methods for encoding/decoding:

-(id)initWithCoder:(NSCoder *)aDecoder{
    self = [super init];
    if (self) {
        a_listeAnnonces = [aDecoder decodeObjectForKey:@"Cartes"];
        a_points = [aDecoder decodeIntForKey:@"Points"];
    }
    return self;
}
-(void)encodeWithCoder:(NSCoder *)aCoder{
    [aCoder encodeObject:a_listeAnnonces forKey:@"Cartes"];
    [aCoder encodeInt:a_points forKey:@"Points"];
}

Here is the exception message:

2014-06-04 11:27:34.681 myApp[3693:303] *** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (QCAnnonce)
2014-06-04 11:27:34.794 myApp[3693:303] (
    0   CoreFoundation                      0x00007fff8937a25c __exceptionPreprocess + 172
    1   libobjc.A.dylib                     0x00007fff8e898e75 objc_exception_throw + 43
    2   CoreFoundation                      0x00007fff8937a10c +[NSException raise:format:] + 204
    3   Foundation                          0x00007fff8a8acdd9 _decodeObjectBinary + 2349
    4   Foundation                          0x00007fff8a8ac34d _decodeObject + 288
    5   Foundation                          0x00007fff8a8d15a5 +[NSKeyedUnarchiver unarchiveObjectWithData:] + 92
    6   myApp                               0x000000010001365f -[QNProtocolWrap performMethod:withArgumentDatas:onObject:] + 543
    7   myApp                               0x000000010001261b -[QNConnection methodCall:withArguments:] + 251
    8   myApp                               0x0000000100012b23 -[QNConnection connectionBaseDidRecieveNewData:] + 211
    9   myApp                               0x0000000100012b8b -[QNConnection connectionBaseDidRecieveNewData:] + 315
    10  myApp                               0x000000010000e38c -[QNConnectionBase readInput] + 204
    11  myApp                               0x000000010000e591 -[QNConnectionBase stream:handleEvent:] + 449
    12  CoreFoundation                      0x00007fff892ebc81 _signalEventSync + 385
    13  CoreFoundation                      0x00007fff892ebac8 _cfstream_solo_signalEventSync + 328
    14  CoreFoundation                      0x00007fff892eb93f _CFStreamSignalEvent + 623
    15  CFNetwork                           0x00007fff81e4401a _ZN29CoreReadStreamCFStreamSupport19coreStreamReadEventEP16__CoreReadStreamm + 102
    16  CFNetwork                           0x00007fff81e43f89 _ZN20CoreReadStreamClient25coreStreamEventsAvailableEm + 53
    17  CFNetwork                           0x00007fff81f77a65 _ZN14CoreStreamBase14_callClientNowEP16CoreStreamClient + 53
    18  CFNetwork                           0x00007fff81e43ca9 _ZN14CoreStreamBase34_streamSetEventAndScheduleDeliveryEmh + 183
    19  CFNetwork                           0x00007fff81e43a32 _ZN12SocketStream40dispatchSignalFromSocketCallbackUnlockedEP24SocketStreamSignalHolder + 74
    20  CFNetwork                           0x00007fff81e43160 _ZN12SocketStream14socketCallbackEP10__CFSocketmPK8__CFDataPKv + 206
    21  CFNetwork                           0x00007fff81e43062 _ZN12SocketStream22_SocketCallBack_streamEP10__CFSocketmPK8__CFDataPKvPv + 64
    22  CoreFoundation                      0x00007fff892eb107 __CFSocketPerformV0 + 855
    23  CoreFoundation                      0x00007fff892ab661 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    24  CoreFoundation                      0x00007fff8929cd12 __CFRunLoopDoSources0 + 242
    25  CoreFoundation                      0x00007fff8929c49f __CFRunLoopRun + 831
    26  CoreFoundation                      0x00007fff8929bf25 CFRunLoopRunSpecific + 309
    27  HIToolbox                           0x00007fff89726a0d RunCurrentEventLoopInMode + 226
    28  HIToolbox                           0x00007fff897267b7 ReceiveNextEventCommon + 479
    29  HIToolbox                           0x00007fff897265bc _BlockUntilNextEventMatchingListInModeWithFilter + 65
    30  AppKit                              0x00007fff82a9526e _DPSNextEvent + 1434
    31  AppKit                              0x00007fff82a948bb -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 122
    32  AppKit                              0x00007fff82a889bc -[NSApplication run] + 553
    33  AppKit                              0x00007fff82a737a3 NSApplicationMain + 940
    34  myApp                               0x0000000100001262 main + 34
    35  myApp                               0x0000000100001234 start + 52
    36  ???                                 0x0000000000000003 0x0 + 3
)
Foi útil?

Solução 2

The problem here is that the runtime didn't load all the class present in my static library. As I had no direct call to that class (only id), it was never loaded.

I fixed it by adding -objC in "other linker flags" in the build settings.

Some informations about why this was a good idea were found in this Technical Q&A:

-objC causes the linker to load every object file in the library that defines an Objective-C class or category

There exists two others flags (same Q&A):

-all_load forces the linker to load all object files from every archive it sees, even those without Objective-C code. -force_load is available in Xcode 3.2 and later. It allows finer grain control of archive loading. Each -force_load option must be followed by a path to an archive, and every object file in that archive will be loaded.

Outras dicas

It seems that Class is not loaded to runtime at the moment when you are trying to decode it by NSKeyedArchiver.

If it's not loaded, NSKeyedArchiver just can't find class QCAnnounce. (And that's why it can't decode it)

To check this, you may look in the + (void)load method of NSObject protocol.

The runtime sends this message once per a class right after the Class is loaded into process's address space.

For classes that are part of the program's executable file, the runtime sends the load message very early in the process's lifetime. For classes that are in a shared (dynamically-loaded) library, the runtime sends the load message just after the shared library is loaded into the process's address space.

You can check this by overloading +(void)load method and setting breakpoint there and see if it's have been loaded to runtime before you are calling unarchive.

+ (void)load {
    NSLog(@"%@ loaded!", self);
}

Your fix [[QCAnnounce alloc] init] works because when Runtime is trying to send first message to a class, it needs to call +(void)initialize to that class and by the time you receive initialize, class in your process should have already received load.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top