Objective-C block "retain cycle" warning, don't understand why - objective-c

I've seen several other questions of the same form, but I either a) can't understand the provided answers, or b) don't see how those situations are similar to mine.
I'm writing a Category on UIView to recursively evaluate all the subviews of a UIView and return an Array of subviews passing a test. I've noted where my compiler warning occurs:
-(NSArray*)subviewsPassingTest:(BOOL(^)(UIView *view, BOOL *stop))test {
__block BOOL *stop = NO;
NSArray*(^__block evaluateAndRecurse)(UIView*);
evaluateAndRecurse = ^NSArray*(UIView *view) {
NSMutableArray *myPassedChildren = [[NSMutableArray alloc] init];
for (UIView *subview in [view subviews]) {
BOOL passes = test(subview, stop);
if (passes) [myPassedChildren addObject:subview];
if (stop) return myPassedChildren;
[myPassedChildren addObjectsFromArray:evaluateAndRecurse(subview)];
// ^^^^ Compiler warning here ^^^^^
// "Capturing 'evaluateAndRecurse' strongly in this block
// is likely to lead to a retrain cycle"
}
return myPassedChildren;
};
return evaluateAndRecurse(self);
}
Also, I get a bad_access failure when I don't include the __block modifier in my block's declaration (^__block evaluateAndRecurse). If someone could explain why that is, that would be very helpful too. Thanks!

The problem here is that your block evaluteAndRecurse() captures itself, which means that, if it's ever to be copied (I don't believe it will in your case, but in slightly less-trivial cases it may), then it will retain itself and therefore live forever, as there is nothing to break the retain cycle.
Edit: Ramy Al Zuhouri made a good point, using __unsafe_unretained on the only reference to the block is dangerous. As long as the block remains on the stack, this will work, but if the block needs to be copied (e.g. it needs to escape to a parent scope), then the __unsafe_unretained will cause it to be deallocated. The following paragraph has been updated with the recommended approach:
What you probably want to do here is use a separate variable marked with __unsafe_unretained that also contains the block, and capture that separate variable. This will prevent it from retaining itself. You could use __weak, but since you know that the block must be alive if it's being called, there's no need to bother with the (very slight) overhead of a weak reference. This will make your code look like
NSArray*(^__block __unsafe_unretained capturedEvaluteAndRecurse)(UIView*);
NSArray*(^evaluateAndRecurse)(UIView*) = ^NSArray*(UIView *view) {
...
[myPassedChildren addObjectsFromArray:capturedEvaluateAndRecurse(subview)];
};
capturedEvaluateAndRecurse = evaluteAndRecurse;
Alternatively, you could capture a pointer to the block, which will have the same effect but allow you to grab the pointer before the block instantiation instead of after. This is a personal preference. It also allows you to omit the __block:
NSArray*(^evaluateAndRecurse)(UIView*);
NSArray*(^*evaluteAndRecursePtr)(UIView*) = &evaluateAndRecurse;
evaluateAndRecurse = ^NSArray*(UIView*) {
...
[myPassedChildren addObjectsFromArray:(*evaluateAndRecursePtr)(subview)];
};
As for needing the __block, that's a separate issue. If you don't have __block, then the block instance will actually capture the previous value of the variable. Remember, when a block is created, any captured variables that aren't marked with __block are actually stored as a const copy of their state at the point where the block is instantiated. And since the block is created before it's assigned to the variable, that means it's capturing the state of the capturedEvaluteAndRecurse variable before the assignment, which is going to be nil (under ARC; otherwise, it would be garbage memory).
In essence, you can think of a given block instance as actually being an instance of a hidden class that has an ivar for each captured variable. So with your code, the compiler would basically treat it as something like:
// Note: this isn't an accurate portrayal of what actually happens
PrivateBlockSubclass *block = ^NSArray*(UIView *view){ ... };
block->stop = stop;
block->evaluteAndRecurse = evaluateAndRecurse;
evaluteAndRecurse = block;
Hopefully this makes it clear why it captures the previous value of evaluateAndRecurse instead of the current value.

