Blocks Capturing Objects

Capturing Objects

We saw examples using integer variables. Next, let’s see what will happen when an object is used from a Block. In the next source code, an object of the NSMutableArray class is created and the assigned variable has ownership of it. Because the scope of the variable, which qualified with __strong, is left immediately, the object is released and discarded.

{
    id array = [[NSMutableArray alloc] init];
}

Listing 5–7 uses the variable “array” in a Block literal.

Listing 5–7. Capturing an object

blk_t blk;
{
    id array = [[NSMutableArray alloc] init];
    blk = [^(id obj) {
        [array addObject:obj];
        NSLog(@"array count = %ld", [array count]);
    } copy];
}
blk([[NSObject alloc] init]);
blk([[NSObject alloc] init]);

blk([[NSObject alloc] init]);

Because the strong reference of variable “array” disappears, the NSMutableArray class object, which is assigned to the variable “array”, must be released and discarded. But the result shows that it works without any problem.

array count = 1
array count = 2
array count = 3

This means that in the last part of the source code, where the Block is executed, the object of NSMutableArray class, which is assigned to the variable “array”, exists even after the variable scope is finished. The source code is converted as shown in Listing 5–8.

Listing 5–8. Converted source code of Listing 5–7

/* a struct for the Block and some functions */

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    id __strong array;
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,
    id __strong _array, int flags=0) : array(_array) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself, id obj)
{
    id __strong array = __cself->array;
    [array addObject:obj];
    NSLog(@"array count = %ld", [array count]);
}

static void __main_block_copy_0(struct __main_block_impl_0 *dst,         struct
__main_block_impl_0 *src)
{
    _Block_object_assign(&dst->array, src->array, BLOCK_FIELD_IS_OBJECT);
}

static void __main_block_dispose_0(struct __main_block_impl_0 *src)
{
    _Block_object_dispose(src->array, BLOCK_FIELD_IS_OBJECT);
}

static struct __main_block_desc_0 {
    unsigned long reserved;
    unsigned long Block_size;
    void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = {
    0,
    sizeof(struct __main_block_impl_0),
    __main_block_copy_0,
    __main_block_dispose_0
};


/* Block literal and executing the Block */

blk_t blk;
{
    id __strong array = [[NSMutableArray alloc] init];

    blk = &__main_block_impl_0(
        __main_block_func_0, &__main_block_desc_0_DATA, array, 0x22000000);
    blk = [blk copy];
}

(*blk->impl.FuncPtr)(blk, [[NSObject alloc] init]);
(*blk->impl.FuncPtr)(blk, [[NSObject alloc] init]);
(*blk->impl.FuncPtr)(blk, [[NSObject alloc] init]);

Please pay attention to the captured automatic variable “array” which stores the object of the NSMutableArray class. You can see that the struct for the Block has a member variable “array” with __strong ownership qualifier.

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    id __strong array;
};

In Objective-C, the C struct cannot have member variables that are qualified with __strong as we learned in Chapter 2. The reason is that the compiler can’t detect when the C struct is initialized or discarded in order to do proper memory management.

However, the Objective-C runtime library can detect when a Block is copied from the stack to the heap, and when a Block on the heap is destroyed. So, if the struct for Block has variables that are qualified with __strong or __weak, the compiler can initialize and dispose of them properly. For that, the member variables in struct __main_block_desc_0 “copy” and “dispose” are added and functions __main_block_copy_0 and __main_block_dispose_0 are assigned to them.

In the source code, the struct for the Block has an object type variable “array”, which is qualified with __strong. Because the compiler has to manage the object in the variable “array” properly, the __main_block_copy_0 function calls the _Block_object_assign function to assign the target object to the member variable “array” and to take ownership of it.

static void __main_block_copy_0(struct __main_block_impl_0 *dst,
        struct __main_block_impl_0 *src)
{
     _Block_object_assign(&dst->array, src->array, BLOCK_FIELD_IS_OBJECT);
}

The _Block_object_assign function assigns the object to the member variable and calls a function, which is equivalent to the retain method.

Also, the __main_block_dispose_0 function calls the _Block_object_disposefunction to release the object, which is assigned to the member variable “array” in the struct for the Block.

