-[__NSDictionaryI isNSString__]: 发送到释放实例的消息

-[__NSDictionaryI isNSString__]: message sent to deallocated instance

我在这个问题上已经坚持了一个多星期了,显然之前没有人在 Whosebug 上报告过这个问题。请在向我介绍其他帖子之前仔细阅读我的描述,因为我已经阅读了所有帖子并且其中 none 有我的答案。

我有一个 NSDictionary 包含一个 NSNumber 和一个 NSStrings 的 NSArray,两个键都是 NSStrings。

现在 NSDictionary writeToFile 崩溃并出现错误: -[NSDictionaryI isNSString]: 发送到释放实例的消息

writeToFile 在 class 方法上调用如下:

@implementation AppDataStore

+ (void) saveData:(NSDictionary*)dataDictionary {

    NSArray *directories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documents = [directories firstObject];
    NSString *appDataFilePath = [documents stringByAppendingPathComponent:@"AppDataStore.plist"];
    [dataDictionary writeToFile:appDataFilePath atomically:YES];
}

值得注意的是,我正在将一个 NSMutableDictionary 传递给该方法,但这不是问题,因为它写入其他键值 NSMutableDictionaries 就好了,但不是这个包含和 NSArray。即使我取出 NSNumber 元素,所以字典只包含一个 NSArray,writeToFile 仍然会崩溃同样的错误。在模拟器和 iPhone.

上都崩溃

这是怎么回事?!

编辑 1(回应 Adam 的问题):

直到崩溃前的堆栈跟踪:

Stack trace : (
    0   MedList                             0x00009b39 +[AppDataStore saveData:] + 217
    1   MedList                             0x0000811d -[ViewController tableView:didSelectRowAtIndexPath:] + 1101
    2   UIKit                               0x010c894c -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1559
    3   UIKit                               0x010c8af7 -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 285
    4   UIKit                               0x010cddf3 __38-[UITableView touchesEnded:withEvent:]_block_invoke + 43
    5   UIKit                               0x00fe20ce ___afterCACommitHandler_block_invoke + 15
    6   UIKit                               0x00fe2079 _applyBlockToCFArrayCopiedToStack + 415
    7   UIKit                               0x00fe1e8e _afterCACommitHandler + 545
    8   CoreFoundation                      0x00b289de __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
    9   CoreFoundation                      0x00b28920 __CFRunLoopDoObservers + 400
    10  CoreFoundation                      0x00b1e35a __CFRunLoopRun + 1226
    11  CoreFoundation                      0x00b1dbcb CFRunLoopRunSpecific + 443
    12  CoreFoundation                      0x00b1d9fb CFRunLoopRunInMode + 123
    13  GraphicsServices                    0x03efd24f GSEventRunModal + 192
    14  GraphicsServices                    0x03efd08c GSEventRun + 104
    15  UIKit                               0x00fb88b6 UIApplicationMain + 1526
    16  MedList                             0x0000681d main + 141
    17  libdyld.dylib                       0x02f6bac9 start + 1
    18  ???                                 0x00000001 0x0 + 1
)
2015-02-16 21:39:37.762 MedList[14029:496685] *** -[__NSDictionaryI isNSString__]: message sent to deallocated instance 0x7974e910

编辑 2:我做了更多测试。我的 NSArray 中的 NSDictionary 在 class 方法调用之前(在 writeToFile 之前)被释放,并且仅在 didSelectRowAtIndexPath 之后被释放。为什么选择 table 行会自动释放包含在数组中的字典?这是一种奇怪的行为。有什么想法吗?

ozgur 的理论是对正在发生的事情的一个不错的猜测。另一种可能性是您正在编写的数组中包含一些奇怪的东西。 writeToFile:atomically: 及其亲戚都要求你写的整个 "object graph" 必须是 "property list" 个对象(类 的一个小列表:NSString、NSData、NSDate、NSNumber、NSArray , 或 NSDictionary)。任何不是对象图中任何容器或子容器中的对象之一的对象都会导致写入失败。

但是,我习惯看到的只是没有写入文件,而不是崩溃。你可能在你的数组中的某个地方有一个僵尸,尽管在 ARC 下这比在手动引用计数下发生的可能性要小得多。您将不得不向数组或数组内的容器添加一个 __unsafe_unretained 对象。

首先,感谢大家,尤其是 Duncan 和 Hot Licks 帮助我缩小了错误范围并最终发现了它。所以,我注意到我的 NSArray 中的一个 NSDictionary 在我离开 viewDidLoad 的范围后立即被释放,这意味着我实际上并不拥有那个 NSDictionary,尽管我认为我拥有。

问题是因为我通过另一个 class 方法调用创建了那个 NSDictionary,我天真地将其命名为 init,因此隐藏了我对返回的 NSDictionary 的实际非所有权,让我误以为我拥有它通过调用 init.

在我将 init 重命名为 "create," 然后在调用创建之前在我的 viewDidLoad 中正确分配初始化我的 NSDictionary 后问题解决了。现在保留了字典,错误消失了。

对于那些面临类似问题的人,我强烈推荐这个资源,这实际上是让我最终找到错误的原因:

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.pdf