I've done something similar, but in a different way to cut down on time allocating new arrays, and haven't had any problems. You could try adapting your method to look something like this:
- (void)addSubviewsOfKindOfClass:(id)classObject toArray:(NSMutableArray *)array {
if ([self isKindOfClass:classObject]) {
[array addObject:self];
}
NSArray *subviews = [self subviews];
for (NSView *view in subviews) {
[view addSubviewsOfKindOfClass:classObject toArray:array];
}
}

Related

How to replicate NSArray memory semantics in a subclass

Question
In my ARC project I have a class that manages objects, called LazyMutableArray. Some of the objects are actually nil, but users of my collection will never know about this; therefore I made it a subclass of NSMutableArray, and it tries to do "the same thing". In particular, objects are retained when added.
Now let's take a look at a memory behavior of other methods. It turns out that the NSArray destruction methods are documented by Apple to be an exception to this rule, in that they release, not autoreleased object.
There is some debate as to whether the combination of addObject: + objectAtIndex: + array destruction is documented by Apple to be never autoreleasing or simply happens to be in the examples I tested and in the example Apple includes.
How can I create in my subclass a method with exact same memory semantics?
Last update
After some thought, I've decided implementation based on NSMutableArray is more appropriate in this case compared to NSPointerArray. The new class, I should note, has the same retain/autorelease pair as the previous implementation.
Thanks to Rob Napier I see that no modification of my objectAtIndex: method would change this behavior, which answers my original question about this method.
On a practical level, several people said that any method can tackle an extra retain/autorelease pair for no reason; it's not reasonable to expect otherwise and not reasonable to try to find out which methods do this and which do not. It's been therefore a great learning opportunity for me on several levels.
Code (based on NSMutableArray) is available at GitHub: implementation, header, test (that's -testLazyMutableMemorySemantics).
Thank you all for participating.
Why I try to subclass NSMutableArray:
Subclassing foundation objects, I agree, is not always an appropriate solution. In tho case I have objects (in fact, OData resources), most of which have subobjects. The most natural class for an array of subobjects is obviously NSArray. Using a different class doesn't seem to make sense to me.
But for an OData collection this "array of sub objects", while, being an NSArray, must have a different implementation. Specifically, for a collection of 1000 elements, servers are encouraged to return collection in batches of (say)20, instead of all at once. If there is another pattern appropriate in this case, I'm all ears.
Some more detail in how I found this
I unit test the hell out of this collection, and values can be put into array, read from the array, and so forth. So far, so good. However, I realized that returning the object increases its retain count.
How do I see it? Suppose I insert two objects into lazy array lazy, one held weakly, one held strongly (*see the code *). Then retain count of weakSingleton is, as expected, 1. But now I read element:
XCTAssertEqual(weakSingleton, lazy[0], #"Correct element storage"); // line B
And in the debugger I see the retain count go up to 2. Of course, -retainCount may give me wrong information, so let's try to destroy the reference in array by
lazy[0] = nil; // yep, does the right thing
XCTAssertNil(weakSingleton, #"Dropped by lazy array"); // line C <-- FAIL
indeed, we see that weakSingleton is not released.
By now you probably guess that it's not just a retain, it's an autoreleased retain — putting an #autorelease around line B releases the weakSingleton. The exact source of this pair is not obvious, but seems to come from NSPointerArray -addPointer: (and unfortunately not from ARC's [[object retain] autorelease]). However, I don't want to return an autoreleased object and make method semantics different from its superclass!
After all, the method I'm overriding, NSMutableArray -objectAtIndex:`, doesn't do that; the object it returns will dealloc immediately if an array is released, as noted in the Apple's example. That's what I want: modify the method around line A so that the object it returns does not have an extra retain/autorelease pair. I'm not sure the compiler should even let me do it :)
Note 1 I could turn off ARC for a single file, but this would be my first non-ARC Objective-C code. And in any case the behavior may not some from ARC.
Note 2 What the fuss? Well, in this case I could change my unit tests, but still, the fact is that by adding or deleting line B, I'm changing the result of unit test at line C.
In other words, the described behavior of my method [LazyMutableArray -objectAtIndex] is essentially that by reading an object at index 0, I'm actually changing the retain count of this object, which means I could encounter unexpected bugs.
Note 3 Of course, if nothing is to be done about this, I'll document this behavior and move on; perhaps, this indeed should be considered an implementation detail, not to be included into tests.
Relevant methods from implementation
#implementation LazyMutableArray {
NSPointerArray *_objects;
// Created lazily, only on -setCount:, insert/add object.
}
- (id)objectAtIndex:(NSUInteger)index {
#synchronized(self) {
if (index >= self.count) {
return nil;
}
__weak id object = [_objects pointerAtIndex:index];
if (object) {
return object;
}
}
// otherwise do something else to compute a return value
// but this branch is never called in this test
[self.delegate array:self missingObjectAtIndex:index];
#synchronized(self) {
if (index >= self.count) {
return nil;
}
__weak id object = [_objects pointerAtIndex:index];
if (object) {
return object;
}
}
#throw([NSException exceptionWithName:NSObjectNotAvailableException
reason:#"Delegate was not able to provide a non-nil element to a lazy array"
userInfo:nil]);
}
- (void)createObjects {
if (!_objects) {
_objects = [NSPointerArray strongObjectsPointerArray];
}
}
- (void)addObject:(id)anObject {
[self createObjects];
[_objects addPointer:(__bridge void*)anObject];
}
The complete test code:
// Insert two objects into lazy array, one held weakly, one held strongly.
NSMutableArray * lazy = [LazyMutableArray new];
id singleton = [NSMutableArray new];
[lazy addObject:singleton];
__weak id weakSingleton = singleton;
singleton = [NSMutableDictionary new];
[lazy addObject:singleton];
XCTAssertNotNil(weakSingleton, #"Held by lazy array");
XCTAssertTrue(lazy.count == 2, #"Cleaning and adding objects");
// #autoreleasepool {
XCTAssertEqual(weakSingleton, lazy[0], #"Correct element storage");
XCTAssertEqual(singleton, lazy[1], #"Correct element storage");
// }
lazy = nil;
XCTAssertNotNil(singleton, #"Not dropped by lazy array");
XCTAssertNil(weakSingleton, #"Dropped by lazy array");
The last line fails, but it succeeds if I change first line to lazy = [NSMutableArray new] or if I uncomment #autoreleasepool.
First, I would not make this subclass. This is exactly what NSPointerArray is for. Wrapping that into an NSArray obscures important details that this approach can break. For example, what is the correct behavior for [NSArray arrayWithArray:lazyMutableArray] if lazyMutableArray includes NULLs? Algorithms that assume that NSArray can never include NULL need to be wary of the fact that this one can. It's true that you can get similar issues treating a non-retaining CFArray as an NSArray; I speak from experience that this is exactly why this kind of subclass can be very dangerous (and why I stopped doing that years ago). Don't create a subclass that cannot be used in every case that its superclass can be used (LSP).
If you have a collection with new semantics, I would subclass it from NSObject, and have it conform to <NSFastEnumeration>. See how NSPointerArray is not a subclass of NSArray. This was not an accident. Faced with the same problem, note the direction Apple chose.
By now you probably guess that it's not just a retain, it's an autoreleased retain — putting an #autorelease around line B releases the weakSingleton. This seems to be because line A under ARC translates to [[object retain] autorelease]. However, I don't want to return an autoreleased object and make caller remember this!
The caller should never assume anything else. The caller is never free to assume that a method does not add balanced autoreleases. If a caller wants the autorelease pool to drain, that is their responsibility.
All that said, there is some benefit to avoiding an extra autorelease if it's not required, and it's an interesting learning opportunity.
I would start by reducing this code to the simplest form, without your subclass at all. Just explore how NSPointerArray works:
__weak id weakobject;
#autoreleasepool
{
NSPointerArray *parray = [NSPointerArray strongObjectsPointerArray];
{
id object = [NSObject new];
[parray addPointer:(__bridge void*)object];
weakobject = object;
}
parray = nil;
}
NSAssert(!weakobject, #"weakobject still exists");
My structure here (such as the extra nesting block) is designed to try to avoid accidentally creating strong references I don't mean to make.
In my experiments, this fails without the autoreleasepool and succeeds with it. That indicates that the extra retain/autorelease is being added around or by the call to addPointer:, not by ARC modifying your interface.
If you're not using this implementation for addObject:, I'd be interested in digging deeper. It is an interesting question, even if I don't believe you should be subclassing this way.
I'm going to elaborate on why I said this "looks a lot like a homework assignment." This will likely earn me many down votes, but it will also server as a good learning case for others who later find this question.
Subclassing NSMutableArray not a goal of a program. It is a means to achieve something else. If I were to venture a guess, I expect you were trying to create an array that lazily creates the object when they are accessed. There are better ways to do this without dealing with memory management yourself.
Here's an example of how I would implement a lazy loading array.
#interface LazyMutableArray : NSMutableArray
- (id)initWithCreator:(id(^)(int))creator;
#end
#interface LazyMutableArray ( )
#property (nonatomic, copy) id (^creator)(int);
#property (nonatomic, assign) NSUInteger highestSet;
#end
#implementation LazyMutableArray
- (id)initWithCreator:(id(^)(int))creator
{
self = [super init];
if (self) {
self.highestSet = NSNotFound;
self.creator = creator;
}
return self;
}
- (id)objectAtIndex:(NSUInteger)index
{
id obj = nil;
if ((index < self.highestSet) && (self.highestSet != NSNotFound)) {
obj = [super objectAtIndex:index];
if ([obj isKindOfClass:[NSNull class]]) {
obj = self.creator(index);
[super replaceObjectAtIndex:index withObject:obj];
}
} else {
if (self.highestSet == NSNotFound) {
self.highestSet = 0;
}
while (self.highestSet < index) {
[super add:[NSNull null]];
self.highestSet += 1;
}
obj = self.creator(index);
[super add:obj];
self.highestSet += 1;
}
return obj;
}
Fair Warning: I'm not compiling or syntax checking any of this code. It probably has a few bugs in it, but it should generally work. Additionally, this implementation is missing an implementation of add:, count, removeObjectAtIndex:, insertObject:atIndex:, and possibly replaceObjectAtIndex:withObject:. What I show here is just to get you started.

Autoreleasing blocks in NSMutableArray retained by their creator

I'm trying to write a category based on node.js EventEmitter, which can take a number of blocks, store them weakly in an array, and execute them later if the instance creating the block isn't deallocated (in which case they would be removed from the array). This is in order not to keep filling the array with old, unused blocks.
The problem is that the blocks seem to be copied by the class, and thusly never released, even though the instance creating the block is deallocated.
So the implementation looks something like this;
Usage
[object on:#"change" do:^(id slf, NSArray *args) {
NSLog(#"something changed");
}];
Implementation (WeakReference class found here, courtesy of noa)
- (void)on:(NSString *)eventType do:(Callback)callback
{
NSMutableArray *callbacks = self.emitterEvents[eventType];
__weak Callback wcb = callback;
// Wrap the callback in NSValue subclass in order to reference it weakly
WeakReference *cbr = [WeakReference weakReferenceWithObject:wcb];
callbacks[callbacks.count] = cbr;
}
- (void)emit:(NSString *)eventType withArgs:(NSArray *)objArgs
{
NSInteger idx = 0;
NSMutableIndexSet *indices = [NSMutableIndexSet indexSet];
callbacks = (NSMutableArray *)callbacks;
for (WeakReference *cbv in callbacks) {
__weak id cb = [cbv nonretainedObjectValue];
if (cb) {
Callback callback = (Callback)cb;
__weak id slf = self;
callback(slf, objArgs);
} else {
[indices addIndex:idx];
}
idx++;
}
[callbacks removeObjectsAtIndexes:indices];
}
I read something about blocks being copied when used past their scope, but frankly, reading about all these block semantics is kind of making my head spin right now.
Is this way of approaching the problem even possible?
In Objective-C, blocks are objects, but unlike other objects, they are created on the stack. If you want to use the block outside of the scope it was created you must copy it.
[object on:#"change" do:^(id slf, NSArray *args) {
NSLog(#"something changed");
}];
Here, you are passing a pointer to a block on the stack. Once your current stack frame is out of scope, your block is gone. You could either pass a copy to the block, making the caller the owner of the block, or you could copy the block in the receiver.
If you want the caller to own the block, then you have to keep a strong reference to the block in the caller (e.g. as a property). Once the caller gets deallocated, you lose your strong reference and your weak reference is set to nil.
copy a block which is already copied is same as retain it, so if the caller of the method copy the block first then pass it to the method, it should works as you expected. but this means you cannot simply use the method as you described in your usage section.
you have use it like this
typeofblock block = ^(id slf, NSArray *args) {
NSLog(#"something changed");
};
self.block = [block copy]
[object on:#"change" do:self.block];
to actual solve the problem, you have to figure out owns the block. the caller of on:do:, or the object been called?
sounds to me you want to remove the block when the caller is deallocated, which means the owner of the block is the caller. but your on:do: method does not aware the owner of the block, and cannot remove the block when the caller is deallocated.
one way is to pass the owner of the block into the method and remove the block when it deallocated. this can be done use associate object.
- (void)on:(NSString *)eventType do:(Callback)callback sender:(id)sender
{
// add the block to dict
// somehow listen to dealloc of the sender and remove the block when it is called
}
another way is to add new method to remove the block, and call the method in dealloc or other place to remove the block manually.
your approach is similar to KVO, which require the observer to unregister the observation, and I think is a good practice that you should follow.
Thanks for the answers, I realize I was a little bit off on how blocks are managed. I solved it with a different approach, inspired by Mike Ash's implementation of KVO with blocks & automatic dereferencing, and with xlc's advice on doing it in dealloc.
The approach is along the lines of this (in case you don't want to read the whole gist):
Caller object assigns listener to another object with on:event do:block with:caller
Emitter object creates a Listener instance, with a copy of the block, reference to emitter & the event-type
Emitter adds the copied block to an array inside a table (grouped by event-types), creates an associated object on the caller and attaches the listener
Emitter method-swizzles the caller, and adds a block to its dealloc, which removes itself from the emitter
The caller can then choose to handle the listener-instance, which is returned from the emit-method, if it wants to manually stop the listener before becoming deallocated itself
Source here
I don't know if it is safe for use, I've only tested it on a single thread in a dummy-application so far.

capturing self strongly in this block is likely to lead to a retain cycle

How can I avoid this warning in xcode. Here is the code snippet:
[player(AVPlayer object) addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil usingBlock:^(CMTime time) {
current+=1;
if(current==60)
{
min+=(current/60);
current = 0;
}
[timerDisp(UILabel) setText:[NSString stringWithFormat:#"%02d:%02d",min,current]];///warning occurs in this line
}];
The capture of self here is coming in with your implicit property access of self.timerDisp - you can't refer to self or properties on self from within a block that will be strongly retained by self.
You can get around this by creating a weak reference to self before accessing timerDisp inside your block:
__weak typeof(self) weakSelf = self;
[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil
usingBlock:^(CMTime time) {
current+=1;
if(current==60)
{
min+=(current/60);
current = 0;
}
[weakSelf.timerDisp setText:[NSString stringWithFormat:#"%02d:%02d",min,current]];
}];
__weak MyClass *self_ = self; // that's enough
self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
if (!error) {
[self_ showAlertWithError:error];
} else {
self_.items = [NSArray arrayWithArray:receivedItems];
[self_.tableView reloadData];
}
};
And one very important thing to remember:
do not use instance variables directly in block, use it as a properties of weak object, sample:
self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
if (!error) {
[self_ showAlertWithError:error];
} else {
self_.items = [NSArray arrayWithArray:receivedItems];
[_tableView reloadData]; // BAD! IT ALSO WILL BRING YOU TO RETAIN LOOP
}
};
and don't forget to do:
- (void)dealloc {
self.loadingCompletionHandler = NULL;
}
another issue can appear if you will pass weak copy of not retained by anybody object:
MyViewController *vcToGo = [[MyViewCOntroller alloc] init];
__weak MyViewController *vcToGo_ = vcToGo;
self.loadingCompletion = ^{
[vcToGo_ doSomePrecessing];
};
if vcToGo will be deallocated and then this block fired I believe you will get crash with unrecognized selector to a trash which is contains vcToGo_ variable now. Try to control it.
Better version
__strong typeof(self) strongSelf = weakSelf;
Create a strong reference to that weak version as the first line in your block. If self still exists when the block starts to execute and hasn’t fallen back to nil, this line ensures it persists throughout the block’s execution lifetime.
So the whole thing would be like this:
// Establish the weak self reference
__weak typeof(self) weakSelf = self;
[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil
usingBlock:^(CMTime time) {
// Establish the strong self reference
__strong typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.timerDisp setText:[NSString stringWithFormat:#"%02d:%02d",min,current]];
} else {
// self doesn't exist
}
}];
I have read this article many times. This is an excellent article by Erica Sadun on
How To Avoid Issues When Using Blocks And NSNotificationCenter
Swift update:
For example, in swift a simple method with success block would be:
func doSomeThingWithSuccessBlock(success: () -> ()) {
success()
}
When we call this method and need to use self in the success block. We'll be using the [weak self] and guard let features.
doSomeThingWithSuccessBlock { [weak self] () -> () in
guard let strongSelf = self else { return }
strongSelf.gridCollectionView.reloadData()
}
This so-called strong-weak dance is used by popular open source project Alamofire.
For more info check out swift-style-guide
In another answer, Tim said:
you can't refer to self or properties on self from within a block that will be strongly retained by self.
This isn’t quite true. It’s OK for you to do this so long as you break the cycle at some point. For example, let’s say you have a timer that fires that has a block that retains self and you also keep a strong reference to the timer in self. This is perfectly fine if you always know that you will destroy the timer at some point and break the cycle.
In my case just now, I had this warning for code that did:
[x setY:^{ [x doSomething]; }];
Now I happen to know that clang will only produce this warning if it detects the method starts with “set” (and one other special case that I won’t mention here). For me, I know there is no danger of there being a retain loop, so I changed the method name to “useY:” Of course, that might not be appropriate in all cases and usually you will want to use a weak reference, but I thought it worth noting my solution in case it helps others.
Many times, this is not actually a retain cycle.
If you know that it's not, you need not bring fruitless weakSelves into the world.
Apple even forces these warnings upon us with the API to their UIPageViewController, which includes a set method (which triggers these warnings–as mentioned elsewhere–thinking you are setting a value to an ivar that is a block) and a completion handler block (in which you'll undoubtedly refer to yourself).
Here's some compiler directives to remove the warning from that one line of code:
#pragma GCC diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
[self.pageViewController setViewControllers:#[newViewController] direction:navigationDirection animated:YES completion:^(BOOL finished) {
// this warning is caused because "setViewControllers" starts with "set…", it's not a problem
[self doTheThingsIGottaDo:finished touchThePuppetHead:YES];
}];
#pragma GCC diagnostic pop
Adding two cents on improving precision and style. In most cases you will only use one or a couple of members of self in this block, most likely just to update a slider. Casting self is overkill. Instead, it's better to be explicit and cast only the objects that you truly need inside the block. For example, if it's an instance of UISlider*, say, _timeSlider, just do the following before the block declaration:
UISlider* __weak slider = _timeSlider;
Then just use slider inside the block. Technically this is more precise as it narrows down the potential retain cycle to only the object that you need, not all the objects inside self.
Full example:
UISlider* __weak slider = _timeSlider;
[_embeddedPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
queue:nil
usingBlock:^(CMTime time){
slider.value = time.value/time.timescale;
}
];
Additionally, most likely the object being cast to a weak pointer is already a weak pointer inside self as well minimizing or eliminating completely the likelihood of a retain cycle. In the example above, _timeSlider is actually a property stored as a weak reference, e.g:
#property (nonatomic, weak) IBOutlet UISlider* timeSlider;
In terms of coding style, as with C and C++, variable declarations are better read from right to left. Declaring SomeType* __weak variable in this order reads more naturally from right to left as: variable is a weak pointer to SomeType.
I ran into this warning recently and wanted to understand it a bit better. After a bit of trial and error, I discovered that it originates from having a method start with either "add" or "save". Objective C treats method names starting with "new", "alloc", etc as returning a retained object but doesn't mention (that I can find) anything about "add" or "save". However, if I use a method name in this way:
[self addItemWithCompletionBlock:^(NSError *error) {
[self done]; }];
I will see the warning at the [self done] line. However, this will not:
[self itemWithCompletionBlock:^(NSError *error) {
[self done]; }];
I will go ahead and use the "__weak __typeof(self) weakSelf = self" way to reference my object but really don't like having to do so since it will confuse a future me and/or other dev. Of course, I could also not use "add" (or "save") but that's worse since it takes away the meaning of the method.

In objective c can a class essentially delete itself?

In objective c, suppose I have an object Obj stored in a NSMutableArray, and the array's pointer to it is the only strong pointer to Obj in the entire program. Now suppose I call a method on Obj and I run this method in another thread. In this method, if Obj sets the pointer for itself equal to nil will it essentially delete itself? (Because there will be no more strong pointers left) I suspect the answer is no, but why? If this does work, is it bad coding practice (I assume its not good coding, but is it actually bad?)
It is highly unlikely that an object would be in a position to cause its own release/deallocation if your code is designed properly. So yes, the situation you describe is indicative of bad coding practice, and can in fact cause the program to crash. Here is an example:
#interface Widget : NSObject
#property (retain) NSMutableArray *array;
#end
#implementation Widget
#synthesize array;
- (id)init
{
self = [super init];
if(self) {
array = [[NSMutableArray alloc] init];
[array addObject:self];
}
return self;
}
- (void)dealloc
{
NSLog(#"Deallocating!");
[array release];
[super dealloc];
}
- (void)removeSelf
{
NSLog(#"%d", [array count]);
[array removeObject:self];
NSLog(#"%d", [array count]);
}
#end
and then this code is in another class:
Widget *myWidget = [[Widget alloc] init];
[myWidget release]; // WHOOPS!
[myWidget removeSelf];
The second call to NSLog in removeSelf will cause an EXC_BAD_ACCESS due to the fact that array has been deallocated at that point and can't have methods called on it.
There are at least a couple mistakes here. The one that ultimately causes the crash is the fact that whatever class is creating and using the myWidget object releases it before it is finished using it (to call removeSelf). Without this mistake, the code would run fine. However, MyWidget shouldn't have an instance variable that creates a strong reference to itself in the first place, as this creates a retain cycle. If someone tried to release myWidget without first calling removeSelf, nothing would be deallocated and you'd probably have a memory leak.
If your back-pointer is weak (which it should be since a class should never try to own it's owner, you will end up with a retain-cycle) and you remove the strong pointer from the array the object will be removed from the heap. No strong pointers = removed from memory.
You can always test this.
If you need a class to bring to a situation where its deleted, the best practice is to first retain/autorelease it and then make the situation happen. In this case the class won't be deleted in a middle of its method, but only afterwards.
I think we can say it might be bad coding practice, depending on how you do it. There are ways you could arrange to do it safely, or probably safely.
So let's assume we have a global:
NSMutableArray *GlobalStore;
One approach is to remove yourself as your final action:
- (void) someMethod
{
...
[GlobalStore removeObject:self];
}
As this is the final action there should be no future uses of self and all should be well, probably...
Other options include scheduling the removal with a time delay of 0 - which means it will fire next time around the run loop (only works of course if you have a run loop, which in a thread you may not). This should always be safe.
You can also have an object keep a reference to itself, which produces a cycle and so will keep it alive. When its ready to die it can nil out its own reference, if there are no other references and that is a final action (or a scheduled action by another object) then the object is dead.

Why shouldn't I use the getter to release a property in objective-c?

I was told by a fellow StackOverflow user that I should not use the getter method when releasing a property:
#property(nonatmic, retain) Type* variable;
#synthesize variable;
// wrong
[self.variable release];
// right
[variable release];
He did not explain in detail why. They appear the same to me. My iOS book said the getter on a property will look like this:
- (id)variable {
return variable;
}
So doesn't this mean [self variable], self.variable, and variable are all the same?
For a retained property with no custom accessor, you can release the object by:
self.variable = nil;
This has the effect of setting the ivar (which may not be called 'variable' if you have only declared properties) to nil and releasing the previous value.
As others have pointed out, either directly releasing the ivar (if available) or using the method above is OK - what you must not do is call release on the variable returned from a getter.
You can optionally write custom getter behavior, which may result in completely different behavior. So, you cannot always assume that [variable release] has the same results as [self.variable release].
As well, you can write custom properties without an exclusive ivar backing them... it can get messy fast if you start releasing objects from references returned by getters!
There may be additional reasons that I'm unaware of...
A typical getter will look more like this:
- (id)variable {
return [[variable retain] autorelease];
}
So if you use [self.variable release] you have an additional retain and autorelease that you don't really need when you just want to release the object and that cause the object to be released later than necessary (when the autorelease pool is drained).
Typically, you would either use self.variable = nil which has the benefit that it also sets the variable to nil (avoiding crashes due to dangling pointers), or [variable release] which is the fastest and may be more appropriate in a dealloc method if your setter has custom logic.
not all getters take this form:
- (id)variable { return variable; }
...that is merely the most primitive form. properties alone should suggest more combinations, which alter the implementation. the primitive accessor above does not account for idioms used in conjunction with memory management, atomicity, or copy semantics. the implementation is also fragile in subclass overrides.
some really brief examples follow; things obviously become more complex in real programs where implementations become considerably more complex.
1) the getter may not return the instance variable. one of several possibilities:
- (NSObject *)a { return [[a copy] autorelease]; }
2) the setter may not retain the instance variable. one of several possibilities:
- (void)setA:(NSObject *)arg
{
...
a = [arg copy];
...
}
3) you end up with memory management implementation throughout your program, which makes it difficult to maintain. the semantics of the class (and how it handles instance variables' ref counting) should be kept to the class, and follow conventions for expected results:
- (void)stuff:(NSString *)arg
{
const bool TheRightWay = false;
if (TheRightWay) {
NSMutableString * string = [arg mutableCopy];
[string appendString:#"2"];
self.a = string;
[string release];
// - or -
NSMutableString * string = [[arg mutableCopy] autorelase];
[string appendString:#"2"];
self.a = string;
}
else {
NSMutableString * string = [arg mutableCopy];
[string appendString:#"2"];
self.a = string;
[self.a release];
}
}
failing to follow these simple rules makes your code hard to maintain and debug and painful to extend.
so the short of it is that you want to make your program easy to maintain. calling release directly on a property requires you to know a lot of context of the inner workings of the class; that's obviously bad and misses strong ideals of good OOD.
it also expects the authors/subclassers/clients to know exactly how the class deviates from convention, which is silly and time consuming when issues arise and you have to relearn all the inner details when issues arise (they will at some point).
those are some trivial examples of how calling release on the result of a property introduces problems. many real world problems are much subtler and difficult to locate.