static void __main_block_dispose_0(struct __main_block_impl_0 *src)
{
_Block_object_dispose(src->array, BLOCK_FIELD_IS_OBJECT);
}

The _Block_object_dispose function calls a function, which is equivalent to the instance method “release”, on the object in the target member variable of the struct.

By the way, the __main_block_copy_0 function (referred to as “copy function” in the rest of this book) and the __main_block_dispose_0 function (dispose function) are assigned to the member variables “copy” and “dispose” in the __main_block_desc_0 struct. But in the converted source code, none of these functions are called at all, including through the pointers. How are the functions used?

These functions are called when the Block is copied from the stack to the heap, and when the Block on the heap is disposed of (see Table 5–4).

images

When is the Block on the stack copied to the heap?

  • When the instance method “copy” is called on the Block
  • When the Block is returned from a function
  • When the Block is assigned to a member variable of id or the Block type class, with __strong qualifier
  • When the Block is passed to a method, including “usingBlock” in the Cocoa Framework, or a Grand Central Dispatch API

“When the instance method ’copy’ is called on the Block,” if the Block is on the stack, the Block is copied to the heap. “When the Block is returned from a function” or “When the Block is assigned to a member variable of id or the Block type class, with __strong qualifier,” the compiler automatically calls the _Block_copy function with the target Block as an argument, which is equivalent to calling a “copy” instance method on the Block. “When the Block is passed to a method, including ’usingBlock’ in the Cocoa Framework, or a Grand Central Dispatch API,” the instance method “copy” is called on the Block or the _Block_copy function is called with the Block as the argument inside the method or function.

A Block on the stack will be copied that way in the various situations, but, in a sense, all the situations are just the same. Actually, a Block is copied only when the _Block_copy function is called.

On the contrary, the dispose function is called when a Block on the heap is released and disposed of because no one has ownership of it. It is equivalent to the dealloc method of objects. With this mechanism, the object, which is captured by a Block, can exist beyond the variable scope by assigning to an automatic variable with a __strong qualifier. Although we skipped it in the section “__block specifier,” this mechanism with copy and dispose functions is used for __block variables as well.

static void __main_block_copy_0(
    struct __main_block_impl_0*dst, struct __main_block_impl_0*src)
{
    _Block_object_assign(&dst->val, src->val, BLOCK_FIELD_IS_BYREF);
}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    _Block_object_dispose(src->val, BLOCK_FIELD_IS_BYREF);
}

In the converted source code, a portion related to the struct for the Block is almost the same except for the difference listed in Table 5–5.

images

The argument BLOCK_FIELD_IS_OBJECT and BLOCK_FIELD_IS_BYREF switches the target of copy and dispose functions over the object or the __block variable.

The same way it takes ownership of the captured object by the copy function and the ownership is released by the dispose function, it takes ownership of the __block variable by the copy function and the ownership is released by the dispose function.

Now we’ve learned that when an object is assigned to automatic variables with __strong, the object can exist beyond the variable scope, and when a __block variable is copied to the heap and a Block on the heap has ownership of it, it can exist beyond the variable scope.

When You Should Call the “copy” Method

By the way, in the previous source code, what will happen when the instance method “copy” isn’t called on the Block?

blk_t blk;

{
    id array = [[NSMutableArray alloc] init];
    blk = ^(id obj) {
        [array addObject:obj];
        NSLog(@"array count = %ld", [array count]);
    };
}

blk([[NSObject alloc] init]);
blk([[NSObject alloc] init]);
blk([[NSObject alloc] init]);

As the result, the application is terminated.

Only when the _Block_copy function is called, is ownership of the captured object type automatic variable with a __strong qualifier taken. So, as an example, if the _Block_copy function isn’t called, the object is disposed of even if the object is captured. Therefore, when you use an automatic variable of an object type inside a Block, you should call the instance method “copy” on the Block, except in the following situations.

  • When the Block is returned from a function
  • When the Block is assigned to a member variable of id or the Block type class, with a __strong qualifier
  • When the Block is passed to a method, including “usingBlock” in the Cocoa Framework, or a Grand Central Dispatch API

Next, let’s see what happens when an object is stored in a __block variable.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值