I am using this code to get book names from a config.plist file. However my memory management is problematic. The '[dict release]' breaks the app completely and it exits.
The code works when the '[dict release]' is removed but it causes memory leaks as far as I can tell.
bnames is a global NSMutableArray
What am I doing wrong?
- (NSString *)loadBookname: (NSInteger) bookToLoad {
bookToLoad = [self bookOrder:bookToLoad];
//---get the path to the property list file---
plistFileNameConf = [[self documentsPath] stringByAppendingPathComponent:#"Config.plist"];
//---if the property list file can be found---
if ([[NSFileManager defaultManager] fileExistsAtPath:plistFileNameConf]) {
//---load the content of the property list file into a NSDictionary object---
dict = [[NSDictionary alloc] initWithContentsOfFile:plistFileNameConf];
bnames = [dict valueForKey:#"BookNames"];
[dict release];
}
else {
//---load the property list from the Resources folder---
NSString *pListPath = [[NSBundle mainBundle] pathForResource:#"Config" ofType:#"plist"];
dict = [[NSDictionary alloc] initWithContentsOfFile:pListPath];
bnames = [dict valueForKey:#"BookNames"];
[dict release];
}
plistFileNameConf = nil;
NSString *bookNameTemp;
bookNameTemp = [bnames objectAtIndex:bookToLoad - 1];
NSLog(#"bookName: %#", bookNameTemp);
return bookNameTemp;
}
You need to allocate your array properly:
bnames = [[NSArray alloc] initWithArray:[dict valueForKey:#"BookNames"]];
Double check that your dict returns the right data type.
There does not appear to be anything wrong with the way you allocate NSDictionary (although you could also use the [NSDictionary dictionaryWithContentsOfFile:] and save yourself having to worry about the release.
Either way I would suggest the issue is not with the [release] but probably the line BEFORE release:
bnames = [dict valueForKey:#"BookNames"];
a) Where is that allocated. I don't see an allocation or declaration of it anywhere?
b) What type of value do you expect back?
Put a break point on it and make sure your getting what you expect or anything.
If dict is not already a strong property, make it one. Then, use self.dict when assigning to it (and keep the release).
I've found what appears to be a better solution to the issue. This lets iOS manage the memory.
//---finds the path to the application's Documents directory---
- (NSString *) documentsPath {
NSLog(#"Start documentsPath");
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
// NSLog(#"Found documentsPath 40");
NSLog(#"End documentsPath");
return documentsDir;
}
- (NSString *) configPath {
NSLog(#"Start configPath");
NSString *plistFileNameConf = [[self documentsPath] stringByAppendingPathComponent:#"Config.plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistFileNameConf]) {
plistFileNameConf = [[NSBundle mainBundle] pathForResource:#"Config" ofType:#"plist"];
}
NSLog(#"plistFile: %#",plistFileNameConf);
NSLog(#"End configPath");
return plistFileNameConf;
}
The following calls the above code as necessary:
NSString *Choice;
NSArray *properties;
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:[self configPath]];
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
if (!temp) {
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
Choice = [temp objectForKey:#"Choice"];
properties = [temp objectForKey:Choice];
Related
I'm trying to read and write data to my plist file, While the reading part goes well, the writing part does nothing.
I might be mistaken for the writing part - i can't see any changes in my file under my bundle - it is still empty after my changes, and when i close the app and open it again - i still see empty email address line.
The code for writing (and placing the plist in the document folder for future writings)
NSFileManager *fileManger=[NSFileManager defaultManager];
NSError *error;
NSArray *pathsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *doumentDirectoryPath=[pathsArray objectAtIndex:0];
NSString *destinationPath= [doumentDirectoryPath stringByAppendingPathComponent:#"userData.plist"];
NSLog(#"plist path %#",destinationPath);
if ([fileManger fileExistsAtPath:destinationPath]){
NSLog(#"database localtion %#",destinationPath);
//return;
}
NSString *sourcePath = [[[NSBundle mainBundle] resourcePath]stringByAppendingPathComponent:#"userData.plist"];
[fileManger copyItemAtPath:sourcePath toPath:destinationPath error:&error];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile: sourcePath];
NSString *emailAddress = (NSString *)[dict objectForKey: #"emailAddress"];
if([emailAddress isEqualToString:#""])
{
// Do stuff
}
And for writing
NSArray *pathsArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *doumentDirectoryPath =[pathsArray objectAtIndex:0];
NSString *destinationPath = [doumentDirectoryPath stringByAppendingPathComponent:#"userData.plist"];
NSMutableDictionary *plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile: destinationPath];
[plistDict setValue:#"myEmail#gmail.com" forKey:#"emailAddress"];
[plistDict writeToFile:destinationPath atomically: YES];
But as i said, nothing is changed in the file itself, and not even when i save, close the app and open it again (the string is always empty on my reading part)
Any help will be more than welcomed.
I've done a lot of object oriented programming in Java but im pretty new to objective C.
Am I doing the following properly.
Package.h
#interface Packages : NSObject
#property (nonatomic) NSArray *pictureArray;
#property (nonatomic) NSString *folderPath;
- (id)initWithPath:(NSString *)path;
-(NSArray *)getPackageItems;
-(NSString *)getFolderPath;
#end
Package.m
#implementation Packages
- (id)initWithPath:(NSString *)path
{
self = [super init];
if (self)
{
NSString *folderPath = [NSString stringWithFormat:#"%#/Objects/%#", [[NSBundle mainBundle] bundlePath], path]; //Path
NSArray *fileList = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:folderPath error: nil];
self.pictureArray = [fileList filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"pathExtension IN %#", #"png", #"(content BEGINSWITH %#)", #"object_"]];
}
for (NSString *s in self.pictureArray) {
NSLog(#"%#", s);
}
return self;
}
-(NSArray *)getPackageItems{
return self.pictureArray;
}
-(NSString *)getFolderPath{
return self.folderPath;
}
#end
Code in question inside the viewdidload of one of my views
NSMutableArray *packages = [[NSMutableArray alloc] init];
for(id key in temp){
NSLog(#"key=%# value=%#", key, [temp objectForKey:key]);
Packages *tmp = [[Packages alloc] initWithPath:[temp objectForKey:key]];
[packages addObject:tmp];
}
so this for loop is iterating over a plist that I have. A series of paths.
for each path, it is creating an instance of Packages and adding it to an array of packages.
However, when do I do this
NSArray *something = [packages[1] getPackageItems];
I get an empty result back no matter what!
and even when I do this
NSLog(#"%#", [packages[0] getFolderPath]);
It prints out null.
What am I doing wrong.
EDIT
This is how the picture folder looks like
The code to pull it use to work when all of the pictures where in the Objects directory without any of the sub directories. I hope I edited the code properly to handle the sub dir
Edit 2
NSError *error;
NSArray *fileList = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.folderPath error: &error];
NSLog(#"%#, %#", fileList , error);
on the line
NSString *folderPath = [NSString stringWithFormat:#"%#/Objects", [[NSBundle mainBundle] bundlePath], path]; //Path
which successfully prints
2014-03-23 21:25:32.200 ScrollBar[4475:60b] (
"object_eyes_0_0.png",
"object_eyes_10_1.png",
"object_eyes_10_2.png",
"object_eyes_10_3.png",
"object_eyes_10_4.png",
"object_eyes_10_5.png",
"object_eyes_10_6.png",
"object_eyes_10_7.png",
When I do
NSString *folderPath = [NSString stringWithFormat:#"%#/Objects/%#/objects", [[NSBundle mainBundle] bundlePath], path]; //Path
which outputs
2014-03-23 21:27:46.951 ScrollBar[4496:60b] (null), Error Domain=NSCocoaErrorDomain Code=260 "The operation couldn’t be completed. (Cocoa error 260.)" UserInfo=0x8e2dda0 {NSUnderlyingError=0x8e2d020 "The operation couldn’t be completed. No such file or directory", NSFilePath=/Users/dev/Library/Application Support/iPhone Simulator/7.1/Applications/8B55D440-5727-471E-9BCC-1513C190740E/ScrollBar.app/Objects/object_eye_0_0/objects, NSUserStringVariant=(
Folder
)}
But as you can see in the image above, the file structure does exist.
Solution:
I accepted an answer even though it wasnt the real answer, it was the clear path to find out what was wrong.
The problem was that you can not have referenced folders containing sub folders using the code provided. I just many folders.
in your initWithPath:, you have NSString *folderPath = which declare a local variable and assign some value to it. However you did not save the value so self.folderPath is nil
- (id)initWithPath:(NSString *)path
{
self = [super init];
if (self)
{
self.folderPath = [NSString stringWithFormat:#"%#/Objects/%#", [[NSBundle mainBundle] bundlePath], path];
NSArray *fileList = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.folderPath error: nil];
self.pictureArray = [fileList filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"pathExtension IN %#", #"png", #"(content BEGINSWITH %#)", #"object_"]];
}
for (NSString *s in self.pictureArray) {
NSLog(#"%#", s);
}
return self;
}
for logging error
NSError *error;
NSArray *fileList = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.folderPath error: &error];
NSLog(#"%#, %#", fileList , error);
NEVER do NSLog([Someclass someMethod]), use NSLog(#"%#", [Someclass someMethod]) instead
I am quite new to iOS and Objective-c.
I am trying to auto generate a pList in my app that looks like this.
I've so far been able to create the file making it a normal Value => Key file if i replace my for loop by
for (NSString* exercisePictureName in bigPictureData) {
[data setObject:exercisePictureName forKey:exercisePictureName];
}
but my problem is that I have no idea how to structure the logic at the end of my loop to create a file structure like shown in the picture. As it has to be exact.
Could anyone point me in the right direction on how to structure my loop so that it creates the file with the right format????
- (void) createImageListFromSource {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"exercisePictures.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path])
{
path = [documentsDirectory stringByAppendingPathComponent: [NSString stringWithFormat: #"exercisePictures.plist"] ];
}
//To insert the data into the plist
NSArray* bicepPictureData = [self getAllimagesThatStartWith:#"bicep-"];
NSArray* tricepPictureData = [self getAllimagesThatStartWith:#"tricep-"];
NSArray* absPictureData = [self getAllimagesThatStartWith:#"abs-"];
NSArray* chestPictureData = [self getAllimagesThatStartWith:#"chest-"];
NSArray* backPictureData = [self getAllimagesThatStartWith:#"back-"];
NSArray* bigPictureData = [bicepPictureData arrayByAddingObjectsFromArray:tricepPictureData];
bigPictureData = [bigPictureData arrayByAddingObjectsFromArray:absPictureData];
bigPictureData = [bigPictureData arrayByAddingObjectsFromArray:chestPictureData];
bigPictureData = [bigPictureData arrayByAddingObjectsFromArray:backPictureData];
NSArray* finalData = [[NSArray alloc] init];
for (NSString* exercisePictureName in bigPictureData) {
NSDictionary* data = [[NSDictionary alloc] initWithObjectsAndKeys:exercisePictureName,#"text",exercisePictureName,#"image", nil];
[finalData arrayByAddingObject:data];
NSLog(#"%#",data);
}
NSLog(#"%#",finalData);
[finalData writeToFile: path atomically:YES];
}
What you have is an array of dictionaries. Pseudocode to show the structure:
NSMutableArray* arr = [NSMutableArray array];
for (...) {
NSDictionary* d = #{#"image": something, #"text": somethingelse};
[arr addObject:d];
}
When you are all done, just save the array directly with writeToURL....
I know there are a multitude of questions about this on SO but I can't see where I am making the mistake and am hoping some extra eyes will help. I've verified the plist is in my bundle and it is also in my docs directory and it contains data. Here's a screen capture of the app package with the plist at top:
I pass the plist in from another class and have verified that it is the correct plist.
Here's my code:
-(id)init {
if (self = [super init]) {
//set up the appTracker
appTracker = [[OAI_AppTracker alloc] init];
//set up a file manager and error container
fileManger=[NSFileManager defaultManager];
//docs directory path
documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
//track event
appTracker.appEvent = #"File Manager initialized";
[appTracker recordEvent];
}
return self;
}
- (NSDictionary* ) readPlist {
NSError* error;
//set up dictionary to hold our app data
NSDictionary* appData;
//set up destination path
NSString* destinationPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#", plistToRead]];
if ([fileManger fileExistsAtPath:destinationPath]){
//read plist
appData = [[NSDictionary alloc] initWithContentsOfFile:destinationPath];
} else {
//file doesn't exist so we have to move it to the doc folder
NSString *sourcePath=[[[NSBundle mainBundle] resourcePath]stringByAppendingPathComponent:plistToWrite];
[fileManger copyItemAtPath:sourcePath toPath:destinationPath error:&error];
//now read the plist
appData = [[NSDictionary alloc] initWithContentsOfFile:destinationPath];
}
NSLog(#"%#", appData);
return appData;
}
My log shows NULL instead of the data in the plist. Appreciate any help as to what I am doing wrong.
To read your plist try something like this:
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"PlistFileName" ofType:#"plist"];
NSDictionary *plistData = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
Why would the following implementation of the Dictionary cause a memory leak? See the screenshot below as well. Practically all of the leaks there are from this method.
- (void) setLocation:(NSString *) location:(NSString *) turnPage {
NSLog(#"Start setLocation");
//---get the path to the property list file---
NSString *localPlistFileNameConf = [[self documentsPath] stringByAppendingPathComponent:#"Config.plist"];
NSMutableDictionary *copyOfDict;
//---if the property list file can be found---
if ([[NSFileManager defaultManager] fileExistsAtPath:localPlistFileNameConf]) {
//---load the content of the property list file into a NSDictionary object---
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:localPlistFileNameConf];
//---make a mutable copy of the dictionary object---
copyOfDict = [dict mutableCopy];
[dict release];
}
else {
//---load the property list from the Resources folder---
NSString *pListPath = [[NSBundle mainBundle] pathForResource:#"Config" ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:pListPath];
//---make a mutable copy of the dictionary object---
copyOfDict = [dict mutableCopy];
[dict release];
}
location = [self checkLocationValidity:location:turnPage];
[copyOfDict setValue:location forKey:#"Location"];
[self writeConfigToFile:copyOfDict];
NSLog(#"End setLocation");
}
You're not releasing copyOfDict anywhere. You own any object created with a method that starts with copy, so you need to release those objects. It's probably misreporting the source as the original dictionary due to a bit of trickery in the NSDictionary class cluster for efficiency reasons. Try running analyse over your code, it should point these things out to you.