- [__ NSDictionaryI isNSString__]:发送给解除分配的实例的消息

时间:2015-02-16 23:43:17

标签: ios objective-c nsdictionary writetofile

我现在已经坚持了一个多星期了,显然没有人在stackoverflow之前报告过这个问题。在将我介绍给其他帖子之前,请仔细阅读我的说明,因为我已经阅读了所有帖子,但没有一个人有我的答案。

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

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

在类方法上调用writeToFile:

@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(回应亚当的问题):

直到崩溃前的堆栈跟踪:

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在类方法调用之前(在writeToFile之前)和仅在AFTER didSelectRowAtIndexPath之前被释放。为什么选择表行会自动释放数组中包含的字典?这是奇怪的行为。有什么想法吗?

2 个答案:

答案 0 :(得分:0)

对于正在发生的事情,奥克古尔的理论是一个不错的猜测。另一种可能性是你正在编写的数组中包含一些时髦的东西。 writeToFile:atomically:及其亲属都要求整个"对象图"你写作必须是"属性列表"对象(一小类类:NSString,NSData,NSDate,NSNumber,NSArray或NSDictionary)。任何对象图中任何容器或子容器中没有对象的对象都会导致写入失败。

但是,我以前看到的只是没有写入文件,而不是崩溃。你可能在你的阵列中的某个地方有一个僵尸,尽管在ARC下它比在手动引用计数下更不可能。您必须将__unsafe_unretained对象添加到数组或数组内的容器。

答案 1 :(得分:0)

首先,谢谢大家,特别是Duncan和Hot Licks,帮助我缩小bug并最终捕获它。所以,我注意到,当我离开viewDidLoad的范围时,我的NSArray中的NSDictionary被释放,这意味着我实际上并没有拥有那个NSDictionary,尽管我以为我做了。

问题是由于我通过另一个类方法调用创建了NSDictionary,我天真地命名为init,因此隐瞒了我对返回的NSDictionary的实际非所有权,愚弄我以为我只是通过调用init来拥有它。

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

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

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