Objective C - fatal error - objective-c

I have a problem with my Xcode app, When I push a button, my app crashes.
Here is my button's action, I also declared variables, strings, etc... but it isn't in this code:
{
NSLog(#" - Writing Data.plist Labels");
NSString *error;
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *plistPath = [rootPath stringByAppendingPathComponent:#"Data.plist"];
NSDictionary *plistDict = [NSDictionary dictionaryWithObjects:
[NSArray arrayWithObjects: compteur01, compteur02, compteur03, compteur04, compteur05, compteur06, nil]
forKeys:[NSArray arrayWithObjects: #"name1", #"name2", #"name3", #"name4", #"name5", #"name6", nil]];
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict
format:NSPropertyListXMLFormat_v1_0
errorDescription:&error];
if(plistData) {
[plistData writeToFile:plistPath atomically:YES];
}
else {
NSLog(#"Error writeToFile:plistData:labels");
[error release];
}
}

Aparently compteur01 is nil.
Most likely the other compteur0x are nil too. But for this message at least the first one is nil.
Make sure that none of the objects added to the dictionary are nil. Use NSNULL instead if you need them to represent a nil/null value.

Related

Writing NSArray into plist

I'm trying to save a NSMutableArray into a pre-existing plist.
When I try:
NSError *errorDesc;
NSPropertyListFormat format;
NSString *plistPath;
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0];
plistPath = [rootPath stringByAppendingPathComponent:#"Array_Label_Generiche.plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
NSLog(#"prima volta");
plistPath = [[NSBundle mainBundle] pathForResource:#"Array_Label_Generiche" ofType:#"plist"];
}
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSMutableArray *temp = (NSMutableArray *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&errorDesc];
if (!temp) {
NSLog(#"Error reading plist: %#, format: %lu", errorDesc, format);
}
contatore = (int)temp.count ;
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc]init];
[dictionary setObject:array_copiato forKey:[NSString stringWithFormat: #"%#", [alertView textFieldAtIndex:0].text]];
NSDictionary *dictionary_da_salvare = [[NSDictionary alloc ] initWithDictionary: dictionary];
//AGGIUNGO I DATI CREATI
NSLog(#"TEMP %#",temp);
[temp addObject:dictionary_da_salvare];
BOOL didWriteArray = [temp writeToFile:plistPath atomically:YES];
if (didWriteArray)
{
NSLog(#"Write to .plist file is a SUCCESS!");
}
else
{
NSLog(#"Write to .plist file is a FAILURE!");
}
In this case, output show me that "Write to .plist file is a FAILURE!".
In the Apple Guide, I read that I must serialize data before writing on the plist.
Then I tried to write these lines of code:
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:temp format:NSPropertyListXMLFormat_v1_0 errorDescription:&errorDesc];
if(plistData == nil){
NSLog (#"error writing to file: %#", errorDesc);
}
When I run the application, the output area reads:
error writing to file: Property list invalid for format (property
lists cannot contain objects of type 'CFType') Write to .plist file
is a FAILURE!
I think because the array test is NSMutableArray because I have read in the Apple Guide:
The NSPropertyListSerialization class provides methods that convert
property list objects to and from several serialized formats. Property
list objects include NSData, NSString, NSArray, NSDictionary, NSDate,
and NSNumber objects. These objects are toll-free bridged with their
respective Core Foundation types (CFData, CFString, and so on).

NSInvalidArgumentException [NSDictionary initWithObjects:forKeys:]: count of objects (0) differs from count of keys (1)

Got the error NSInvalidArgumentException [NSDictionary initWithObjects:forKeys:]: count of objects (0) differs from count of keys (1). I used following code for
NSString *errorDesc = nil;
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSDictionary *jsonDictionary = [parser objectWithString:responseData];
paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
documentsPath = [paths objectAtIndex:0];
plistPath = [documentsPath stringByAppendingPathComponent:#"json_keys.plist"];
NSDictionary *plistDict = [NSDictionary dictionaryWithObjects: [NSArray arrayWithObjects:jsonDictionary, nil] forKeys:[NSArray arrayWithObjects: #"json_keys", nil]];
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 errorDescription:&errorDesc];
if (plistData)
{
[plistData writeToFile:plistPath atomically:YES];
}
else
{
NSLog(#"Error in saveData: %#", errorDesc);
[errorDesc release];
}
Judging from error message, [parser objectWithString:responseData] returns nil in this case. So, [NSArray arrayWithObjects:jsonDictionary, nil] creates empty array. After that, you are trying to create NSDictionary, passing empty array as values and array with #"json_keys" string as keys. So, error message is pretty accurate in this case.
Fix for this situation depends on what you are trying to achieve.
You can simply return from your method and don't do anything in case, when jsonDictionary is nil:
// ...
NSDictionary *jsonDictionary = [parser objectWithString:responseData];
if (nil == jsonDictionary) {
return;
}
paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// ...
Another possibility is storing empty plistDict for this case:
NSMutableDictionary *plistDict = [NSMutableDictionary dictionaryWithCapacity:1];
if (nil != jsonDictionary) {
[plistDict setObject:jsonDictionary forKey:#"json_keys"];
}
Of course, you can also find out why [parser objectWithString:responseData] returns nil and fix that.

Creating a plist with multiple strings

I have a multiple strings I would like to write to one plist using objective c. Can anyone please tell me exactly how to do this? I appreciate it
As H2CO3 hinted, you could use NSArray's writeToFile:atomically: method.
For example:
NSArray *arr = #[
#"my first string",
#"my second string",
#"and the last one"
];
[arr writeToFile:#"./out.plist" atomically:NO]; // Or YES depending on your needs
Here's one possibility:
// Create the path that you want to write your plist to.
NSError *error = nil;
NSURL *documentsURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&error];
if (documentsURL == nil) {
NSLog(#"Error finding user documents in directory: %#", [error localizedDescription]);
return nil;
}
NSString *path = [[documentsURL path] stringByAppendingPathComponent:#"YourFile.plist"];
// Populate your strings and save to the plist specified in the above path.
NSString *kRoot = #"kRoot";
NSMutableDictionary *tempDict = [NSMutableDictionary dictionary];
tempDict[kRoot] = [NSMutableArray array];
[tempDict[kRoot] addObject:#"String 1"];
[tempDict[kRoot] addObject:#"String 2"];
[tempDict[kRoot] addObject:#"String 3"];
// Etc, add all your strings
if (![tempDict writeToFile:path atomically:YES])
{
NSLog(#"Error writing data to path %#", path);
}

Memory leak in NSMutableArray, can't autorelease

I am having trouble releasing tempArray below... tempArray is a leak and I have tried return [tempArray autorelease] and it causes a crash. Does anyone know how to get rid of the memory leak in tempArray?
+(NSMutableArray*) returnTheArray:(NSString*)thePath forTheKey:(NSString*)theKey {
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSString *plistPath;
NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES) objectAtIndex:0];
NSString *testString = [thePath stringByAppendingString:#".plist"];
plistPath = [rootPath stringByAppendingPathComponent:testString];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath]) {
plistPath = [[NSBundle mainBundle] pathForResource:thePath ofType:#"plist"];
}
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format
errorDescription:&errorDesc];
if (!temp) {
CCLOG(#"Error reading plist: %#, format: %d", errorDesc, format);
}
NSMutableArray *tempArray = [[NSMutableArray alloc] initWithArray:[temp objectForKey:theKey]];
return tempArray;
}
Well, the problem is that returnTheArray is not a name that the Analyzer will recognize as returning a retained value -- this is why it complains. So either you rename the method or return an autoreleased value. But if the latter you need to make sure that the "consumer" of the returned value appropriately deals with it -- retaining it if the value must persist beyond the next autorelease pool drain operation.
try:
NSMutableArray *tempArray = [NSMutableArray arrayWithArray:[temp objectForKey:theKey]];

NSArray writeToFile returning "is deprecated" - iPhone SDK

I'm trying to save some settings from my app to a plist file when it closes, then load them when the app launches. I have 4 numbers saved in an NSArray called array. The loading works fine because if I change the file, the app starts the way the file was changed.
This code works fine:
- (void)LoadSettings {
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:kSaveFileLocation];
NSArray *array = [[NSArray alloc] initWithContentsOfFile:finalPath];
clockFaceImageSlider.value = [[array objectAtIndex:0] integerValue];
HourTypeSwitch.on = [[array objectAtIndex:1] integerValue];
touchRotationSwitch.on = [[array objectAtIndex:2] integerValue];
accelerometerRotationSwitch.on = [[array objectAtIndex:3] integerValue];
}
This code works up to the save line:
- (void)SaveSettings {
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:kSaveFileLocation];
NSArray *array = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:clockFaceImageSlider.value], [NSNumber numberWithInt:HourTypeSwitch.on], [NSNumber numberWithInt:touchRotationSwitch.on], [NSNumber numberWithInt:accelerometerRotationSwitch.on], nil];
if (![array writeToFile:finalPath atomically:YES]) {
NSLog(#"error");
}
//The log does print error
}
Does anybody know how I can make it save to the plist?
~thanks
Dont write array's description NSString, just write the array instead:
[array writeToFile:finalPath atomically:YES];
Or if you want the strings you should use en encoding type
NSError *error;
[[array description] writeToFile:finalPath atomically:YES encoding:NSUTF8StringEncodingerror:&error];
but this will not write a plist.
Instead of taking the description (which is an NSString) of an NSArray and write it to a file, just use writeToFile:atomically of NSArray itself, as in
[array writeToFile:finalPath atomically:YES];
The reason writeToFile:atomically: of an NSString is deprecated is that it doesn't correctly account for the character encodings. Who told you to use description? That person/book should be punished...
That said, if you want to save just a few entries, it would be easier to just use NSUserDefaults, see here. Then all you have to do is
[[NSUserDefaults standardUserDefaults] setObject: ... forKey:#"..."]
and the framework does everything from preparing a plist path behind the scenes to saving it to the disk.