Crash after checking if NSDictionary can be archived - iphone

I have the following code to create an NSDictionary and then check if all of its values (and itself) are nil or not when being archived:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *openbook = [defaults objectForKey:#"OpenBookKey"];
NSString *book = [NSString stringWithFormat:#"UploadedWB%#", openbook];
Reachability *reachability = [Reachability reachabilityForInternetConnection];
NetworkStatus internetStatus = [reachability currentReachabilityStatus];
if (internetStatus != NotReachable) {
[activityIndicator setHidden:NO];
[activityIndicator startAnimating];
NSLog(#"Sending data to cloud");
NSInteger integer = [[defaults objectForKey:#"CurrentIndex"] integerValue];
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fileName = [NSString stringWithFormat:#"%#/BVAuthors.txt",
documentsDirectory];
NSString *fileName1 = [NSString stringWithFormat:#"%#/BVImages.txt",
documentsDirectory];
NSString *fileName2 = [NSString stringWithFormat:#"%#/BVTitles.txt",
documentsDirectory];
NSMutableArray *authors = [NSMutableArray arrayWithContentsOfFile:fileName];
NSMutableArray *images = [NSMutableArray arrayWithContentsOfFile:fileName1];
NSMutableArray *titles = [NSMutableArray arrayWithContentsOfFile:fileName2];
NSString *author = [authors objectAtIndex:integer];
UIImage *image = [images objectAtIndex:integer];
NSString *title = [titles objectAtIndex:integer];
NSLog(#"Sending %# by %#", title, author);
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:_pages, #"pagesarray", _pagessavedbg, #"pagessavedbgarray", author, #"author", image, #"image", title, #"title", nil];
// NSLog(#"DICT: %#", dict);
if([NSKeyedArchiver archivedDataWithRootObject:dict] != NULL)
{
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:dict];
NSLog(#"Here the data is used, but it crashes before this point");
}
else
{
NSLog(#"This is never called :(");
}
}
I always get a crash on [NSKeyedArchiver archivedDataWithRootObject:dict] != NULL and the following error appears:
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x1e8a8fd0> was mutated while being enumerated.'
*** First throw call stack:
(0x38e8c6c3 0x3b0a597f 0x38e8c1a5 0x3b1b0527 0x3b1b02b9 0x3b1af04f 0x3b1b0537 0x3b1ea455 0x3b1af04f 0x3b1ae8f5 0xcfeaf 0x3b24d6bd 0x388dc351 0x388dc218)
libc++abi.dylib: terminate called throwing an exception
(lldb)
Why is this occurring?

Are you doing that inside a cycle? (For or while?). Because for the looks of:
'*** Collection <__NSArrayM: 0x1e8a8fd0> was mutated while being enumerated.'
It seems so.

Related

Simple NSSArray regarding strings and NSRangeException

I have a simple question. I am designing some simple highscores code yet I am having alot of trouble with it, specifically storing and saving players names. I have not been programming for very long in obj-c, only 2 weeks. I am not quite sure what is going on when I use NSStrings in an array.
Here is the code for saving strings:
-(NSString *) getFilePath2 {
NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[pathArray objectAtIndex:0] stringByAppendingPathComponent:#"savedFile2.plist"];
}
-(void) savePlayerNameData {
NSArray *highScoreNames = [[NSArray alloc]
initWithObjects:
playerName1,
playerName2,
playerName3,
playerName4,
playerName5,
nil];
[highScoreNames writeToFile:[self getFilePath2] atomically:YES];
}
-(void) loadPlayerNameData{
// load data here
NSString *myPath = [self getFilePath2];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists) {
NSArray *values = [[NSArray alloc] initWithContentsOfFile:myPath];
playerName1 = [values objectAtIndex:0];
playerName2 = [values objectAtIndex:1];
playerName3 = [values objectAtIndex:2];
playerName4 = [values objectAtIndex:3];
playerName5 = [values objectAtIndex:4];
}
else {
NSLog(#"first Launch. no file yet");
}
}
I end up getting this error :
2012-08-15 22:25:30.780 Pig Fly![5304:f803] *** Terminating app due to uncaught exception 'NSRangeException', reason: '-[__NSCFArray objectAtIndex:]: index (0) beyond bounds (0)'
*** First throw call stack:
(0x14c5022 0xec5cd6 0x146da48 0x146d9b9 0x14bec30 0xbd21 0x62e2 0x964eb6 0x1499936 0x14993d7 0x13fc790 0x13fbd84 0x13fbc9b 0x13ae7d8 0x13ae88a 0x28626 0x1fdd 0x1f45)
terminate called throwing an exception(lldb)
However, what I find very interesting is that when I write similar code for saving int values, The program runs fine. This is the working int code:
-(NSString *) getFilePath {
NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[pathArray objectAtIndex:0] stringByAppendingPathComponent:#"savedFile.plist"];
}
-(void) saveData {
//save data here
NSArray *highScores = [[NSArray alloc]
initWithObjects:
[NSString stringWithFormat:#"%i",highScore1],
[NSString stringWithFormat:#"%i",highScore2],
[NSString stringWithFormat:#"%i",highScore3],
[NSString stringWithFormat:#"%i",highScore4],
[NSString stringWithFormat:#"%i",highScore5],
nil];
[highScores writeToFile:[self getFilePath] atomically:YES];
}
-(void) loadData{
// load data here
NSString *myPath = [self getFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists) {
NSArray *values = [[NSArray alloc] initWithContentsOfFile:myPath];
highScore1 = [[values objectAtIndex:0] intValue];
highScore2 = [[values objectAtIndex:1] intValue];
highScore3 = [[values objectAtIndex:2] intValue];
highScore4 = [[values objectAtIndex:3] intValue];
highScore5 = [[values objectAtIndex:4] intValue];
}
else {
NSLog(#"first Launch. no file yet");
}
}
Can anybody tell me what is going on? What are the differences that need to be kept in mind when writing NSArrays for ints and NSStrings?
Make sure that playerName1 is not NULL.

read and write using NSKeyedArchiver, ios

Below is a class to read and write data using nsarchive
Data.m
-(id)init {
self = [super init];
if(self) {
arr = [[NSMutableArray alloc] init];
}
return self;
}
-(NSString *)getPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath;
if ([paths count] > 0)
documentPath = [paths objectAtIndex:0];
NSString *draftDataPath = [documentPath stringByAppendingPathComponent:#"draftData.dat"];
return draftDataPath;
}
-(void)saveDataToDisk {
NSString *path = [self getPath];
[NSKeyedArchiver archiveRootObject:arr toFile:path];
}
-(void)loadDataFromDisk {
NSString *path = [self getPath];
self.arr = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
}
At later on, I am adding some objects into arr by doing
CustomerClass.m
- (void) viewDidLoad {
Data *data = [[Data alloc] init];
[data.arr addObject:myObject1]
[data.arr addObject:myObject2]
[data.arr addObject:myObject3]
[data saveDataToDisk];
}
At DisplayData.m, I want to check data.arr by
- (void) viewDidLoad {
Data *data = [[Data alloc] init];
[data loadDataFromDisk];
NSLog(#"length of array is %d",[data.arr count]);
}
On the console, I am getting
length of array is 1
I thought it should be 3 after all.
Please point out what I have just made a mistake in the middle of work if you have any clues about it.
So, I suspect that your "myObjects" are not NSCoding compliant. I just did this:
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:3];
[arr addObject:#"Hello"];
[arr addObject:#" "];
[arr addObject:#"World"];
BOOL ret = [NSKeyedArchiver archiveRootObject:arr toFile:[self getPath]];
NSArray *arr2 = [NSKeyedUnarchiver unarchiveObjectWithFile:[self getPath]];
NSLog(#"count = %d", [arr2 count]);
And the results was "count = 3"
I feel like there's too much code here to do what you're looking for. I think all you need is:
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:dataClass] forKey:NSUserDefaultString];
[[NSUserDefaults standardUserDefaults] synchronize];
to save it.
And:
NSData *someData = [[NSUserDefaults standardUserDefaults] objectForKey:NSUserDefaultString];
if (settingsData != nil)
{
dataClass = [NSKeyedUnarchiver unarchiveObjectWithData:settingsData];
}
to retrieve it.

initWithDocPath not found

it shows me warning that initWithDocPath not found when i want to get private directory content..
+ (NSMutableArray *)loadScaryBugDocs {
NSString *documentsDirectory = [VideoDatabase getPrivateDocsDir];
NSError *error;
NSArray *files =[NSFileManagerdefaultManager]contentsOfDirectoryAtPath:documentsDirectory error:&error];
if (files == nil) {
return nil;
}
NSMutableArray *retval = [NSMutableArray arrayWithCapacity:files.count];
for (NSString *file in files) {
if ([file.pathExtension compare:#"scarybug" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:file];
VideoNotepadViewController *doc = [[[VideoNotepadViewController alloc] initWithDocPath:fullPath] autorelease];
//shows warning here
[retval addObject:doc];
}
}
return retval;
}
You need to implement the initWithDocPath: method in VideoNotepadViewController. Apple doesn't provide a method by that name.

Problem with UISearchBar and NSMutableArray

I have a tableview with a search bar. I want to search my whole core data database.
I loaded everything into an array here:
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"Entity" inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
NSError *error = nil;
NSArray *array = [managedObjectContext executeFetchRequest:request error:&error];
self.myArray = array;
I'm then searching using this method but getting an error:
- (void) searchTableView {
NSString *searchText = searchBar.text;
NSMutableArray *searchArray = [[NSMutableArray alloc] init];
for (NSDictionary *dictionary in myArray)
{
NSString *value = [dictionary objectForKey:#"name"];
[searchArray addObject:value];
}
for (NSString *sTemp in searchArray)
{
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
[copyListOfItems addObject:sTemp];
}
[searchArray release];
searchArray = nil;
}
But when I search, I get the crash here:
MedicalCode *code = [self.copyListOfItems objectAtIndex:indexPath.row];
cell.codeLabel.text = code.description;
Error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ICD9Disease objectForKey:]: unrecognized selector sent to instance 0x1be6e0'
You are getting the error because you are asking for an object, but you're trying to set a string, in the line:
NSString *value = [dictionary objectForKey:#"name"];
Since you're getting a string, not an object, what you need to use is valueForKey:
NSString *value = [dictionary valueForKey:#"name"];
It looks like you're expecting the fetch request to return dictionaries, but it's actually returning instances of the class ICD9Disease.

Why can't I serialize this data?

I want to serialize an array and an integer. When I save only the integer, it works. When I add the array, it fails.
NSArray *tempArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [tempArray objectAtIndex:0];
tempArray = nil;
tempArray = [NSArray arrayWithObjects:
( (someOtherArray != nil) ? (id) someOtherArray : [NSNull null] ),
[NSNumber numberWithInt:someIntValue],
nil];
NSString *filePath = [NSString stringWithFormat:#"%#/%#.plist", docsPath, kDataFilename];
NSString *errString;
NSData *serializedStrings= [NSPropertyListSerialization
dataFromPropertyList:tempArray
format:NSPropertyListBinaryFormat_v1_0
errorDescription:&errString];
if( errString != nil ) {
NSLog(#"SaveData NSpropertyListSerialization error: %#", [errString description]);
[errString release];
}
// returns NO
BOOL success = [serializedStrings writeToFile:filePath atomically:YES];
The array may be nil so I do a check for that first. I also tried [NSNumber numberWithInt:0] instead of [NSNull null] just to see if it would work instead of null, but it didn't.
I also get: NSpropertyListSerialization error: Property list invalid for format
You can't store +[NSNull null] in a plist. You need a workaround like a placeholder value (even though NSNull is itself a placeholder value...)