美文网首页JG专题
《Objective-C高级编程》Blocks 阅读笔记 ite

《Objective-C高级编程》Blocks 阅读笔记 ite

作者: dibadalu | 来源:发表于2016-02-18 16:45 被阅读110次

《Objective-C高级编程》Blocks 阅读笔记系列

《Objective-C高级编程》Blocks 阅读笔记 item1(Blocks概要和模式)
《Objective-C高级编程》Blocks 阅读笔记 item2(Block的实质)
《Objective-C高级编程》Blocks 阅读笔记 item3(截获自动变量值)
《Objective-C高级编程》Blocks 阅读笔记 item4(__block说明符)
《Objective-C高级编程》Blocks 阅读笔记 item5(Block存储域)
《Objective-C高级编程》Blocks 阅读笔记 item6(__block变量存储域)
《Objective-C高级编程》Blocks 阅读笔记 item7(截获对象)
《Objective-C高级编程》Blocks 阅读笔记 item8(__block变量和对象)
《Objective-C高级编程》Blocks 阅读笔记 item9(Block循环引用)
《Objective-C高级编程》Blocks 阅读笔记 item10(copy/release实例方法)

2.3 Blocks的实现

2.3.6 截获对象

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

该源代码生成并持有NSMutableArray类的对象,但是附有__strong修饰符的赋值目标(变量array)变量作用域立即就会结束,因此对象被立即释放并废弃。

blk_t blk;

{
  id array = [[NSMutableArray alloc] init];
  blk = [^(id obj){
  
      [array addObject:obj];
      
      NSLog(@"array count = %ld", [array count]);
  } copy]; // 调用copy方法(Block从栈复制到堆)
}

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

变量作用域结束的同时,变量array被废弃,其对NSMutableArray类的对象的强引用失效,因此NSMutableArray类的对象被释放并废弃(此处我不确定是否会被废弃)。但是,该源代码运行正常,执行结果如下:

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

这意味着赋值给变量array的NSMutableArray类的对象在Block的执行部分超出其变量作用域而存在。
经clang转换:

/* Block的结构体 / 函数部分  */

// 结构体 __main_block_impl_0
struct __main_block_impl_0 {
    // 成员变量
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    id __strong array; 
    /*
    理解:
    1. 被NSMutableArray类对象并被截获的自动变量array,是附有__strong修饰符的成员变量。在Objective-C中,C语言结构体不能含有附有__strong修饰符的变量。因为编译器不知道何时进行C语言结构体的初始化和废弃操作,不能很好地管理内存。
    2. 但是,Objective-C的运行时库能准确把握Block从栈复制到堆以及堆上的Block被废弃的时机,因此Block的结构体即时含有附有__stong修饰符或__weak修饰符的变量,也可以恰当地进行初始化和废弃。
    */
    
    // 构造函数
    __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;
    }
};

// 静态函数 __main_block_func_0
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]);
}

// 静态函数 __main_block_copy_0
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);
    /*
    理解:
    1. __main_block_copy_0函数使用_Block_object_assign函数将“对象类型对象”赋值给Block的结构体成员变量array中并持有该对象
    2. _Block_object_assign函数调用“相当于ratain实例方法的函数”,将“对象”赋值在对象类型的结构体成员变量中。
    */
}

// 静态函数 __main_block_dispose_0
static void __main_block_dispose_0(struct __main_block_impl_0 *src){
    _Block_object_dispose(src->array, BLOCK_FIELD_IS_OBJECT);
    /*
    理解:
    1. __main_block_dispose_0函数使用_Block_object_dispose函数,释放赋值在Block的结构体成员变量array中的对象。
    2. _Block_object_dispose函数调用相当于release实例方法的函数,释放赋值在对象类型的结构体成员变量中的对象。
    */
}

// 静态结构体 __main_block_desc_0
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*);
} __mian_block_desc_0_DATA = {
    0,
    sizeof(struct __main_block_impl_0),
    __main_block_copy_0,
    __main_block_dispose_0
};
/*
理解:
1. __main_block_copy_0函数(copy函数)和__main_block_dispose_0函数(dispose函数)指针被赋值__main_block_desc_0结构体成员变量copy和dispose中,但是在转换后的源代码中,这些函数包括使用指针全都没有被调用。
2. 而是,在Block从栈复制到堆时以及堆上的Block被废弃时会调用这些函数。
*/

/* Block语法,使用Block部分 */

blk_t blk;

{
    id __strong array = [[NSMutableArray alloc] init];
    
    blk = &__main_block_impl_0(__main_block_func_0, &__mian_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]);

表 调用copy函数和dispose函数的时机

函数 调用时机
copy函数 栈上的Block复制到堆时
dispose函数 堆上的Block被废弃时

*** 何时栈上的Block会复制到堆 ***

  • 调用Block的copy实例方法时
  • Block作为函数返回值返回时
  • 将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
  • 在方法名中含有usingBlock的Cocoa框架方法或GCD的API中传递Block时

在栈上的Block被复制到堆时copy函数被调用,而在释放复制到堆上的Block后,谁都不持有Block而被废弃时dispose函数被调用。正因为这种构造,通过使用附有__strong修饰符的自动变量,Block中截获的对象才能给超出其变量作用域而存在。

*** 如何区分copy函数和dispose函数的对象类型 ***


表 截获对象时和使用__block变量时的不同

对象 BLOCK_FIELD_IS_OBJECT
__block变量 BLOCK_FIELD_IS_BYREF

通过BLOCK_FIELD_IS_OBJECT和BLOCK_FIELD_IS_BYREF参数,区分copy函数和dispose函数的对象类型是对象还是__block变量。

但是,与copy函数持有被截获的对象,dispose函数释放截获的对象相同,copy函数持有所使用的__block变量,dispose函数释放所使用的__block。

由此可知,Block中使用的赋值给附有__stong修饰符的自动变量的对象和复制到堆上的__block变量,由于被堆上的Block所持有,因而可超出其变量作用域而存在。

*** 何种情形下,不调用Block的copy实例方法 ***
在Block使用对象类型自动变量是,除以下情形外,推荐调用Block的copy实例方法:

  • Block作为函数返回值返回时
  • 将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
  • 在方法名中含有usingBlock的Cocoa框架方法或GCD的API中传递Block时

相关文章

网友评论

本文标题:《Objective-C高级编程》Blocks 阅读笔记 ite

本文链接:https://www.haomeiwen.com/subject/xzimkttx.html