When I build and analyze my project on XCode, I obtain a 'warning' on the following line:
NSString *contactEmail = (NSString *)ABMultiValueCopyValueAtIndex(emailInfo, 0);
The message is: Potential leak on object allocated on line ... and stored into contactEmail.
Is there any error on that line?
UPDATE
I get the same 'warning' with this line of code:
ABMultiValueRef emailInfo = ABRecordCopyValue(person, kABPersonEmailProperty);
But here, I can't do this:
[emailInfo release];
I'm developing for iPhone.
ABMultiValueCopyValueAtIndex is a "Copy" function, which follows the "Create Rule". You need to call CFRelease to release it after finish using it.
NSString *contactEmail = (NSString *)ABMultiValueCopyValueAtIndex(emailInfo, 0);
...
if (contactEmail != nil)
CFRelease((CFTypeRef) contactEmail);
The cast is somewhat pointless.
The line might leak, unless you release or autorelease it somewhere.
Edit: For brevity:
NSString *contactEmail = [(NSString *)ABMultiValueCopyValueAtIndex(emailInfo, 0) autorelease];
(The cast might still be pointless, I'm unsure as to how the compiler would handle trying sending a message directly to a CFTypeRef.)
Related
I am using the following line of code...
NSString *clientFirstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
The 'analyse' feature on Xcode is saying that this giving rise to a potential memory leak. I am not releasing clientFirstName at all as I have neither alloc or retain'd it.
However, I am conscious that ABRecordCopyValue may not be returning an object as say a command like [NSMutableArray arrayWithArray:someArray] would which might mean I am indeed creating a new object that I control and must release.
Keen to hear thoughts...
Any sort of copy returns an object with a retainCount of 1, so you need to release it via CFRelease().
See the doc:
You are responsible for releasing this object.
Refer to this link. It has same as yours:
NSString potential leak
Refer to answer of KennyTM:
ABMultiValueCopyValueAtIndex is a "Copy" function, which follows
the "Create
Rule".
You need to call CFRelease to release it after finish using it.
NSString *contactEmail = (NSString *)ABMultiValueCopyValueAtIndex(emailInfo, 0);
...
if (contactEmail != nil)
CFRelease((CFTypeRef) contactEmail);
Release your object clientFirstName using CFRelease.
Add the code below:
if(clientFirstName != nil)
{
CFRelease((CFTypeRef) clientFirstName);
}
NOTE: Do not forget to check if clientFirstName is not nil. It is better to have a practice of checking that object is not nil before executing any function on it as it saves us from potential crashes though not in this case, but in many cases
EDIT:
Also as #Dondragmer says in one of the comments below:
I think even
[clientFirstName release];
should solve the problem.
Let me know if you need more help.
Hope this helps you.
I don't fully understand how ObjectiveC reference counting works, but a small update in code fixed the Analyze warning:
Before:
NSString *firstName = (__bridge NSString*) ABRecordCopyValue(person, kABPersonFirstNameProperty);
After
NSString *firstName = (__bridge_transfer NSString*) ABRecordCopyValue(person, kABPersonFirstNameProperty);
I'm not sure if this is a simulator issue, but I don't remeber having this problem before when I was using the iPad 5.0 simulator and below (now I'm running iPad 5.1 simulator). I overrode the description method for my Condition object to be:
- (NSString *)description {
NSString *str = [[NSString alloc] initWithFormat:#"Condition: %#", _conditionName];
return [str autorelease];
}
I have an array of these objects. My values are all valid. When I do:
for (Condition *p in self.reportsArray) {
NSLog(#"%#", [p description]);
}
It logs all my values, and then it crashes at the end. When I look at Instruments with Zombies, the last 4 calls are
-[NSPlaceHolderString initWithBytes:length:encoding:]
+[NSString stringWithUTF8String:]
-[NSAutoreleasePool release]
-[NSPlaceholderString initWithFormat:locale:arguments:]
Am I overriding description correctly?
Edit:
In Instruments, I get: message was sent to a deallocated object (zombie) at address:0x8ccf190. On the app itself, I get EXC_BAD_ACCESS.
It seems like the string returned from your description method is being released too soon.
Try rewriting your method using the stringWithFormat: class method.
- (NSString *)description {
return [NSString stringWithFormat:#"Condition: %#", _conditionName];
}
Make sure _conditionName is not a primitive.
Because the format string "%#" expects an object.
The following code for some reason poradically works. I have checked the URL so many times it's not funny (It returns plain text that I would like to parse). The code was 100% functional then it just stopped working and started giving me a EXC_BAD_ACCESS error.
There is nothing in the debugging output to post other than a line saying the output is switching to the process twice. (Except sometimes something about a double release.)
So far (as much as I can remember) I have tried:
Reinstalling the app - it only has problems on the 'Default' run (not the first Run/initiate Run.)
Running the URL in the browser (chrome, firefox, IE...)
Putting the call in a #try / #catch block
Using retain
Using a temp NSAutoreleasePool
Splitting up / separating the elements of the call (along with loggin Everything - once it hits the error, nothing gets logged)
Using the dataWithContentsOfURL functions with the above
NSAutoreleasePool *tmpPool = [[NSAutoreleasePool alloc] init];
NSString *url_string = [self getNormalVersionDownloadURL];
NSLog(#"urlString: -%#-", url_string);
NSError *er;
NSURL *the_URL = [[NSURL URLWithString:url_string] retain];
NSString *version_String = [NSString stringWithContentsOfURL:the_URL encoding:NSASCIIStringEncoding error:&er];
NSLog(#"verions_string: -%#-", version_String);
if ([version_String length] < 16)
return;
[tmpPool release];
(NSAutoreleasePool and autorelease added due to http://discussions.apple.com/thread.jspa?threadID=1667544)
(Cashed page - http://webcache.googleusercontent.com/search?q=cache:8D7zlQdG9PMJ:discussions.apple.com/thread.jspa%3FthreadID%3D1667544+http://discussions.apple.com/thread.jspa%3FthreadID%3D1667544&cd=1&hl=en&ct=clnk&gl=us&source=www.google.com)
discussions.apple.com is currently down so I cannot read the discussion thread. At any rate:
NSString *url_string = [[self getNormalVersionDownloadURL] autorelease];
Does -getNormalVersionDownloadURL return an owned or a non-owned object? You only send -autorelease if the method returns an owned object.
NSError **er;
This should be NSError *er instead, or it should be initialised with the address of a variable of type NSError *. Since the latter is uncommon and unnecessary, the following assumes NSError *er.
NSURL *the_URL = [[NSURL URLWithString:url_string] autorelease];
+URLWithString: returns an NSURL object that you don’t own, hence you don’t (auto)release it.
version_String = [[NSString stringWithContentsOfURL:the_URL
encoding:NSASCIIStringEncoding error:er] autorelease]; //ERROR occurs here
Two problems:: +stringWithContentsOfURL: returns an NSString object that you don’t own, hence you don’t (auto)release it. Furthermore, the third parameter should be &er instead of er.
URLWithString and stringWithContentsOfURL are convenience methods and then already put the variable in autorelease I don't think you need to add autorelease while creating the_URL and version_String
try to remove autorelease ...
I'm trying to reduce the memory leaks in my app, so i used instruments to find all the leaks. I managed to remove almost all of the leaks, except a very annoying one.
Instruments is telling me that i have a lot of NSPlaceholderstring leaks.
The code that generated the leak (according to instruments) is:
if (nil == storedHash)
{
NSString *description = [[NSString alloc] initWithFormat:#"1 = %# 2= %d", uId, service];
self.storedHash = description; // This line is the leak according to instruments
[description release];
description = nil;
}
return storedHash
storedHash is define like this:
#property(copy) NSString* storedHash;
I tried everything i can think of:
I used retain instead of copy
I used an autorelease allocation of the NSString (stringWithFormat)
I tried wrapping the code with an autorelease pool
Nothing of the above changed the leak. (In some cases the type of the leaks change, but there are still leaks)
Ideas anyone?
Where do you release storedHash? Do you release it in dealloc?
Note that NSPlaceholdeString is an implementation detail; it is the class of the instance returned by NSString's +alloc method.
How about in the dealloc method? Did you release the storedHash in the dealloc method? And how about checking if (nil == self.storedHash)
You should use
#property(nonatomic, retain) NSString* storedHash;
instead copy. #property(copy) didn't release your old NSObject and you should do it yourself:
if (nil == storedHash)
{
NSString *description = [[NSString alloc] initWithFormat:#"1 = %# 2= %d", uId, service];
[self.storedHash release];
self.storedHash = description; // This line is the leak according to instruments
[description release];
// description = nil; // it's unnecessary
}
also you should release storedHash in dealloc.
If I have a method
- (void) myMethod:(NSString *)string {
[Object anothermethodWithString:string];
}
and I call
[Object myMethod:#"this is a string with no alloc statement"]
Do I need to do something like
- (void) myMethod:(NSString *)string {
NSString *string2 = [[NSString alloc] initWithFormat:#"%#", string];
[Object anothermethodWithString:string2];
[string2 release];
}
instead of the way I had myMethod before? I have misbehaving code that seems to be caused by a string being auto-released while a second method within another method is being called (like in the example). The second way I have myMethod fixed all of my problems.
So is a "non-alloc" string an auto-released string? I asked this question as a follow up to another question (which was completely unrelated and is why I am creating this post), and a few sources said that I don't need to reallocate the string. I am confused, because the behavior of my code tells me otherwise.
Dave's got it right. You only need to worry about calling release on objects that you new, alloc, retain, or copy.
The above rule works very well, but if you're curious and want to get into a lot of detail, I suggest reading the Memory management Programming Guide from Apple's docs. It's free and goes from basic concepts into a lot of details.
If you use : NSString *str = #"". It is kind of a constant, you don't need to do any memory management.
If you call from a method : NSString *str = [NSString stringWithFormat:#""], the str is already autoreleased.
If you manually alloc, init. You need to call release, or autorelease yourself.
The general memory convention is : if you do something with new, alloc, retain, or copy, you need to release it yourself, any other cases, the object is autoreleased, don't release it