In ARC, does it create a memory leak to alloc into a #property (strong)
// MyClass.h
#property (strong) NSString *myString;
// MyClass.m
#synthesize myString=_myString;
- (void)viewDidLoad
{
self.myString = [[NSString alloc] initWithFormat:#"Test %#", otherString];
}
I know that in manual memory management, the equivalent would create a leak
// MyClass.h
#property (retain) NSString *myString;
// MyClass.m
#synthesize myString=_myString;
- (void)viewDidLoad
{
self.myString = [[NSString alloc] initWithFormat:#"Test %#", otherString];
}
- (void)dealloc
{
[_myString release];
}
Is ARC able to handle the top example correctly? Does it optimize away one of the retains? Or maybe release twice in the dealloc?
This is perfectly valid under ARC.
I would recommend reading the ARC documentation to get more comfortable with trusting what it has to offer. http://clang.llvm.org/docs/AutomaticReferenceCounting.html and Mike Ash has a great blog post on how it works http://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html
Related
I've been using code from Raphael Cruzeiro's PDF Annotator, and have discovered a number of memory leaks (ARC is off, and will stay off to support older devices). After patching most of them up, I'm down to the last couple, and they have me stumped. So in a class called PDFDocument, he has properties for a CGPDFPageRef, CGPDFDocument, and a custom annotation class #synthesize'd. I had to pepper his dealloc method with releases and eliminate some dangling pointers, which works well except for one small problem: After about 3 complete retain-release cycles, it crashes at the #synthesize line for his annotation object... I've never seen a SIGABRT because of a deallocated object sent during #synthesize, so naturally have no idea how to fix it. If I remove the release code in dealloc, it leaks, but if I leave it in, it crashes. Here's the code for the PDFDocument class:
//.h
#import <Foundation/Foundation.h>
#class Annotation;
#interface PDFDocument : NSObject {
Annotation *_annotation;
}
- (id)initWithDocument:(NSString *)documentPath;
- (NSInteger) pageCount;
- (void) loadPage:(NSInteger)number;
- (BOOL)save;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *hash;
#property (readwrite, nonatomic, assign) CGPDFDocumentRef document;
#property (readwrite, nonatomic, assign) CGPDFPageRef page;
#property (nonatomic, retain) NSString *version;
#property (nonatomic, assign) BOOL dirty;
#property (nonatomic, retain) Annotation *annotation;
#end
//.m
#import "PDFDocument.h"
#import "Annotation.h"
#import "HashExtensions.h"
#import "DocumentDeserializer.h"
#import "DocumentSerializer.h"
#implementation PDFDocument
#synthesize document;
#synthesize page;
#synthesize annotation = _annotation; //after 3rd cycle, it crashes here.
#synthesize name;
#synthesize hash;
#synthesize dirty;
#synthesize version;
- (id)initWithDocument:(NSString *)documentPath
{
if((self = [super init]) != NULL) {
self.name = [documentPath lastPathComponent];
if ([self.name isEqualToString:#"Musette.pdf"] || [self.name isEqualToString:#"Minore.pdf"] || [self.name isEqualToString:#"Cantata.pdf"] || [self.name isEqualToString:#"Finalé.pdf"])
{
CFURLRef ref = CFBundleCopyResourceURL(CFBundleGetMainBundle(), (CFStringRef)self.name, NULL, NULL);
self.document = CGPDFDocumentCreateWithURL(ref);
self.page = CGPDFDocumentGetPage(document, 1);
self.version = #"1.0";
DocumentDeserializer *deserializer = [[[DocumentDeserializer alloc] init] autorelease];
self.annotation = [deserializer readAnnotation:[[(NSURL*)ref absoluteString] stringByDeletingPathExtension]];
CFRelease(ref);
}
else {
CFURLRef pdfURL = (CFURLRef)[[NSURL alloc] initFileURLWithPath:documentPath];
self.document = CGPDFDocumentCreateWithURL(pdfURL);
self.page = CGPDFDocumentGetPage(document, 1);
self.version = #"1.0";
DocumentDeserializer *deserializer = [[[DocumentDeserializer alloc] init] autorelease];
self.annotation = [deserializer readAnnotation:[[(NSURL*)pdfURL absoluteString] stringByDeletingPathExtension]];
CFRelease(pdfURL);
CGPDFPageRelease(self.page);
}
}
return self;
}
- (NSInteger)pageCount
{
return CGPDFDocumentGetNumberOfPages(self.document);
}
- (void)loadPage:(NSInteger)number
{
self.page = CGPDFDocumentGetPage(document, number);
}
- (BOOL)save
{
DocumentSerializer *serializer = [[[DocumentSerializer alloc] init] autorelease];
[serializer serialize:self];
self.dirty = NO;
return !self.dirty;
}
- (void)dealloc
{
CGPDFDocumentRelease(self.document);
if (self.annotation != nil && _annotation != nil) {
[_annotation release];
self.annotation = nil;
} //my attempt to prevent the object from being over-released
self.document = nil;
self.name = nil;
[super dealloc];
}
#end
Then I ran it through Instruments to find zombie objects, and sure enough, Instruments found a deallocated object being sent a message at the exact same #synthesize line!
Does anyone have any idea what's going on and how to fix it?
This bit looks very wrong:
if (self.annotation != nil && _annotation != nil) {
[_annotation release];
self.annotation = nil;
}
Firstly, why are you checking self.annotation and _annotation for nil-ness. That's effectively doing the same check twice.
Secondly, you're using direct ivar access to release _annotation and then the setter for annotation will be releasing _annotation again and setting _annotation = nil. Effectively it's doing this:
if (self.annotation != nil && _annotation != nil) {
[_annotation release];
[_annotation release];
_annotation = [nil retain];
}
Which as you can see, is going to over-release _annotation.
Also, seriously, just use ARC. ARC is (mainly) compile time and has nothing to do with the device or OS version it's running on. The only bit that's not supported on pre iOS 5 is auto nil-ed weak pointers. But that really shouldn't be a problem as that's totally new in Lion / iOS 5 anyway.
I have this simple class which I know is ok in terms of memory leaks.
#interface location : NSObject {
NSString *name;
float lat;
float lon;
NSString *subtitle;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *subtitle;
#property (nonatomic, assign) float lat;
#property (nonatomic, assign) float lon;
#end
#implementation location
#synthesize name;
#synthesize lon;
#synthesize lat;
#synthesize subtitle;
-(void)dealloc{
[name release];
[subtitle release];
[super dealloc];
}
#end
There is retain in the #property so i release in the dealloc method. Now, my question is: If I alloc one of the strings in a init method or some other method I create, should I do another release? If so, when?
#implementation location
#synthesize name;
#synthesize lon;
#synthesize lat;
#synthesize subtitle;
-(void) init{
name = [[NSString alloc] init];
}
-(void)dealloc{
[name release]; // IS THIS CORRECT?!
[subtitle release];
[super dealloc];
}
#end
If you are assigning value using self. notation then you should release (as retain was called automatically, if you use synthesize) if your were using alloc + init approach for creating new object. Example:
NSString *str = [[NSString alloc] init];
self.name = str;
[str release];
If you are assigning value using self. notation and assigning autoreleased object then you shouldn't retain and release. Example:
NSString *str = [NSString string];
self.name = str;
If you are assigning value without self. prefix then you should not assign autorelease object and should not release, you just should alloc + init object. Example:
NSString *str = [[NSString alloc] init];
name = str;
Or if you want to assign autoreleased object without self. prefix then you should retain it. Example:
NSString *str = [NSString string];
name = [str retain];
In dealloc method you should release objects if you didn't do that earlier.
Basically you don't need retain here as you are not reatining anything. Your code is same as if all the properties are assigned.
until you don't call self.name, the retain semantics is not applied.
There is a code style which remove that confusion. keep the property name and instance name different and assign instance name to property name at #synthesize.
Like this:
#interface location : NSObject {
NSString *name;
}
#property (nonatomic, retain) NSString *nameProp;
#end
#implementation location
#synthesize nameProp = name;
I have this code in my .m file, which is a Cocos 2D CCLayer class. I initialize an array in the init method and then I try to use contents of this array in the nextFrame method. But when the nextFrame method gets called, the contents of the array seem empty. When I try to get the first item, I receive an error message saying:
Program received signal "EXC_BAD_ACCESS"
How can I successfully access the contents of this array in my nextFrame method?
NSMutableArray *cars;
-(id) init {
cars = [NSMutableArray array];
Car *car;
car = [[Car alloc] init];
[cars addObject:car];
self.isTouchEnabled = YES;
}
- (void) nextFrame:(ccTime)dt {
Car *car = [cars objectAtIndex:i]; // Program received signal "EXC_BAD_ACCESS"
}
Car.h
#import <Foundation/Foundation.h>
#import "cocos2d.h";
#interface Car : NSObject {
NSInteger type;
CCSprite *sprite;
}
#property (readwrite, assign) NSInteger type;
#property (retain) CCSprite *sprite;
#end
Car.m
#import "Car.h"
#implementation Car
#synthesize type;
#synthesize sprite;
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
- (void) dealloc {
[sprite release];
[super dealloc];
}
#end
You're assigning the result of [NSMutableArray array] to an instance variable. That is an autoreleased object, which essentially means it doesn't have any owners and thus will feel free to go away after the current runloop iteration*. You need to retain it if you want it to stick around (or just use [[NSMutableArray alloc] init], which returns an object you own).
* Basically. You should see the Cocoa memory mangement guide for more details. It's pretty short but full of essential information.
I have spent hours on this and read everybit on the web about memory management, zombies. leaks (tried instruments). But I cannot figure this one out. Does someone has a clue? I am getting a EXC_BAD_ACCESS at the following like of code on popping the ChildViewController.
[profileVO release]; profileVO = nil;
I strongly feel I have followed all memory management best practices!
Details:
I have a model file. (CachedProfileVO)
CachedProfileVO.h
#interface CachedProfileVO : NSObject {
NSString *name;
NSString *email;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *email;
#end
CachedProfileVO.m
#synthesize name, email;
- (void) dealloc
{
[super dealloc];
[name release]; name= nil;
[email release]; email = nil;
}
Now I have a UINavigationController. The ParentViewController and the ChildViewController.
I invoke the ChildViewController as follows:
[self.navigationCntroller pushViewcontroller:childViewcontroller animated:YES];
In the ChildViewController, I basically use the model CachedProfileVO. However when this view controller is popped (back button on UI), it gives a EXC_BAD_ACCESS
ChildViewController.h
#interface ChildViewcontroller : UITableViewController {
CachedProfileVO *profileVO;
}
#property (nonatomic, retain) CachedProfileVO *profileVO;
#end
ChildViewController.m
#synthesize profileVO;
- (void) dealloc
{
[super dealloc];
[profileVO release]; profileVO = nil; ****** GETTING EXE_BAD_ACCESS here
}
- (void) viewDidLoad
{
CachedProfileVO *vo = [CachedProfileVO alloc] init];
self.profileVO = vo;
[vo release];
}
//responseString looks like this: [Murdoch, murdoch#email.com][other data][more data]
- (void) populateProfile:(NSString *) responseString
{
NSMutableString *str = [[NSMutableString alloc] initWithCapacity:20];
[str setString:responseString];
[str deleteCharactersInRange: NSMakeRange(0,1)];
[str deleteCharactersInRange: NSMakeRange([str length]-1,1)];
NSArray *tempArray = [str componentsSeparatedByString: #"]["];
NSString *tempStr = (NSString*)[tempArray objectAtIndex:0];
NSArray *bioArray = [tempStr componentsSeparatedByString:#","];
self.profileVO.name = (NSString*)[bioArray objectAtIndex:0];
self.profileVO.email= (NSString*)[bioArray objectAtIndex:1];
[str release]; str = nil;
}
Note that the function populateProfile is called after some event. I know it is called. And dealloc then causes the problem. Also this does not happen in every pop. I have to try several times to reproduce. It is never reproduced using zombies in instruments!!!
You are calling [super dealloc]; first in your examples. That should always be the last call otherwise you are accessing instance variables that belong to a now deallocated class. The following should work fine if you followed memory management rules elsewhere.
- (void) dealloc
{
[profileVO release];
[super dealloc];
}
I have found a similar issue:
NSMutableArray addObject in for loop - memory leak
But none of those suggestions seem to fix my problem.
I have a simple loop where I'm creating an object and adding it to an array. When I try to release the object at the end of each loop the app crashes with "EXC_BAD_ACCESS". If I don't release the object I get leaked memory:
In .h
NSMutableArray *mainlist;
...
#property (nonatomic, retain) NSMutableArray *mainList;
In .m
#synthesize mainlist;
...
for (int i = 0; i < [self.objects count]; i++) {
MyObj *myObj = [[MyObj alloc] init];
myObj.title = [[self.objects objectAtIndex: i] valueForKey: #"title"];
[self.mainlist addObject:myObj];
[myObj release]; // crashes with release
}
MyObj just has some properties:
#property (nonatomic, retain) NSString *title;
#property (nonatomic, retain) NSString *date_text;
...
#synthesize title;
#synthesize date_text;
- (void)dealloc
{
[super dealloc];
[title release];
[date_text release];
}
#end
Any help would be much appreciated.
Thanks.
Crashes cause you first call dealloc of superclass and then try to release attributes. Change this to:
- (void)dealloc
{
[title release];
[date_text release];
[super dealloc];
}
And also: I'm almost certain that your self.mainlist is nil, when you're adding objects there. Creating a property doesn't mean that the attribute would be initialized automatically.