I have found that if I alloc a new object inside a Class method and return it to main() it seems to cause a memory leak when I no longer want the object.
For example, here is a simple Class that includes a Class method that returns an instance of itself:
#interface Stinker : NSObject
{
int a;
}
+(instancetype) makeAStink;
-(void) showThem;
-(void) setEm: (int) z;
#end
#implementation Stinker
-(void) showThem
{
NSLog(#"%d",a);
}
-(void) setEm: (int) z
{
a = z;
}
-(void) dealloc
{
NSLog(#"Arrrgggggh!");
}
+(instancetype) makeAStink
{
id temp = [[self alloc] init];
return temp;
}
#end
Now if I create an instance directly from main():
Stinker *aStink =[[self alloc] init];
and subsequently set aStink to nil:
aStink = nil;
the overridden dealloc method is called and the Argggggh! message is logged. That's fine and as expected.
But if I use the Class method I wrote to create an instance:
Stinker *aNewStink = [Stinker makeAStink];
the behaviour is different.
Now if I set aNewStink to nil, it will no longer point to the object but the object is not destroyed. dealloc is not called and the Arggggh message is not logged.
It seems like it still has an owner somewhere.
Of course when main() terminates the object is destroyed and dealloc is eventually called.
But this seems to suggest that unused and unloved objects are still hanging around on the heap until the program terminates.
Isn't this a memory leak?
Should I just avoid using Class methods to alloc new instances?
When using ARC, the following code
+(instancetype) makeAStink
{
id temp = [[self alloc] init];
return temp;
}
will be same with Non-ARC like this:
+(instancetype) makeAStink
{
id temp = [[self alloc] init];
return [temp autorelease];
}
Thanks to autorelease, aNewStink = nil will make aNewStink do release in next runloop.
So if you do this:
#autoreleasepool {
Stinker *aNewStink = [Stinker makeAStink];
aNewStink = nil;
}
Dealloc method is called immediately.
this is MRC (without ARC) code for your example
+(instancetype) makeAStink
{
id temp = [[self alloc] init];
return [temp autorelease];
}
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [Stinker makeAStink]; // obj is autoreleased object
id obj2 = [[Stinker alloc] init]; // obj2 is not autoreleased
[obj2 release]; // so you need to release it
[pool release]; // now obj is released and deallocated
so obj have an extra retain count which will be released (and deallocated) in next runloop whereas obj2 will be released immediately when release is called
this is not memory leak, it is usual behaviour and (normally) doesn't affect program performance in noticeable way
Related
I'm confused about the lifetime of a block under ARC. I've written a unit test to demonstrate what is confusing me.
- (void)testBlock {
NSObject *testObject = [[NSObject alloc] init];
CompletionBlock testBlock = ^{ NSLog(#"%#", testObject); };
XCTAssertNotNil(testObject, #"testObject should not be nil");
__weak NSObject *weakTestObject = testObject;
#autoreleasepool {
testObject = nil;
}
XCTAssertNotNil(weakTestObject, #"testObject should be retained by testBlock");
#autoreleasepool {
testBlock = nil;
}
//THIS IS THE FAILING TEST CASE
XCTAssertNil(weakTestObject, #"testObject should have been released when testBlock was released");
}
I'm guessing that this behavior has something to do with how blocks are stored on the stack/heap.
Update!
Setting the block to nil doesn't release the block as I expected because it is on the stack and will not be released until it goes out of scope. Forcing the block to go out of scope fixes my test. Updated code below.
- (void)testBlock {
NSObject *testObject = [[NSObject alloc] init];
__weak NSObject *weakTestObject = testObject;
#autoreleasepool {
CompletionBlock testBlock = ^{ NSLog(#"%#", testObject); };
XCTAssertNotNil(testBlock, #"testBlock should not be nil");
XCTAssertNotNil(testObject, #"testObject should not be nil");
testObject = nil;
XCTAssertNotNil(weakTestObject, #"testObject should be retained by testBlock");
//testBlock goes out of scope here
}
XCTAssertNil(weakTestObject, #"testObject should have been released when testBlock was released");
}
Blocks are created on stack, and only become destroyed when scope exits, pretty much like C++ stack-allocated objects with destructors. These blocks are exempt from reference counting and will ignore retain/release messages. Only after being copied (via Block_copy() function or copy message) they become normal heap-allocated objects that can be retained and released.
In your example, the assert shall start working if you wrap all the code before it in extra curly brackets { }, so that the assert executes after block variable's scope ends.
I have a class like this:
#interface MyCollection : NSObject {
NSMutableDictionary *data;
}
and in the implementation of it, I have a method to init it like this:
- (id) init {
if(self = [super init])
{
self.data = [[NSMutableDictionary alloc] init];
}
return self;
}
Now when I create a object of this class in my code like this:
MyCollection *c = [[MyCollection alloc] init];
... at which point the Leaks utility shows that I have a memory leak in the init function on the very line where I try to set up the instance variable. I am totally new to Objective C & Iphone and I can't just get what is going wrong here. I have read through the Memory Management Guide and all, but I think I'm missing something pretty serious here.
Any help would be greatly appreciated. Thanks for your time already.
you are using self.data =. So there is most likely a property. And it most likely is a property which either copies or retains your object if you use it.
By calling
self.data = [[NSMutableDictionary alloc] init];
The retain count of the NSMutableDictionary increases because of the alloc, and if the property of data has a retain or copy statement you get another increase in retain count.
you could write data = [[NSMutableDictionary alloc] init]; or self.data = [NSMutableDictionary dictionary]. This would increase the retain count only one time.
And don't forget to release the object in dealloc.
You have to release the object in your dealloc method. That's why it's showing up as a leak.
to add to what fluchtpunkt mentioned you could try this instead:
- (id) init {
if(self = [super init])
{
self.data = [NSMutableDictionary dictionaryWithCapacity:0];
}
return self;
}
and in the dealloc
-(void)dealloc
{
self.data = nil;
}
I see weird situations with the Leaks utility as sometimes it reports old leaks, sometimes it doesn't report new ones, and so on. Also, from what I could collect with all your answers and opinion elsewhere on the web, people are divided on whether one should set a pointer to nil or not.
As of now, I have solved the situation with the following approach.
- (id) init {
if(self = [super init])
{
data = [[[NSMutableDictionary alloc] initWithCapacity:0];
}
return self;
}
-(void)dealloc
{
[data release];
}
Thanks everyone for contributing.
Are you creating the instance of "MyCollection" in the interface section?
If it has method scope try to release it in the same method after you are done with it.
Given the following property definition:
#property (nonatomic,retain) MyObject* foo;
does the following code cause a memory leak:
self.foo = [[MyObject alloc] init];
?
It looks like the alloc call increments the retain count on the object to 1, then the retain inside the property setter increases it to 1. But since the initial count is never decremented to 0, the object will stick around even when self is released. Is that analysis correct?
If so, it looks like I have two alternatives:
self.foo = [[[MyObject alloc] init] autorelease];
which is not recommended on the iPhone for performance reasons, or:
MyObject* x = [[MyObject alloc] init];
self.foo = x
[x release];
which is a bit cumbersome. Are there other alternatives?
Are there any alternatives?
No.
You are not going to be able write much of an iPhone application without using autorelease and the Cocoa Touch library uses them in many places. Understand what it's doing (adding the pointer to a list for removal on the next frame) and avoid using it in tight loops.
You can use class method on MyObject that does alloc/init/autorelease for you to clean it up.
+ (MyObject *)object {
return [[[MyObject alloc] init] autorelease];
}
self.foo = [MyObject object];
The easiest way to manage a retained property on the iPhone is the following (autorelease is not as bad as you think, at least for most uses):
-(id)init {
if (self = [super init]) {
self.someObject = [[[Object alloc] init] autorelease];
}
return self;
}
-(void)dealloc {
[someObject release];
[super dealloc];
}
The autorelease releases the reference to the floating instance which is assigned to self.object which retains its own reference, leaving you with the one reference you need (someObject). Then when the class is destroyed the only remaining reference is released, destroying the object.
As described in another answer, you can also create one or more "constructor" messages to create and autorelease the objects with optional parameters.
+(Object)object;
+(Object)objectWithCount:(int)count;
+(Object)objectFromFile:(NSString *)path;
One could define these as:
// No need to release o if fails because its already autoreleased
+(Object)objectFromFile:(NSString *)path {
Object *o = [[[Object alloc] init] autorelease];
if (![o loadFromFile:path]) {
return nil;
}
return o;
}
You are right, self.foo = [[MyObject alloc] init]; is leaking memory. Both alternatives are correct and can be used. Regarding the autorelease in such a statement: keep in mind that the object will released by the autorelease pool as soon as the current run loop ends, but it will most probably be retained a lot longer by self, so there is no issue with memory usage spikes here.
I'm using the singleton pattern in several places in an application, and I'm getting memory leak errors from clang when analyzing the code.
static MyClass *_sharedMyClass;
+ (MyClass *)sharedMyClass {
#synchronized(self) {
if (_sharedMyClass == nil)
[[self alloc] init];
}
return _sharedMyClass;
}
// clang error: Object allocated on line 5 is no longer referenced after this point and has a retain count of +1 (object leaked)
I'm using these settings for scan-build:
scan-build -v -v -v -V -k xcodebuild
I'm fairly certain that the code in the singleton is just fine - after all, it's the same code referenced here on Stack Overflow as well as in Apple's documentation - but I would like to get the memory leak warning sorted out so my scan-build returns success.
I may be being exceptionally dense, but surely your line 5
[[self alloc] init];
allocates an object of the containing class type, and promptly throws it away? Do you not want
_sharedMyClass = [[self alloc] init];
?
Apple has since updated their recommended singleton code to pass the static analyzer:
+ (MyGizmoClass*)sharedManager
{
if (sharedGizmoManager == nil) {
sharedGizmoManager = [[super allocWithZone:NULL] init];
}
return sharedGizmoManager;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedManager] retain];
}
Now +sharedManager calls super's -allocWithZone: and assigns the return of -init, and the singleton's -allocWithZone: just returns a retained sharedInstance.
Edit:
Why the retain in +allocWithZone:?
+allocWithZone: is overridden because someone using MyGizmoClass could circumvent the singleton by calling [[MyGizmoClass alloc] init] instead of [MyGizmoClass sharedManager]. It's retained because +alloc is expected to always return an object with a retain count of +1.
Every call to +alloc should be balanced with a -release or -autorelease, so without the retain in +allocWithZone:, the shared instance could potentially be deallocated out from under other users.
You may be interested in a simple, one-method, GCD-based singleton implementation (and thus 10.6+ only) posted on Mike Ash's site:
+ (id)sharedFoo
{
static dispatch_once_t pred;
static Foo *foo = nil;
dispatch_once(&pred, ^{ foo = [[self alloc] init]; });
return foo;
}
You are referencing self in a class method! Big no-no! Secondly, you are calling [[self alloc] init] and just throwing away the instance. You should assign the singleton reference in the class method, and not in init like I am guessing you are doing. Next, there is no real guarantee that _sharedMyClass will be initialized to zero. You should explicitly initialize it to nil.
static MyClass *_sharedMyClass = nil;
+ (MyClass *)sharedMyClass {
#synchronized(self) {
if (_sharedMyClass == nil)
_sharedMyClass = [[MyClass alloc] init];
}
return _sharedMyClass;
}
You also probably had this in there too...
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [super allocWithZone:zone];
return sharedInstance; // assignment and return on first allocation
}
}
return nil; // on subsequent allocation attempts return nil
}
The reason you weren't storing it in init is because you were storing it in the method that alloc called. This is the pattern Apple has in their examples. If you save the value in your init as well, all is fine and the warning goes away. I'd leave the allocWithZone implementation alone.
I am allocating myMDD in main which contains an NSMutableArray instance variable (alloc/init-ed in init). When I add items to the NSMutableArray (frameList) I release after adding. The array and the objects it now contains are released at the bottom of main.
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
MDD *myMDD = [[MDD alloc] init];
Frame *myFrame = [[Frame alloc] init];
[myMDD addFrame:myFrame];
[myMDD release];
[pool drain];
return 0;
}
// METHOD_ mdd addFrame:
-(void)addFrame:(Frame*) inFrame {
[frameList addObject:inFrame];
[inFrame release];
}
// METHOD_ mdd dealloc
-(void)dealloc {
NSLog(#"_deal...: %#", self);
[frameList release];
[super dealloc];
}
My question is that the "static analyser" reports a potential memory leak, prefering to have the release for frame added main. (i.e)
int main (int argc, const char * argv[]) {
...
[myFrame release]; // Added
[myMDD release];
[pool drain];
return 0;
}
// METHOD_ mdd addFrame:
-(void)addFrame:(Frame*) inFrame {
[frameList addObject:inFrame];
// [inFrame release];
}
I can see why this is, if I alloc myMDD and never call addFrame then I need to release it. Maybe its just a case of adding a autorelease to myMDD, but would that work in the situation where I call addFrame and the NSMutableArray is releasing the object?
EDIT_001
Changed to ...
int main (int argc, const char * argv[]) {
...
[myMDD addFrame:myFrame];
[myFrame release];
myFrame = nil;
[myMDD release];
[pool drain];
return 0;
}
// METHOD_ mdd addFrame:
-(void)addFrame:(Frame*) inFrame {
[frameList addObject:inFrame];
}
gary
The reason you got that warning is because an NSMutableDArray retains any object put into it; likewise, when an NSMutableArray is released, it also releases any object contained within it. So let's look at your code.
This line:
Frame *myFrame = [[Frame alloc] init];
creates a new instance of Frame called myFrame. myFrame has a retain count of 1, because you used alloc/init to create it.
You then pass this to addFrame::
[myMDD addFrame:myFrame];
Which in turn puts it into an instance of an NSMutableArray:
[frameList addObject:inFrame];
At this point, inFrame and myFrame point to the same object. When added to the array, this object's retain count is incremented, so now it is 2.
Later on, back in main, you release myMDD, which releases frameList. Assuming frameList now has a retain count of 0, it is deallocated -- and, as an NSMutableArray, it releases any object it contains, which includes the object pointed to my myFrame.
So now myFrame's retain count is 1...so it doesn't get released, and you have a memory leak.
One Cocoa-y way to solve the problem is by autorelease myFrame:
Frame *myFrame = [[[Frame alloc] init] autorelease];
Which means it won't leak. Then, use the -[MDD dealloc] method in your second example (Edit_001). You're right that you shouldn't release inFrame in your addFrame method, since you're not retaining it.
As per convention, an add method should just retain the object if needed, not release it. And as a general rule, you should not release object that you did not retain, in your example the scope where you retained (created) the frame is not the same as in the addFrame method.
By scope I mean logic scope, not language scope.
In that particular example, you must call release just after addFrame. But the release should not be in the addFrame method.
In most cases, Cocoa provides class methods that initialize and return an autoreleased version of an object. i.e. [NSMutableDictionary dictionary] vs [[NSMutableDictionary alloc] init].
I advise to always use the class methods where possible if you're creating an object that you won't need to keep around or if you going to store it in a collection (NSArray, NSDictionary, NSSet, etc).
The general rule then is to only alloc objects your class owns directly (i.e. an instace or class variable, not inside a collection) and to use the class methods for all other cases.