Accessing self in NSBlockOperation – retain cycle? - objective-c

self.operationQueue = [[NSOperationQueue alloc] init];
[self.operationQueue addOperationWithBlock:^{
[self doSomethingElse];
}];
- (void)doSomethingElse {
[self doAnother];
}
Does this create a retain cycle? I keep a reference to the operation queue but not the operation. Thoughts?

This could create a retain cycle. Create a weak pointer to self and use that:
_weak MyObject *weakSelf = self;
EDIT:
In your block, create a strong reference back to self. Evaluate your pointer to make sure it's valid, and you're safe. Your snippet (based on what you've described, then), should read:
self.opeartionQueue = [[NSOperationQueue alloc] init];
_weak MyObject *weakSelf = self;
[[self operationQueue] addOperationBlock:^{
_strong MyObject *strongSelf = weakSelf; // Obtain a strong reference so our pointer won't dangle out from under us...
if(strongSelf) // Make sure it's valid
{
[strongSelf doSomethingElse]; // Do your work
}
}

Related

Objective-C Array of Objects

Although experienced with OOP, I am an absolute newbie with Objective-C. I have the following code:
// header files have been imported before this statement...
CCSprite *treeObstacle;
NSMutableArray *treeObstacles;
#implementation HelloWorldLayer {
}
-(id) init
{
// create and initialize our seeker sprite, and add it to this layer
treeObstacles = [NSMutableArray arrayWithObjects: nil];
for (int i=0; i<5; i++) {
treeObstacle = [CCSprite spriteWithFile: #"Icon.png"];
treeObstacle.position = ccp( 450-i*20, 100+i*20 );
[self addChild:treeObstacle];
[treeObstacles addObject: treeObstacle];
}
NSLog (#"Number of elements in array = %i", [treeObstacles count]);
return self;
}
- (void) mymethod:(int)i {
NSLog (#"Number of elements in array = %i", [treeObstacles count]);
}
#end
The first NSLog() statement returns "Number of elements in array = 5". The problem is that (although treeObstacles is a file-scope variable) when calling the method "mymethod", I'll get an EXC_BAD_ACCESS exception.
Can anybody please help me?
Thanks a lot
Christian
you created treeObstacles by
treeObstacles = [NSMutableArray arrayWithObjects: nil];
which will return an autoreleased object, and you didn't retain it so it will be released soon
you have to retain it by calling retain on it
[treeObstacles retain];
of simple create it by
treeObstacles = [[NSMutableArray alloc] init];
and you need to remember to release it when done like
- (void)dealloc {
[treeObstacles release];
[super dealloc];
}
you need to read more about management in Objective-C
https://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/MemoryManagement.html
or use ARC so no need to worry retain/release anymore
http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html
another problem, you need to call [super init] in your init method
- (id)init {
self = [super init];
if (self) {
// your initialize code
}
}
otherwise your object won't initialize properly

Using self.objectname causes profiler to report a memory leak

Please help;
Header File
#import <Foundation/Foundation.h>
#interface MyClass : NSObject {
NSMutableString * myString;
}
#property (nonatomic, retain) NSMutableString * myString;
-(id) init;
-(void) dealloc;
#end
Implementation File
#import "MyClass.h"
#implementation MyClass
#synthesize myString;
-(id) init {
if ((self = [super init])) {
self.myString = [[NSMutableString alloc] init];
}
return self;
}
-(void) dealloc {
[super dealloc];
[self.myString release];
}
#end
Usage
MyClass * m = [[MyClass alloc] init];
[m release];
//-- Xcode 4 profiler reports a memory leak here.
However, when the code in implementation file of the class is changed to not use the [self.myString .....] notation, then no memory leak is reported.
So,
-(id) init {
if ((self = [super init])) {
myString = [[NSMutableString alloc] init];
}
return self;
}
}
and
-(void) dealloc {
[super dealloc];
[myString release];
}
works fine. No memory leaks reported.
Any ideas - is it profiler or is it me (be nice)?
Your memory leak is not caused by using your setter. Your memory leak is caused by you not managing your memory correctly!
If you declare the following property
#property (nonatomic, retain) id value;
That means that the compiler generates methods that look something like this (highly simplified):
- (id)value {
return value;
}
- (void)setValue:(id)aValue {
[value autorelease];
value = [aValue retain];
}
When you use dot-notation, self.value = obj is desugared into [self setValue:obj]. Thence, you are actually causing obj to be retained within the setter. If you initially create an owning reference to obj (by using an +alloc without a corresponding -release or -autorelease), you'll have over-retained obj, and it will never be deallocated. Hence, you need to do something like this:
id obj = [[[NSObject alloc] init] autorelease];
self.value = obj;
or
id obj = [[NSObject alloc] init];
self.value = [obj autorelease];
or
id obj = [[NSObject alloc] init];
self.value = obj;
[obj release];
Whatever you do, you need to make sure that when you assert ownership of an object (by retaining it), you also release it.
Setter methods in Objective-C equate to a reatain of the new object and release of the old object. In your case the compiler will generate a setter method for your myString property that looks something like...
- (void)setMyString:(NSMutableString*)aString {
[myString autorelease];
myString = [aString retain];
}
When you invoke self.myString = in your init method this translates to a call to the setter. The setter in turn retains the object you pass to it. Because you've directly alloc'd the NSString it begins life with a retain count of one, you then call the setter and the retain count becomes two.
There's two approaches to fixing the problem, the first would be to add a call to [myString autorelease] after you alloc it. Or secondly switch your init method to directly assign the ivar...
// in your init method...
myString = [[NSMutableString alloc] init];
It's a good idea to avoid setter usage in init methods, not because of retain counts but because the object as a whole is not yet fully initialized.
#property (nonatomic, RETAIN)
you are retaining my friend. You have to release the object twice then because the retain count is 2
here is what you should do in the INIT method:
NSString *str = [[NSString alloc] initWithString:#"Hello World!"];
self.myString = str;
[str release]; // dont leak
Also I do not recommend using self.someProperty in the class itself. Doing so requires 1 extra objc_msgSend() to be done to access your variable and will slow down your application.

Does this code leak?

I just ran my app through the Leaks in Instruments and I am being told that the following code causes leaks, but I don't see how.
I allocate some NSMutableArrays in my viewDidLoad with this code:
- (void)viewDidLoad {
[super viewDidLoad];
self.currentCars = [[NSMutableArray alloc] init];
self.expiredCars = [[NSMutableArray alloc] init];
}
Then I populate these arrays inside of my viewWillAppear method with the following:
[self.currentCars removeAllObjects];
[self.expiredCars removeAllObjects];
for (Car *car in [self.dealership cars]) {
if ([car isCurrent])
[self.currentCars addObject:car];
if ([car isExpired])
[self.expiredCars addObject:car];
}
And later in the code I release these arrays here:
- (void) viewWillDisappear:(BOOL)animated {
if (currentCars != nil) {
[currentCars release], currentCars = nil;
}
if (expiredCars != nil) {
[expiredCars release], expiredCars = nil;
}
[super viewWillDisappear:animated];
}
Any ideas? Thanks!
Your leak is here:
self.currentCars = [[NSMutableArray alloc] init];
self.expiredCars = [[NSMutableArray alloc] init];
Assuming that you declared property accessores like this:
#property(nonatomic, retain) NSMutableArray *currentCars;
#property(nonatomic, retain) NSMutableArray *expiredCars;
In my opinion, the best way to find leaks (other than using Instruments) is to keep track of the retain count manually.
If you were to do that with for example currentCars, you would find your leak easily. Here is what happens:
self.currentCars = [[NSMutableArray alloc] init];
// The 'init' makes the retain count 1.
// 'self.currentCars = ..' translates to the setCurrentCars: method.
// You probably did not implement that method yourself,
// but by synthesizing your property it is automatically implemented like this:
- (void)setCurrentCars:(NSMutableArray *)array {
[array retain]; // Makes the retain count 2
[currentCars release];
currentCars = array;
}
// In your viewWillDisappear: method
[currentCars release], currentCars = nil; // Makes the retain count 1 so the object is leaked.
The solution is simple. Use this:
NSMutableArray *tempMutableArray = [[NSMutableArray alloc] init];
self.currentCars = tempMutableArray;
[tempMutableArray release];
A little sidenote. You shouldn't release your objects in viewWillDisappear:. The recommended place to do that is dealloc. So your code would be:
- (void)dealloc {
[currentCars release], currentCars = nil;
[expiredCars release], expiredCars = nil;
[super dealloc];
}
The problem is (probably) that you are using the property accessors for the initial setting of the arrays in -viewDidLoad. Since well-implemented property accessors will retain the object, you are getting 1 retain from the +alloc and another retain from assigning it. To fix this, you should release your arrays after assigning them or use [NSMutableArray array] to get an autoreleased one to use for your initial assignments.
Unless you're doing something very odd in currentCars, expiredCars, dealership or cars, no, there's no leak there.
Instruments' pointer to the location of a leak isn't necessarily where the object is actually leaked, per se. If I were to guess, I'd say you're probably neglecting to release either currentCars or expiredCars in your dealloc method.

Why do they initialize pointers this way?

In almost all of the books I read and examples I go through I see pointers initialized this way. Say that I have a class variable NSString *myString that I want to initialize. I will almost always see that done this way:
-(id)init {
if (self = [super init]) {
NSString *tempString = [[NSString alloc] init];
self.myString = tempString;
[tempString release];
}
return self;
}
Why can't I just do the following?
-(id)init {
if (self = [super init]) {
self.myString = [[NSString alloc] init];
}
return self;
}
I don't see why the extra tempString is ever needed in the first place, but I could be missing something here with memory management. Is the way I want to do things acceptable or will it cause some kind of leak? I have read the Memory Management Guide on developer.apple.com and unless I am just missing something, I don't see the difference.
If self.myString is a retained property, the second example has to be
-(id)init {
if (self = [super init]) {
self.myString = [[[NSString alloc] init] autorelease];
}
return self;
}
or it will leak. I can only assume this is the case and the first example simply wants to avoid using autorelease.
The second example is correct.
Assuming that myString is an ivar, the first example is actually wrong because it leaves myString with a dangling pointer (a pointer to a deallocated object). If it were self.myString that would be a different story.

Objective-C: How to use memory management properly for asynchronous methods

I need to call a method that starts some asynchronous code
MyClass* myClass = [[MyClass alloc] init];
[myClass startAsynchronousCode];
Now I can't simply release it as this would cause an error since the code is still running:
[myClass release]; // causes an error
What is the best way to deal with the memory?
You could have -[MyClass startAsynchronousCode] invoke a callback:
typedef void(*DoneCallback)(void *);
-(void) startAsynchronousCode {
// Lots of stuff
if (finishedCallback) {
finishedCallback(self);
}
}
and then instantiate a MyClass like this:
MyClass *myClass = [[MyClass alloc] initWith: myCallback];
myCallback might look like this:
void myCallback(void *userInfo) {
MyClass *thing = (MyClass *)userInfo;
// Do stuff
[thing release];
}
How are you invoking the asynchronous code? If you use NSThread +detachNewThreadSelector:toTarget:withObject:, you'll find that the target object is retained by the thread until it terminates and then it is released. So you can release the object immediately after the asynchronous message.
e.g.
#implementation MyClass
-(void) startAsynchronousCode
{
[NSThread detachNewThreadSelector: #selector(threadMain:) toTarget: self withObject: nil];
}
-(void) threadMain: (id) anObject
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// do stuff
[pool drain];
}
#end
With the above, the following code is perfectly safe:
MyClass* myClass = [[MyClass alloc] init];
[myClass startAsynchronousCode];
[myClass release];
You must retain your myClass object internally in startAsynchronousCode method. And release it internally too after it finished.
This behavior used in NSURLConnection, UIAlertView and other async objects.
What I've always done is maintained an instance variable that points to the asynchronous object.
- (id)init {
myClass = [[MyClass alloc] init];
[myClass startAsynchronousCode];
}
- (void)myClassDidFinish:(MyClass *)myClass {
[myClass release];
}