I am stuck on this for over a week now, and apparently nobody has reported this issue before on stackoverflow. Please read my description carefully before referring me to other postings, because I have read them all and none of them has my answer.
I have an NSDictionary containing an NSNumber and an NSArray of NSStrings, with both keys being NSStrings.
Now NSDictionary writeToFile crashes with error: -[NSDictionaryI isNSString]: message sent to deallocated instance
writeToFile is called on a class method as such:
@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];
}
Of note, I am passing an NSMutableDictionary to the method, but that's not the issue because it writes other key-value NSMutableDictionaries just fine, but not this one that contains and NSArray. Even when I take out the NSNumber element, so the dictionary only contains an NSArray, writeToFile still crashes the same error. Crashes both on simulator and on iPhone.
What is going on?!
EDIT 1 (in response to Adam's question):
Stack trace up until right before the crash:
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
EDIT 2: I did a few more tests. The NSDictionary inside of my NSArray gets deallocated BEFORE the class method call (before writeToFile) and only AFTER didSelectRowAtIndexPath. Why would selecting the table row automatically deallocate the dictionary contained inside an array? This is bizarre behavior. Any thoughts?
First off, thank you all, especially Duncan and Hot Licks for helping me narrow down the bug and finally catching it. So, I noted that an NSDictionary inside my NSArray was being deallocated as soon as I moved out of the scope of viewDidLoad, meaning that I didn't actually own that NSDictionary, although I thought I did.
Problem was caused by me creating that NSDictionary by another class method call, which I had naively named init, hence concealing my actual non-ownership of the returned NSDictionary, fooling me into thinking I owned it just by calling init.
Problem solved after I renamed init into "create," then properly alloc-init-ed my NSDictionary in my viewDidLoad before calling create. The dictionary is now retained, and the bug is gone.
For those facing similar issues, I highly recommend this resource, which is what actually made me finally find the bug:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.pdf