autoreleasing NSString in class method causing app crash in iOS - objective-c

The error I receive is as follows:
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil); //breakpoint that says Thread 1: Program Received Signal: "EXC_BAD_ACCESS".
[pool release];
return retVal;
}
My two questions can be found at the bottom of this post :)
I am currently working on an assignment for an iOS programming class and have hit a road bump.
I have found a fix, shown below, but it doesn't make sense to me. Check it out:
#implementation MyClass
// This class method takes an (NSMutableArray *) and returns an NSString with its contents printed out.
+ (NSString *)myString:(NSMutableArray)anArray
{
// NSString *myString = [[NSString alloc] init]; OLD CODE THAT CAUSES MEMORY LEAK
NSString *myString = [[[NSString alloc] init] autorelease]; //NEW CODE THAT RELEASES FIRST ALLOCATION OF myString WHEN THE FIRST stringByAppendingFormat: IS CALLED
NSString *vp = VARIABLE_PREFIX; //#defined above to be #"%
for (id object in anArray) {
if ([object isKindOfClass:[NSString class]]) {
if ([object hasPrefix:vp]) {
myString = [myString stringByAppendingFormat:#"%#",[object substringFromIndex:1]];
}else{
myString = [myString stringByAppendingFormat:#"%#",object];
}
}else if ([object isKindOfClass:[NSNumber class]]) {
myString = [myString stringByAppendingFormat:#"%#",object];
}
}
return myString; //shouldn't I autorelease myString right before this line? NO NOT ANY MORE. THIS myString IS NOT THE ORIGINAL THAT I alloc-init, BUT AN AUTORELEASED OBJECT RETURNED BY THE stringByAppendingFormat: message.
}
When I try to send the message [myString autorelease];, the program crashes with the above error. It is working fine now as shown above, but I do not understand why.
Every time I send a message containing the "magic words" alloc, init, copy I have to call release, it don't I? Or are the rules different in a Class method (can the Class itself own a file?). I do not call retain in the object that is calling this file.
Here are my two questions:
Why does this crash when I try to release theDescription using autorelease?
Does my code create a memory leak?
This is my very first question on stack overflow! Thank you for your help!

Why does this crash when I try to release theDescription using autorelease?
Assuming you mean myString, it crashes because myString is already autoreleased. You got it by calling -stringByAppendingFormat:, which returns an autoreleased string. Now, you're probably thinking: "But I created it by calling +alloc, so I should release it." That's true, but NSStrings are immutable, and when you call -stringByAppendingFormat: you get a different string back, and that string is autoreleased. Autoreleasing it a second time is an error.
Does my code create a memory leak?
Yes, but not really. The "leaked" object is the empty string that you allocate in the beginning. You never release that string, so you've got a leak. However, NSString is apparently optimized so that [[NSString alloc] init] returns a singleton, so in this particular case it doesn't make any difference that the empty string isn't released. The other strings that are assigned to myString are all autoreleased, so none of those objects are leaked.

Why does this crash when I try to release theDescription using
With the updated code, the problem is that ypu are reassigning the pointer to myString using the methid which already returns an autoreleases object: stringbyappending, therefore if you call autorelease on this object which is already going to get autoreleased i will crash.
Aditionaly the first assugnment in the alloc init gives a memory leak when ypu reassign with stringbyappendingstring, since you lose the reference to the previously created string with alloc init and therefore you will never be able to release it.
Sorry for the formatting on my iPhone atm =)

Related

EXC_BAD_ACCESS on alloc

If this issue has been posted before, I can't find it.
I'm attempting to allocate and initialize an instance of NSString in the initialization method of a subclass of NSOperation (for use with NSOperationQueue). The NSString pointer is an ivar (not a property).
The program crashes with "EXC_BAD_ACCESS (code=EXC_I386_GPFLT)".
To isolate the problem, I've separated the alloc and init functions. The main thread is crashing on:
m_myString = [NSString alloc];
The code is in an "if" block:
if (someCondition)
{
m_myString = [NSString alloc];
m_myString = [m_myString initWithCString:aCharPointer encoding:NSASCIIStringEncoding];
}
else
{
m_myString = [NSString alloc];
m_myString = [m_myString initWitCString:aDifferentCharPointer encoding:NSASCIIStringEncoding];
}
Examining the thread shows that it's crashing on objc_release. I don't understand why release would be called on an object in the allocation method, but that seems to be the case...
It's worth mentioning that I successfully alloc and init another instance variable NSString in the same method before the if block.
Has anyone else run into this before, and if so, how'd you solve it?
I'd be glad to give more info upon request.

NSString copy or alloc? Do you feel lucky?

I have a thread that needs information from the GUI before starting. What I mistakenly tried to do at first was create pointers to the NSTextFields like so:
NSString *info = [gui_field stringValue];
//launch thread
[self performSelectorInBackground:#selector(myMethod:) withObject:info];
This caused problems when I tried to manipulate "info" from within the thread. I assume this is the case because technically, it was still pointing to the string representation of the NSTextField outside the thread.
This fixed the problem:
NSString *info = [[gui_field stringValue] copy];
I assume this made a copy (with its own memory space) that did not rely on the NSTextField at all. I also assume this should be thread-safe.
Is this the appropriate way to do this? I suppose I could have done this:
NSString *info = [[NSString alloc] initWithString:[gui_field stringValue]];
Are the two producing the same result? And do I have to explicitly call release on the string when using "copy" or is it autoreleased by default?
Update: or, perhaps I could just send a pointer to the thread, and copy the string with "autorelease," adding it to the thread's autorelease pool:
NSString *info = [gui_field stringValue];
//launch thread
[self performSelectorInBackground:#selector(myMethod:) withObject:info];
-(void)myMethod:(NSString*)info
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *copied_str = [[info copy] autorelease];
//do stuff
[pool drain];
//copied_str should now be history
}
This way, I don't have to worry about explicitly releasing copied_str. It will be gone once the thread ends.
No need to rely on luck :)
alloc, copy, new and mutableCopy mean you own the object. Both of those will give you a retained object. If you're managing memory, you need to release it.
By convention, other methods will give you an autoreleased object.
As an example, if you want an autoreleased object, you can call:
NSString *str = [NSString stringWithString:yourString];
See the memory management guide:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html
Specifically the four rules here:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
You own any object you create
You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy).
Finally, both will copy the string.
from the NSString docs:
initWithString:
Returns an NSString object initialized by copying the characters from another given string.
copy is from NSObject. It defines copy as:
Return Value
The object returned by the NSCopying protocol method copyWithZone:, where the zone is nil.
NSString implements the NSCopying protocol so copy will return a copy of the string.
There is one exception where the string isn't copied by initWithString - if you pass a string literal it will wrap the pointer to the constant and ignore retain/release. See here if you're curious: Difference between NSString literals
NSString *info = [[gui_field stringValue] copy];
NSString *info = [[NSString alloc] initWithString:[gui_field stringValue]];
Those do pretty much the exact same thing.
[self performSelectorInBackground:#selector(myMethod:) withObject:info];
-(void)myMethod:(NSString*)info
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *copied_str = [[info copy] autorelease];
No, you can't do that. Even accessing the GUI string object just to copy it could be enough for a crash.
I think this is a case where the usually recommended patterns for memory management really don't provide a great solution, so you can go outside them.
NSString *info = [[gui_field stringValue] copy];
//launch thread
//pass ownership to callee
[self performSelectorInBackground:#selector(myMethod:) withObject:info];
// myMethod owns info!
-(void)myMethod:(NSString*)info
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[info autorelease];
Another option is retain infoCopy as an instance variable somewhere. That would let you use normal memory management patterns, but it doesn't fit semantically. It's really not an instance variable, it's an argument.
"I asked for an argument."
"This is abuse."

Allocation and release problem

I'm trying to explain memory management on iOS to a friend and I'm showing him a wrong code. But when I'm launching the app, it's working and I don't know why.
Here's the snippet :
NSString *myString = [[NSString alloc] initWithString:#"myString"];
[myString release];
NSLog(#"%#",myString);
I don't understand why my NSLog is working ...
Do you have some explanations ?
Thanks !
There are two things to bear in mind with regard to your example.
As MByD explained, accessing an object that’s been deallocated is undefined behaviour. It might or might not crash your program — it depends on whether the memory that had been allocated to that object has been reused and what’s been put there. In some cases the memory hasn’t been reused yet (and you might think the object is still alive but that object is in fact a ghost object), in other cases the memory may have been reused by another Objective-C object (the program won’t crash but you’ll see a different object) and in other cases the memory may have been reused by something that’s not an Objective-C object (in which case the program will likely — but not necessarily — crash).
Your string object is a constant string. As explained in the answer to this question and its comments, a constant string is never deallocated. When you send -[NSString initWithString:] passing a constant string as the argument, Cocoa returns the original constant string so your code is effectively the same as NSString *myString = #"myString";. This is an internal Cocoa implementation detail though. Production code should always consider that objects returned by +alloc (and, usually, subsequent -init) are owned by the caller, should be released when the caller is not interested in them anymore, and won’t be available after deallocation.
For the sake of experimentation, try the following code:
#import <Foundation/Foundation.h>
#include <stdio.h>
int main(void) {
[NSAutoreleasePool new];
NSString *s1 = [[NSString alloc] initWithString:#"myString"];
NSString *s2 = [[NSString alloc] initWithString:#"myString"];
NSString *s3 = [[NSString alloc] initWithString:#"myString"];
NSString *s4 = [[NSString alloc] initWithString:#"myString"];
printf("s1 = %p\n", s1);
printf("s2 = %p\n", s2);
printf("s3 = %p\n", s3);
printf("s4 = %p\n", s4);
[s1 release];
[s2 release];
[s3 release];
[s4 release];
return 0;
}
Conceptually, s1, s2, s3, s4 should be different objects. However, by running this program you can see that they are effectively the same object (they have the same address):
$ ./a.out
s1 = 0x10080b090
s2 = 0x10080b090
s3 = 0x10080b090
s4 = 0x10080b090
This is an undefined behavior. You are not allowed to access this string, yet it might be available.

Function works even after pool drain please help

we add an object to the pool and when the pool is drained the objects are flushed out of memory, and if i don't add them into the pool they will remain into the memory and can be accessed after the calling of pool drain method. What i have done in my code is that i have not added the object of my class into the pool and have called the method after the pool drain here's my code
#import <Foundation/Foundation.h>
#interface Myclass : NSObject
{
}
-(void)fun;
#end
#implementation Myclass
-(void)fun
{
NSMutableArray *arr = [[NSMutableArray alloc]init];
char ch[10];
NSString *str;
for(int i =0;i<3;i++)
{
scanf("%s",ch);
str = [NSString stringWithCString:ch];
[arr addObject:str];
}
for(int i =0;i<3;i++)
{
NSLog(#"The values of mutable array are: %#", [arr objectAtIndex:i]);
}
}
#end
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Myclass *obj = [[Myclass alloc]init];
[obj fun];
// insert code here...
NSLog(#"\nEnter pool drain");
[pool drain];
[obj fun];
return 0;
}
now when the fun method is called after pool drain each time i add a value to the array i get an error which says
NSautoreleaseNoPool(): Object 0x105a80 of class NSCFString autoreleased with no pool in place - just leaking Stack:(0x4dlfof ox3de442)
but even after this msg is shown i continued to add the data to my array it was working fine but every time showed the above msg. Can you please tell me why is this so?
Also i wanted to know is their any function with the help of which we can clear the console screen i tried ncurses.h but was not able to do that.
Please help me out regarding these two problems
You don't add any of your objects to the autorelease pool. When it is drained, it is empty. To add objects to the autorelease pool you must autorelease them. e.g. in fun
NSMutableArray *arr = [[[NSMutableArray alloc]init] autorelease];
In main:
Myclass *obj = [[[Myclass alloc]init] autorelease];
With those two modifications, you will see one of two things happen:
on the second [obj fun] after the drain you might get an exception for sending a message to a dealloc'd object.
you might get lucky and the memory for obj is still intact on the second [obj fun] in which case you should see a message posted to the console log saying that arr will leak because there is no autorelease pool in place.
Edit following the comment
This message
NSautoreleaseNoPool(): Object 0x105a80 of class NSCFString autoreleased with no pool in place - just leaking Stack:(0x4dlfof ox3de442)
is occurring because the method invoked by this line:
str = [NSString stringWithCString:ch];
tries to put the string into an autorelease pool before returning it to you. However, you have already drained the only pool you ever had, so the attempt fails. The string will therefore leak.

Releasing of strings

I got a doubt that when should the strings are released.I am not made any allocation to the string is there any necessary to release the string?
No if you do not "allocate" the string they are auto released.
for example
NSString *aTestString = [NSString stringWithFormat:#"Hello %#",#"World"];
This string is auto released, so you do not have to call [aTestString release];
If you would do:
NSString *aTestString = [[NSString alloc] initWithFormat:#"Hello %#",#"World"];
Then you would need to release it by [aTestString release]; because you manually allocated.
Therefore it is wise to autorelease it, so you do not have to think of it later on
NSString *aTestString = [[[NSString alloc] initWithFormat:#"Hello %#",#"World"] autorelease];
But that would just be the same as the first piece of code I gave ya.
Back to the point, no you do not have to manually release it as long as you do not allocate it yourself.
Did you create the string via a call to alloc, new, or a method containing copy? Did you explicitly retain the string yourself? If you got the NSString from a CFStringRef, did you create the CFStringRef with a function that included create? If not, you don't have to do anything. If you did, you have to either release or autorelease the string.
Object allocation/deallocation rules
You need to call [Object release] if and only if:
You called [Object alloc]
You called [Object retain]
You called [Object new]
If you did not explicitly allocate or retain the object, then you need to release it. If you got the object via a class method, the method did something like this: return [[[Object alloc] init] autorelease];. This allocates a new object, but is autoreleased when the NSAutoReleasePool next gets a chance.