Sergey Demchenko
Sergey Demchenko

Reputation: 2954

How to release block using ARC

Description:

I pass my block to async method, and it is called when operation is finished. I would like to decline calling the block before operation is finished. But if I assign nil to the block variable in my class it is called anyway. I debugged it and I see that if I assign nil to block variable1 the variable2 in not nil. The code below illustrates it:

void (^d1)(NSArray *data) = ^(NSArray *data) {};

void (__weak^d2)(NSArray *data) = d1;

d1 = nil;

Output:

(lldb) po d1
<__NSGlobalBlock__: 0x9c22b8>]
(lldb) po d2
<__NSGlobalBlock__: 0x9c22b8>
(lldb) po d1
<nil>
(lldb) po d2
<__NSGlobalBlock__: 0x9c22b8>

The question:

Why block d2 is not nil? Is it copied by value and isn't copied as pointer?

Upvotes: 0

Views: 1016

Answers (2)

newacct
newacct

Reputation: 122449

First of all, you should never depend on an object to be deallocated in a particular place. It is always possible for someone else (e.g. an autorelease pool) to hold a reference to it without you knowing. It is not incorrect for an object to be not deallocated in any place.

Okay, to your case: the __NSGlobalBlock__ gives a hint: this is a "global block". In the current implementation of blocks, there are 3 types of block objects: stack blocks (__NSStackBlock__), heap blocks (__NSMallocBlock__), and global blocks (__NSGlobalBlock__).

Blocks that are closures (i.e. that use some local variable from an outer scope) start out as "stack blocks" -- the object structure has automatic storage duration, just like a local variable, and it becomes invalid when it leaves the scope it's defined in. When a stack block is "copied", it gets moved to a dynamically-allocated object on the heap, like all other objects in Objective-C. This is a heap block. It is reference-counted like other objects in Cocoa, and copying it has the same effect as retain.

Blocks that are not closures (which is what the block in your example is) are implemented as "global blocks". Because it does not contain any captured variables, all instances of the block are the same, and thus there just exactly one instance of it in the entire program. It is statically allocated like string literals, and lives for the entire lifetime of the program. It is not reference-counted, and retain, release, and copy on it have no effect. It cannot be deallocated; that is why your weak reference never gets set to nil. A __weak reference only gets set to nil if the object it points to gets deallocated.

Upvotes: 5

John
John

Reputation: 2755

d1 is a reference to the block you created, ^(NSArray *data) {};. Once you assign d2 to d1's reference, the reference count on that block increments. When you set d1 to nil, there is still a strong pointer to the block, d2.

Upvotes: 1

Related Questions