• block的原理是怎样的?本质是什么?

    Block其实就是一个OC对象,具体来说是封装了函数调用以及调用环境的OC对象。

    1. struct __main_block_desc_0 {
    2. size_t reserved;
    3. size_t Block_size;
    4. };
    5. struct __block_impl {
    6. void *isa; //isa指针
    7. int Flags;
    8. int Reserved;
    9. void *FuncPtr; //函数调用的地址
    10. };
    11. struct __main_block_impl_0 {
    12. struct __block_impl impl;
    13. struct __main_block_desc_0* Desc;
    14. int age;
    15. };
    • block的变量捕获(capture)

    为了保证block内部能够正常访问外部的变量,block有个变量捕获机制,简单来说就是变量是否在block内部生成。

    变量类型 捕获到block内部 访问方式


    局部变量
    auto 值传递
    static 指针传递
    全局变量 × 直接访问

    在block中self以及类似self.age属于局部变量,访问的方式是按值传递。
    Block - 图1

    • block的类型

    nblock有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型

    1. NSGlobalBlock( _NSConcreteGlobalBlock)

    2. NSStackBlock( _NSConcreteStackBlock )

    3. NSMallocBlock( _NSConcreteMallocBlock )

    Block - 图2

    block类型 环境
    NSGlobalBlock 没有访问auto变量
    NSStackBlock 访问了auto变量
    NSMallocBlock NSStackBlock调用了copy

    每一种类型的block调用copy后的结果如下所示

    Block - 图3

    • block的copy

    在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况

    1. block作为函数返回值时

    2. 将block赋值给__strong指针时

    3. block作为Cocoa API中方法名含有usingBlock的方法参数时

    4. block作为GCD API的方法参数时

    MRC下block属性的建议写法

    @property(copy,nonatomic)void(^block)(void);

    ARC下block属性的建议写法

    @property(strong,nonatomic)void(^block)(void);

    @property(copy,nonatomic)void(^block)(void);

    • 对象类型的auto变量
    • 当block内部访问了对象类型的auto变量时

    如果block是在栈上,将不会对auto变量产生强引用

    • 如果block被拷贝到堆上
    1. 会调用block内部的copy函数

    2. copy函数内部会调用_Block_object_assign函数

    3. _Block_object_assign函数会根据auto变量的修饰符(strong、weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用

    • 如果block从堆上移除
    1. 会调用block内部的dispose函数

    2. dispose函数内部会调用_Block_object_dispose函数

    3. _Block_object_dispose函数会自动释放引用的auto变量(release)

    Block - 图4

    • __block的作用是什么?有什么使用注意点?
    1. __block可以用于解决block内部无法修改auto变量值的问题

    2. __block不能修饰全局变量、静态变量(static)

    3. 编译器会将__block变量包装成一个对象

    Block - 图5

    block调用的代码。

    1. int no = 20;
    2. __block int age = 10;
    3. NSObject *object = [[NSObject alloc] init];
    4. __weak NSObject *weakObject = object;
    5. CJBlock cjBlock = ^{
    6. NSLog(@"%d", no);
    7. NSLog(@"age = %d", age);
    8. NSLog(@"%p", weakObject);
    9. };
    10. cjBlock();

    block实现代码

    1. typedef void (*CJBlock)(void);
    2. struct __Block_byref_age_0 {
    3. void *__isa;
    4. __Block_byref_age_0 *__forwarding;
    5. int __flags;
    6. int __size;
    7. int age;
    8. };
    9. struct __main_block_impl_0 {
    10. struct __block_impl impl;
    11. struct __main_block_desc_0* Desc;
    12. int no;
    13. NSObject *__weak weakObject;
    14. __Block_byref_age_0 *age; // by ref
    15. __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _no, NSObject *__weak _weakObject, __Block_byref_age_0 *_age, int flags=0) : no(_no), weakObject(_weakObject), age(_age->__forwarding) {
    16. impl.isa = &_NSConcreteStackBlock;
    17. impl.Flags = flags;
    18. impl.FuncPtr = fp;
    19. Desc = desc;
    20. }
    21. };
    22. static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
    23. __Block_byref_age_0 *age = __cself->age; // bound by ref
    24. int no = __cself->no; // bound by copy
    25. NSObject *__weak weakObject = __cself->weakObject; // bound by copy
    26. NSLog((NSString *)&__NSConstantStringImpl__var_folders_ld_ls1g9rvn1693sq6qgpbwjgbr0000gn_T_main_c1bc15_mi_0, no);
    27. NSLog((NSString *)&__NSConstantStringImpl__var_folders_ld_ls1g9rvn1693sq6qgpbwjgbr0000gn_T_main_c1bc15_mi_1, (age->__forwarding->age));
    28. NSLog((NSString *)&__NSConstantStringImpl__var_folders_ld_ls1g9rvn1693sq6qgpbwjgbr0000gn_T_main_c1bc15_mi_2, weakObject);
    29. }
    30. static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
    31. _Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
    32. _Block_object_assign((void*)&dst->weakObject, (void*)src->weakObject, 3/*BLOCK_FIELD_IS_OBJECT*/);
    33. }
    34. static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    35. _Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
    36. _Block_object_dispose((void*)src->weakObject, 3/*BLOCK_FIELD_IS_OBJECT*/);
    37. }
    38. static struct __main_block_desc_0 {
    39. size_t reserved;
    40. size_t Block_size;
    41. void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
    42. void (*dispose)(struct __main_block_impl_0*);
    43. } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
    44. int main(int argc, const char * argv[]) {
    45. /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
    46. int no = 20;
    47. __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
    48. NSObject *object = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
    49. __attribute__((objc_ownership(weak))) NSObject *weakObject = object;
    50. CJBlock cjBlock = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, no, weakObject, (__Block_byref_age_0 *)&age, 570425344));
    51. ((void (*)(__block_impl *))((__block_impl *)cjBlock)->FuncPtr)((__block_impl *)cjBlock);
    52. }
    53. return 0;
    54. }
    55. static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
    • 对象类型的auto变量、__block变量

    当block在栈上时,对它们都不会产生强引用

    当block拷贝到堆上时,都会通过copy函数来处理它们
    __block变量(假设变量名叫做a)
    _Block_object_assign((void)&dst->a,(void)src->a,8/BLOCK_FIELD_IS_BYREF/);

    对象类型的auto变量(假设变量名叫做p)
    _Block_object_assign((void)&dst->p,(void)src->p,3/BLOCK_FIELD_IS_OBJECT/);

    当block从堆上移除时,都会通过dispose函数来释放它们
    __block变量(假设变量名叫做a)
    _Block_object_dispose((void)src->a,8/BLOCK_FIELD_IS_BYREF*/);

    对象类型的auto变量(假设变量名叫做p)
    _Block_object_dispose((void)src->p,3/BLOCK_FIELD_IS_OBJECT*/);

    Block - 图6

    • 被__block修饰的对象类型

    当__block变量在栈上时,不会对指向的对象产生强引用

    当__block变量被copy到堆时

    1. 会调用__block变量内部的copy函数

    2. copy函数内部会调用_Block_object_assign函数

    3. _Block_object_assign函数会根据所指向对象的修饰符(strong、weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain)

    如果__block变量从堆上移除

    1. 会调用__block变量内部的dispose函数

    2. dispose函数内部会调用_Block_object_dispose函数

    3. _Block_object_dispose函数会自动释放指向的对象(release)

    1. //Block_object_assign函数会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain)
    2. __block __weak MJPerson *weakPerson = person;
    3. CJBlock block;
    4. {
    5. MJPerson *person = [[MJPerson alloc] init];
    6. __block __weak MJPerson *weakPerson = person;
    7. block = ^{
    8. NSLog(@"%p", weakPerson);
    9. };
    10. }
    11. block();
    12. //在MRC模式下,__block修饰的变量,比如person不管是strong还是weak类型,都不会形成强引用,都不会retain。
    13. CJBlock block;
    14. __block MJPerson *person = [[MJPerson alloc] init];
    15. block = [^{
    16. NSLog(@"%p", person);
    17. } copy];
    18. [person release];
    19. block();
    20. [block release];

    Block - 图7

    • 循环引用问题

    就是两个强指针互相引用,你引用我,我引用你,导致无法正常释放。

    Block - 图8

    解决循环引用问题 - ARC
    Block - 图9

    解决循环引用问题 - MRC

    Block - 图10
    在MRC的情况下 block修饰的修饰的变量,在Block的imp里面,不会有retain的操作,所以不存在强引用,所以再MRC的情况下,用block修饰变量,也可以解决循环引用的问题。

    • block的属性修饰词为什么是copy?使用block有哪些使用注意?

    block一旦没有进行copy操作,就不会在堆上

    使用注意:循环引用问题

    • block在修改NSMutableArray,需不需要添加__block?

    不需要,只是要使用array的值,比如[array addObject:@”chenjie”],而不是对array进行赋值操作。

    • 为什么有的block要加__strong修饰。
    1. __weak typeof(self) weakSelf = self;<br /> self.block = ^{<br /> __strong typeof(weakSelf) myself = weakSelf;<br /> NSLog(@"age = %lu", (unsigned long)myself->_age);<br /> };

    1、首先加__strong能保证编译正确,能正确的访问成员变量。
    2、保住self的命,在程序运行的周期内,不会被无故的销毁。