How to release NSString - iphone

NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [path objectAtIndex:0];
NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:#"DB"];
NSString *fileName = [newWordbookName stringByAppendingString:#".csv"];
NSString *fullPath = [databasePath stringByAppendingPathComponent:fileName];
[[NSFileManager defaultManager] createFileAtPath:fullPath contents:nil attributes:nil];
[databasePath release];
//[fileName release]; Error!
//[fullPath release]; Error!
//NSLog(#"#1 :databasePath: %d",[databasePath retainCount]);
//NSLog(#"#1 :fileName: %d",[fileName retainCount]);
//NSLog(#"#1 :fullPath: %d",[fullPath retainCount]);
I'm using this code and want to release NSString* ..
so, I declare fileName, fullPath, and databasePath of NSString.
database is released but fileName, fullpath doesn't release. I don't know why it happens.
I know that NSArray is Autoreleased. But is documentsDirectory autoreleased?
(newWordbookName is nsstring type)
I hope that I look through a document about iPhone memory management.

By convention the only two cases when a method returns a retained object are constructors i.e. alloc, new etc. and object-copying methods (containing copy in their name).
In all other cases the object is expected to be autoreleased, unless explicitly stated otherwise in the documentation.
This is the complete memory management documentation:
Cocoa Memory Management

You should not be calling release on any of the objects in the above code.
The reason the NSArray is autorelease'd is the same reason all the other objects are autorelease'd: the methods that assigned them their values called autorelease on them before they returned. In general, you can assume methods return autorelease'd objects if they do not have the word "create" or "new" in them. That is the general Cocoa convention. (Although 3rd party code may be goofy and do things differently, so caveat programmer).
You only really need to worry about objects you alloc or copy yourself; in other words, pair every alloc or copy with a release or autorelease.

Related

How to add string and key to dictionary in iPhone programmatically?

I used the following code to write values to dictionary, but when add new one to the dictionary it is not updating, it just displays the plist with only recently added value and it is crashing too.
nameString=nameTxt.text;
NSFileManager *mngr=[NSFileManager defaultManager];
NSArray *docDir=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath=[docDir objectAtIndex:0];
NSString *filePath=[docPath stringByAppendingPathComponent:#"score.plist"];
NSString *bundlePath=[[NSBundle mainBundle] pathForResource:#"score" ofType:#"plist"];
if ([mngr fileExistsAtPath:filePath]) {
NSLog(#"File exists");
}
else {
NSLog(#"NO file exists");
[[NSFileManager defaultManager] copyItemAtPath:bundlePath toPath:filePath error:NULL];
}
dict=[[NSMutableDictionary alloc]init];
dict=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
NSLog(#"dict is %#",dict);
[dict setObject:nameString forKey:#"100"];
[dict writeToFile:filePath atomically:YES];
[dict release];
I get crash when I used the last line "[dict release]"
I have a score.plist file in my bundle.
The crash is due to this line,
dict=[[NSMutableDictionary alloc]init];
dict=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
first line you are allocting memory and then you are overwriting the dict param to link to static dictionary which is not owned by you. So the old one is leaked and when you are releasing it tries to release the static one.
Instead of that use,
dict=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
and do NOT use release statement. Since you dont own it, you dont have to release it.
Check this
This is a simple memory problem.Along with solving the problem you have to understand the problem.
The dict is a NSMutableDictionary that you declared globally. And so that you can alloc it for using this so that you won't lose the scope of the dictionary.
So in the beginning say 'ViewDidLoad:' , you can alloc and init this as
dict=[[NSMutableDictionary alloc]init];
or in the present condition you can use like
dict=[[NSMutableDictionary alloc]initWithContentsOfFile: filePath];
So that you can alloc the dictionary with the score.plist file and everything will work fine.
What happended in your case is you alloced the dict. But in the next line you replace the alloced object of dict with autoreleaed object by the statement
dict=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
As the class methods always returns autoreleased objects, when you try to release the object which is autoreleased, it crashes. :-)
Hope you got the idea.
Now the solution is you can change the line
dict=[[NSMutableDictionary alloc]init];
To
dict=[[NSMutableDictionary alloc]initWithContentsOfFile: filePath];
And remove the line
dict=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
Everything will work. Happy Coding. :-)

memory problem when saving images with UIImagePNGRepresentation

My app makes use of lots of scaled images in animations. To avoid skipping frames, I scale my images and save them, before running the animation. Here is my code to save images:
+ (void)saveImage:(UIImage *)image withName:(NSString *)name {
NSData *data = UIImagePNGRepresentation(image);
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *directory = [paths objectAtIndex:0];
NSString *fullPath = [directory stringByAppendingPathComponent:name];
[fileManager createFileAtPath:fullPath contents:data attributes:nil];
}
Unfortunately, when I call this function repeatedly, I have memory problems. I guess I'm trying to save about 10MB worth of images. I'm thinking that perhaps the problem is with the autoreleased variables--perhaps I should alloc the data, and release at the end. But I can't find an alloc version of UIImagePNGRepresentation. Can anyone help?
UIImagePNGRepresentation returns an autoreleased NSData object. In other words, the data allocated will only be dealloced once you get to the release (or drain) call of the nearest enclosing NSAutoreleasePool block.
If you are calling the above code from within a loop, it's possible your code is never getting the chance to autorelease all that memory. In this sort of situation, you can enclose your calls in your own NSAutoreleasePool:
for (int i = 0; i < 10; i++) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self saveImage:someImage withName:#"someName.png"];
[pool drain];
}
N.B. I believe working with PNGs this way is quite slow compared to using JPGs (UIImageJPGRepresentation). Just FYI.
What does the outer loop look like? If it's something like:
for(n = 0; n < 1000; n++)
{
... something ...
[class saveImage:image withName:name];
}
Then leaving things in the autorelease pool could be your problem. The autorelease pool is drained only when the call stack completely unwinds back to the run loop (since otherwise you wouldn't be able to use autoreleased things as return results). Given that you're not releasing anything, you might try modifying your code to:
+ (void)saveImage:(UIImage *)image withName:(NSString *)name {
// create a local autorelease pool; this one will retain objects only
// until we drain it
NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
NSData *data = UIImagePNGRepresentation(image);
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *directory = [paths objectAtIndex:0];
NSString *fullPath = [directory stringByAppendingPathComponent:name];
[fileManager createFileAtPath:fullPath contents:data attributes:nil];
// drain the pool, which acts like release in reference counted environments
// but also has an effect in garbage collected environments
[localPool drain];
}
So, for each save of the image you create your own autorelease pool. The most recently initialised autorelease pool automatically sets itself to catch all autoreleased objects from then on. In a garbage collected environment like iOS, calling 'drain' on it causes it to be deallocated and all objects it holds instantly to be released.

Problem saving a plist

I was trying to use a plist to store an array with the below code:
NSString *name = firstName.text;
NSString *path = [[NSBundle mainBundle] pathForResource:#"Names" ofType:#"plist"];
NSMutableArray *namesArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
[namesArray addObject:name];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
[paths release];
NSString *docDirPath = [documentsDirectory stringByAppendingPathComponent:#"Names.plist"];
[namesArray writeToFile:docDirPath atomically:YES];
namesArray = [[NSMutableArray alloc] initWithContentsOfFile:docDirPath];
This code seems to work. Using NSLog, I have found that after this code executes the plist contains what I want it to, however, my program crashes because it generates an EXC_BAD_ACCESS on a device, and on the simulator it just crashes without an explanation. Does anyone know why that might happen?
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES); //Auto-released array
NSString *documentsDirectory = [paths objectAtIndex:0];
[paths release]; //Oh noes!
You don't own the reference to paths, so don't release it. Remove [paths release] and I'll bet you're fine. You're crashing because the autorelease pool is releasing paths after you've already done it yourself.
Quoth the guide:
You only release or autorelease objects you own. You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” ... or if you send it a retain message.
Have you checked, at which place it is giving EXC_BAD_ACCESS error.
In your code there are two wrong things; those are.
The Plist file consists a dictionary not an array, Here in the code you are copying the file data to an array. and saving the array to the plist file.
Second one is you are releasing the "paths" array, with out completion of usage of it. you have to release that array at the end of the statements; like after updating the array to the file.
Regards,
Satya

Writing an array of NSData to file

I am trying to save an array of images to the documents folder. I managed to save an image as NSData and retrieve it using the method below, but saving an array seems to be beyond me. I've looked at several other questions that relate and it seems I'm doing everything right.
Adding the image as NSData and saving the image:
[imgsData addObject:UIImageJPEGRepresentation(img, 1.0)];
[imgsData writeToFile:dataFilePath atomically:YES];
Retrieving the data:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"imgs.dat"];
[self setDataFilePath:path];
NSFileManager *fileManager = [NSFileManager defaultManager];
if([fileManager fileExistsAtPath:dataFilePath])
imgsData = [[NSMutableArray alloc] initWithContentsOfFile:dataFilePath];
So, writing an image as NSData using the above works, but not an array of images as NSData. It inits the array, but it has 0 objects, which isn't correct, since the array I am saving has several. Does anyone have any ideas?
First of all, you should brush up Cocoa Memory Management, the first line of code is a little bit of a worry.
For data serialisation, you may like to have a go with NSPropertyListSerialization. This class serialises arrays, dictionaries, strings, dates, numbers and data objects. It has an error reporting system, unlike the initWithContentsOfFile: methods. The method names and arguments are a bit long to fit on one line, so sometimes you may see them written with Eastern Polish Christmas Tree notation. To save your imgsData object, you can use:
NSString *errString;
NSData *serialized =
[NSPropertyListSerialization dataFromPropertyList:imgsData
format:NSPropertyListBinaryFormat_v1_0
errorDescription:&errString];
[serialized writeToFile:dataFilePath atomically:YES];
if (errString)
{
NSLog(#"%#" errString);
[errString release]; // exception to the rules
}
To read it back in, use
NSString *errString;
NSData *serialized = [NSData dataWithContentsOfFile:dataFilePath];
// we provide NULL for format because we really don't care what format it is.
// or, if you do, provide the address of an NSPropertyListFormat type.
imgsData =
[NSPropertyListSerialization propertyListFromData:serialized
mutabilityOption:NSPropertyListMutableContainers
format:NULL
errorDescription:&errString];
if (errString)
{
NSLog(#"%#" errString);
[errString release]; // exception to the rules
}
Check the contents of errString to determine what went wrong. Keep in mind that these two methods are being deprecated in favour of the dataWithPropertyList:format:options:error: and propertyListWithData:options:format:error: methods, but these were added in Mac OS X 10.6 (I'm not sure if they're available on iOS).

iPhone Memory Management with Properties of Singletons

I seem to have a fundamental gap in my memory management understanding. The code below is located within a singleton that gets called multiple times within my app to parse data that is downloaded from the web. For each article I download, I allocate a mutable string, then do tons of parsing, then write the file to the file system for later display in a UIWebView.
But every time I enter this method, I allocate a new "articleString". And I never release this string. I think this is a leak, but if I add a release at the bottom of this method (after the file is written), my app crashes the next time this method is called. I don't understand why it crashes, since another NSMutableString is allocated next time it is called.
UPDATE: I do release articleString in the dealloc method. But it still seems that I should release at the end of this method, since I alloc every time I enter.
UPDATE: articleString is defined as follows in the header:
#property (nonatomic, retain) NSMutableString *articleString;
the parseArticle method below is a placeholder for a series of methods that manipulate articleString.
self.articleString = [[NSMutableString alloc] initWithData:articleData encoding:NSUTF8StringEncoding];
//Parse the article for display
[self parseArticle];
//Write the article string to a file for later display
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"article.html"];
NSLog(#"%#", articleString);
[articleString writeToFile:path atomically:YES];
I like to let properties handle this for me. If the articleString property is set to retain then this is simple.
self.articleString = [[[NSMutableString alloc] initWithData:articleData encoding:NSUTF8StringEncoding] autorelease];
[self doStuff];
Then
- (void)dealloc {
self.articleString = nil;
[super dealloc]
}
article string will get released and properly retain when you set a new one. And it will be cleaned up on dealloc.