【问题标题】:NSInvalidUnarchiveOperationException when unarchieving custom class取消归档自定义类时出现 NSInvalidUnarchiveOperationException
【发布时间】:2014-06-04 09:45:46
【问题描述】:

编辑: 通过在函数调用之前的某处添加QCAnnonce* a = [[QCAnnonce alloc] init];在我的代码中,我解决了问题,所以我猜我“将类引入运行时”。但是我现在有一个警告说“a”未使用,那么是否有一个运行时函数可以用来“将类引入运行时”?

我正在尝试创建一个客户端-服务器应用程序。使用 NSKeyed(Un)Archiver 归档和取消归档对象以进行传输。我的大多数对象都可以毫无问题地传输,但其中一个会引发 NSInvalidUnarchiveOperationException。

异常是在我在 initWithCoder: 中放置的断点之前调用的。

我已尝试在发送前存档/取消存档,效果很好,因此 initWithCoder 应该没有问题。

我尝试在我的服务器应用程序中创建一个客户端(而不是在作为我的客户端的单独应用程序中),他可以解码该对象。除了他们不在同一个应用程序中之外,我看不出我的客户和这个新客户之间有任何区别。

我的最佳猜测是apple doc 的这一部分:

例如,委托可能会加载一些代码来介绍类 到运行时并返回类,或者替换一个不同的类 目的。如果委托返回 nil,则取消归档中止和方法 引发 NSInvalidUnarchiveOperationException。

但我不知道“将类引入运行时”是什么意思。

这里是这个对象的编码/解码方法:

-(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"];
}

这是异常消息:

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
)

【问题讨论】:

  • 这里有一个有趣的问题 :) 你的类是在动态加载的库中定义的吗?

标签: objective-c macos runtime nsdata nskeyedunarchiver


【解决方案1】:

当您尝试通过 NSKeyedArchiver 对其进行解码时,似乎 Class 未加载到运行时。

如果没有加载,NSKeyedArchiver 就是找不到类 QCAnnounce。 (这就是它无法解码的原因)

要检查这一点,您可以查看 NSObject 协议的+ (void)load 方法。

在将类加载到进程的地址空间后,运行时会立即为每个类发送一次此消息。

对于作为程序可执行文件一部分的类,运行时会在进程生命周期的早期发送加载消息。对于共享(动态加载)库中的类,运行时会在共享库加载到进程的地址空间后立即发送加载消息。

您可以通过重载 +(void)load 方法并在此处设置断点来检查它,并在调用 unarchive 之前查看它是否已加载到运行时。

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

您的修复 [[QCAnnounce alloc] init] 有效,因为当运行时尝试向一个类发送第一条消息时,它需要向该类调用 +(void)initialize 并且在您收到初始化时,您的进程中的类应该有已收到负载。

【讨论】:

  • 我的课程在一个静态库中。 QCAnnounce 上不调用加载,但在此库中的其他对象上调用。如果我想加载这个类,除了调用随机方法,我应该怎么做?
  • 我通过在 xcode 的“Linked Framework and Libraries”下添加 libQC.a 来链接这个库。
  • 我通过在其他链接器标志中添加 -objC 解决了这个问题
【解决方案2】:

这里的问题是运行时没有加载我的静态库中存在的所有类。因为我没有直接调用那个类(只有 id),所以它从来没有加载过。

我通过在构建设置的“其他链接器标志”中添加 -objC 来修复它。

在Technical Q&A 中找到了一些关于为什么这是一个好主意的信息:

-objC 使链接器加载库中定义了 Objective-C 类或类别的每个目标文件

还有另外两个标志(相同的问答):

-all_load 强制链接器从它看到的每个存档中加载所有目标文件,即使是那些没有 Objective-C 代码的文件。 -force_load 在 Xcode 3.2 及更高版本中可用。它允许对存档加载进行更精细的粒度控制。每个 -force_load 选项后面必须跟一个存档路径,并且该存档中的每个目标文件都将被加载。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-17
    • 1970-01-01
    • 2010-11-19
    • 2015-09-18
    • 2015-04-18
    • 2018-12-12
    • 1970-01-01
    相关资源
    最近更新 更多