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.
Related
Using ARC, is it now OK to assign a string value like this:
self.userName = [[NSString alloc] initWithString:self.currentParsedCharacterData];
Or does that still cause a memory leak, thus requiring me to assign it like this:
NSString *tmpString = [[NSString alloc] initWithString:self.currentParsedCharacterData];
self.userName = tmpString;
The first snippet is just fine under ARC, and is the better of the two ways. Apple has an example like this in the Transitioning to ARC guide:
- (void)contrived {
Person *aPerson = [[Person alloc] init];
[aPerson setFirstName:#"William"];
[aPerson setLastName:#"Dudney"];
[aPerson setYearOfBirth:[[NSNumber alloc] initWithInteger:2011]];
NSLog(#"aPerson: %#", aPerson);
}
and says:
ARC takes care of memory management so that neither the Person nor the NSNumber objects are leaked.
The setYearOfBirth: message with an alloced NSNumber corresponds to your snippet.
The compiler understands that, in the first case, aPerson is going out of scope and needs to be released before that happens, and, in the second case, that there is no explicit reference to the NSNumber object and that it must be either released or put into the autorelease pool. It takes care of both these requirements on your behalf.
Yes, with ARC it is ok. In fact, I would recommend the first way you have it implemented.
Either of your examples is fine. There is no difference between the two as far as memory management/ARC goes.
I know this is probably the exact opposite of the questions asked on Stackoverflow, but something really weird happened to me today.
I was trying to show someone how to work with Instruments and NSZombies, so I tried to create a crash.
I declared a NSString, released it and then tried to access it but the app didn't crash
NSString* test = [[NSString alloc] init];
[test release];
NSlog(#"%#", test);
I even tried to release it twice, but it still didn't make the app crash, it just printed null.
Can anyone please explain me what I did wrong or where is the flaw in my logic?
Thank you
EDIT : I tried something like this too and still no crash
NSString* test = [[NSString alloc] init];
test = #"something";
NSlog(#"%#", test);
[test release];
NSlog(#"%#", test);
I even added two consecutive releases, and a test = nil; after a release, just to make sure.
NSString may sometimes behave strangely.
In your example, you allocated a NSString without data.
That's the reason why it doesn't crash.
Allocating a NSString without data has no sense, as NSString objects are immutable.
In such a case, NSString will return a kind of singleton instance, meaning you can't release it (well, you can, but it will have no effect).
Every time you allocate a NSString object this way, the same instance will be returned.
Try this instead:
NSString * test = [ [ NSString alloc ] initWithCString: "hello, world" encoding: NSUTF8StringEncoding ];
[ test release ];
NSLog( #"%#", test );
Then it will crash, as expected...
To prove what I have explained earlier, try this:
NSString * test1 = [ [ NSString alloc ] init ];
NSString * test2 = [ [ NSString alloc ] init ];
NSLog( #"OK: %p %p", test1, test2 );
You can see the same address is printed, meaning only one object is allocated (the "singleton" one, for NSString objects without data).
EDIT
I've seen on your comment that you tried to "give the string a value".
This is not possible using NSString, as they are immutable.
I guess you tried this:
NSString * s = [ [ NSString alloc ] init ];
s = #"hello, world";
In such a case, you are not giving a value!
You are re-assigning a pointer!
It means your s variable (which is a pointer) will then point to another string object.
And it also mean you won't be able to access your previously allocated string, as you just lost the pointer to it.
If you want string objects that can be changed, take a look at the NSMutableString class.
But keep in mind that changing a pointer value is not the same as changing an object's value!
There are two things going on in your example.
First, nothing else happened between your release and your NSLog that would modify the memory where your string was stored, so it happens to still be intact. You can't rely on that.
Second, the system has a special case for [[NSString alloc] init]. It returns a predefined object of type __NSCFConstantString that is never deallocated. If you print its reference count, you'll see that it is 2147483647 (0x7fffffff). That is a magic number indicating that any attempt to retain or release the object is ignored.
If you take a look at the retainCount on an NSString allocated with either #"some_string" or [[NSString alloc] init] then you'll see that it's -1. This is because the compiler has known what the object is at compile time and has managed to store it as a literal. So when you release it, it's not actually going to do anything. It's not an object that has been allocated at runtime - it's just always there in your application binary.
You could make your crash occur by doing something like this on Mac:
#import <Foundation/Foundation.h>
int main(int argc, char** argv) {
NSString *test = [[NSString alloc] initWithCString:argv[0] encoding:NSASCIIStringEncoding];
NSLog(#"test = %p", test);
[test release];
NSLog(#"test = %p", test);
[test release];
NSLog(#"test = %p", test);
return 0;
}
The string is now no longer known at compile time, so it will actually be an object allocated and have a positive retain count, etc.
Note that NSNumber (on Mac OS X Lion) also exhibits this behaviour if the number is known at compile time. This is even more interesting as it's an example of tagged pointers in action. Take a look at this blog article for a discussion on that. And here is some code to show it:
#import <Foundation/Foundation.h>
int main(int argc, char** argv) {
NSNumber *test = [[NSNumber alloc] initWithInt:1];
NSLog(#"test = %p", test);
[test release];
NSLog(#"test = %p", test);
[test release];
NSLog(#"test = %p", test);
return 0;
}
Note that also doesn't crash, it just prints out test = 0x183 3 times.
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."
I'm new to Objective-C and I am having some difficulty with understanding memory management.
So let's say I have a class and an instance variable of type NSString* that isn't tied to a property. Would this situation leak memory?
myString = [[NSString alloc] init];
//more program stuff
myString = [[NSString alloc] init];
Should I have called [myString release] before I set myString equal to a new object? Will this sort of code leak memory? Do I have to release an object like this every time I have the pointer point to a different object?
First, Apple's Memory Management Programming Guide is a great place to look for examples and instructions about memory management.
Now to your question. When you call myString = [[NSString alloc] init]; you are reassigning the pointer myString and as such lose access to the original NSString, thus creating a memory leak.
The general rule of thumb here is that for every alloc you should have a release and these must alternate appropriately. If you do
myString = [[NSString alloc] init];
// ...
myString = [[NSString alloc] init];
// ...
[myString release];
[myString release];
you are releasing the same instance twice which results in overreleasing and an error BAD-ACCESS. The correct thing to do is
myString = [[NSString alloc] init];
// ...
[myString release];
myString = [[NSString alloc] init];
// ...
[myString release];
so that each instance is correctly released.
For future, stick to the simple rule that for every alloc/copy/retain you should pair it with a release.
Yes this will leak.
By allocating and assigning another NSString instance to the same variable myString, you lose reference to the original content of myString and therefore the ability to send it a release message.
Both, the Clang static analyzer and the Leaks instrument should uncover this leak.
Yes, you need to release the first object before you reassign your variable to point to the second object. Otherwise, you lose track of your first object and you leak it because you can't release it. You are responsible for releasing the object because you created it (via alloc) as explained in the Memory Management Rules.
Also, bear in mind that [[NSString alloc] init] is creating an empty and immutable string (NSStrings are immutable meaning that they can not be changed after creation). There is little point in doing this.
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 =)