Memory management

Like primitive variables, blocks are created and stored on the stack. That is, the block will be destroyed along with the stack frame when the function or method that created the block returns. Sometimes, however, your block needs to live longer than that. For example, it could become an instance variable of an object. In this case, you must copy your block from the stack to the heap.

To copy a block from the stack to the heap, you send it the copy message:

A​r​r​a​y​E​n​u​m​e​r​a​t​i​o​n​B​l​o​c​k​ ​i​V​a​r​D​e​v​o​w​e​l​i​z​e​r​ ​=​ ​[​d​e​v​o​w​e​l​i​z​e​r​ ​c​o​p​y​]​;​

Now a copy of your block exists on the heap. It is now a heap-based block instead of a stack-based block, and the new block variable is a pointer to the block.

Methods that take blocks as arguments, such as NSArray’s enumerateObjectsUsingBlock: or NSNotificationCenter’s addObserverForName:object:queue:usingBlock:, are expected to copy blocks passed to them to keep them around. In doing so, they create pointers – and strong references – to those blocks.

So we’ve seen blocks be declared, assigned values, and passed like variables. We’ve also seen that they look like functions. Now we’re sending a block a message as if it were an object.

A heap-based block behaving like an object comes with some memory management issues:

What about variables that are used with the block?

A block typically uses other variables (both primitive and pointers to objects) within its code that were created outside of it. To make sure these external variables will be available for as long as the block needs them, the variables are captured by the block when the copy is made.

For primitive variables, this means the values are copied and stored as local variables within the block. For pointers, the block itself will keep a strong reference to any objects it references. That means that any objects referred to by the block are guaranteed to live at least as long as the block itself. (If you’ve been wondering about the difference between blocks and function pointers, it’s right here. Let’s see a function pointer do that!)

As an example, take a look at our VowelMovement program. The devowelizer block mentions two objects that were created outside of the block: newStrings (an array for storing the modified versions of strings) and string (the current string to be copied for modification). devowelizer will keep strong references to both of these objects, keeping them alive as long as the block itself exists.

Can these strong references lead to retain cycles/circular references?

You bet. The fix is the same: one of the references needs to become a weak reference. To do this, declare a __weak pointer outside the block and then reference this pointer within the block instead.

Can I change the variables that the block has copied?

By default, the variables captured by a block are constant within the block, and you cannot change their values. Object pointer variables, for example, are constant within the scope of the block. (Although you can still send the object messages that can change its contents, you cannot modify the pointer itself.)

Sometimes, however, you want to be able to modify an external variable within a block. To do this, you must declare the external variable using the __block keyword. For instance, in the following code, you increment the external variable counter.

_​_​b​l​o​c​k​ ​i​n​t​ ​c​o​u​n​t​e​r​ ​=​ ​0​;​
v​o​i​d​ ​(​^​c​o​u​n​t​e​r​B​l​o​c​k​)​(​)​ ​=​ ​^​{​ ​c​o​u​n​t​e​r​+​+​;​ ​}​;​
.​.​.​
c​o​u​n​t​e​r​B​l​o​c​k​(​)​;​ ​/​/​ ​I​n​c​r​e​m​e​n​t​s​ ​c​o​u​n​t​e​r​ ​t​o​ ​1​
c​o​u​n​t​e​r​B​l​o​c​k​(​)​;​ ​/​/​ ​I​n​c​r​e​m​e​n​t​s​ ​c​o​u​n​t​e​r​ ​t​o​ ​2​

Without the __block keyword, you would get a compilation error in the block definition, indicating that the value of counter cannot be changed.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset