Memory Question - objective-c

I have some code that results in a EXC_BAD_ACCESS error:
recordIDAsString = [
NSString stringWithFormat:#"%i", (int)abRecord.recordID
];
propertyIDAsString = [
NSString stringWithFormat:#"%i", (int)abProperty.propertyID
];
identifierAsString = [
NSString stringWithFormat:#"%i", (int)abProperty.identifier
];
recordIDAsString, propertyIDAsString, and identifierAsString are all defined in the interface. The code is contained in an editing view controller, and the three *AsString variables seem to work fine until the save button is pressed, when their values become invalid. However, I've discovered that the following code does work:
NSString *tempRecordIDAsString = [
NSString stringWithFormat:#"%i", (int)abRecord.recordID
];
NSString *tempPropertyIDAsString = [
NSString stringWithFormat:#"%i", (int)abProperty.propertyID
];
NSString *tempIdentifierAsString = [
NSString stringWithFormat:#"%i", (int)abProperty.identifier
];
recordIDAsString = [tempRecordIDAsString copy];
propertyIDAsString = [tempPropertyIDAsString copy];
identifierAsString = [tempIdentifierAsString copy];
I thought I only had to manage memory when I call alloc or init, so my question is: what is happening in the first code example that causes the memory to become invalid?

A string created via...
[NSString stringWithFormat:#"%i", (int)abRecord.recordID]
... is autoreleased. It will go away at the next autorelease pool -drain unless you -retain it.
You need to revisit the memory management guide:
http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

NSString stringWithFormat is a convenience constructor - meaning that it will the returned object is autoreleased so that you don't have to do that. The issue that causes, is that if you want to keep that value, you must retain or copy it. So even though you are storing a reference to the value, you are not incrementing its retainCount. So when the current run loop is complete, the autorelease pool will send a release to all its objects decreasing the retainCount by 1. If you use a retain, you will increment the retainCount by one, so that when autorelease releases the object, it will still have a retainCount until another release is sent - which should be done by you at some point - maybe in your dealloc.

Related

Retain and release nsstring

str is a NSString I own. It is my responsibility to release it,
since I call initWithString:
if (![[str substringFromIndex:str.length-1] isEqualToString:#"\n"])
{
str = [str stringByAppendingString:#"\n"];
}
if the line inside the if statement reached, I will lose the ownership of the str var.
so my app crashes with a zombie instance, when I release str later:
[str release];
All goes fine if the if statement is NO(false).
What can I do to maintain the ownership of str?
Note that str could be very long I don't want to init another NSString
You need to do normal memory management:
if (![[str substringFromIndex:str.length-1] isEqualToString:#"\n"])
{
NSString *newStr = [str stringByAppendingString:#"\n"];
[str release];
str = [newStr retain];
}
Keep in mind that stringByAppendingString: returns an autoreleased string (and it also creates a whole new string).
Suggestion 1: Use ARC. It solves these problems for you. This is by far the best solution.
As rmaddy says, Xcode has an automated tool for converting apps to ARC. Look in the edit menu, under refactor>Convert to Objective-C ARC. The process is fairly painless. It flags things it wasn't able to figure out on it's own (usually only a few things.) After you clean up those issues you are off and running and never have to worry about retain counts again.
Suggestion 1a: Make str a mutable string, as #rmaddy suggested.
Then your code would look like this:
[str appendString: #"\n"];
That's simpler, easier to read, more memory-efficient, and works exactly the same in both ARC and manual reference counting.
Failing that, change str to be a retained property
#property (nonatomic, retain) NSString *str);
Then use property notation:
if (![[self.str substringFromIndex: self.str.length-1] isEqualToString:#"\n"])
{
self.str = [self.str stringByAppendingString:#"\n"];
}
When you do that the setter for the property takes care of releasing the old object in the str property before assigning a new value to the property.
Be aware, though, that assigning an object to a retained property increases it's retain count. This will create a leak:
self.str = [NSString alloc] initWithFormat: #"number %d", value];
(because all alloc/init calls return objects with a retain-count of 1)
and then the property retains it again.
That code should be written like this:
self.str = [[NSString alloc] initWithFormat: #"number %d", value] autorelease];

Accessing a released NSString doesn't make the app crash

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.

autoreleasing NSString in class method causing app crash in iOS

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 =)

Am I leaking memory here?

Consider the following method, where I build a string and return it. I would like to release the building blocks of the string, but then the string is based on values that no longer exists. Now what?
Am I leaking memory and if so, how can I correct it?
- (NSString) getMiddahInEnglish:(int)day{
NSArray *middah = [[NSArray alloc] initWithObjects:#"Chesed", #"Gevurah", #"Tiferes", #"Netzach", #"Hod", #"Yesod", #"Malchus"];
NSString *firstPartOfMiddah = [NSString stringWithFormat: #"%#", [middah objectAtIndex: ((int)day% 7)-1]];
NSString *secondPartOfMiddah = [NSString stringWithFormat: #"%#", [middah objectAtIndex: ((int)day / 7)]];
NSString *middahStr = [NSString string#"%# She'bi#%", firstPartOfMiddah, secondPartOfMiddah];
[middah release];
[firstPartOfMiddah release];
[secondPartOfMiddah release];
return middahStr;
}
At the end of the method, the return string, middahStr has not been released. Is this a problem? Am I leaking memory?
Quite the opposite: You are over-releasing.
middah you alloc and init. Then you release it. All is well.
firstPartOfMiddah and secondPartOfMiddah, you call an NSString "stringWith" convenience method. This is equivalent to invoking alloc, init, and autorelease. Your releasing them is a mistake, as they are essentially given to autorelease to release later.
middahStr you call the convenience method, but return it. All is well. It will be released later by the autorelease pool.
Rule of thumb (I'm sure there are plenty of exceptions): If you don't alloc it, don't release it.

Does [NSMutableDictionary setValue: value forKey: key] retain NSString key?

When adding items to NSMutableDictionary using the setValue:forKey: method (I suppose this generalizes to any NSObject) does the dictionary retain the second parameter, the NSString?
For example:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
NSString *theString = #"hello";
int i;
for (i=0; i<[theString length]; i++){
NSNumber *myInt = [NSNumber numberWithInt:i];
NSString *character = [NSString stringWithFormat:#"%C",[theString characterAtIndex:i]];
[dict setValue: myInt forKey:character];
}
[dict release];
[pool release];
Clearly, there is no reason to release myInt in the loop, it is retained by dict so it can't be released until the end of the code. But is the same true of character? My thinking is that if NSMutableDictionary stores the string in some other way, then one could create a temporary pool around the loop and release those strings instead of waiting until the release of the dictionary.
I am also curious as to why retainCount of character is 7fffffff as if it is an NSConstantString, I would expect stringWithFormat to return an NSString object which would need retaining, but that doesn't seem to be the case.
It's very common in Cocoa for NSString parameters to be copied instead of retained. That's because you could have just as easily given the dictionary an instance of NSMutableString. Because the string's value could change, NSDictionary makes a copy.
But, regardless of how NSMutableDictionary really operates, you don't have to worry whether character needs to be retained. Once you've passed it to NSMutableDictionary as a parameter, it's really that class's problem to decide how to store the data, unless the documentation specifically tells you that retaining the objects are your responsibility.
I also wouldn't worry too much about the retainCount of any object. Following the retain count of an object too closely can lead you down rabbit holes that just make you spin your wheels.
Finally, I really don't think you need to create your own autorelease pool here. Unless you know with absolute certainty that theString is going to be very long, or you've already observed high memory utilization in Instruments, adding the autorelease pool is an unnecessary optimization.
You don't need to retain character there, the dictionary retains it when you set it as a key and your own code has no need to retain it.
You also don't need to worry about why the retain count isn't what you expect. Maybe the Foundation framework has Flyweight-like instances of a load of single-character NSString instances. In any case if you've got the memory management correct following the guidelines, you'll be OK regardless of what the framework's doing behind the scenes. http://iamleeg.blogspot.com/2008/12/cocoa-memory-management.html