美文网首页架构师之路111
关于Block再啰嗦几句

关于Block再啰嗦几句

作者: OldSix1987 | 来源:发表于2016-05-27 14:07 被阅读134次

    1.一共有6种类型:

    BLOCK_EXPORT void * _NSConcreteStackBlock[32];

    // 只有在调用copy的情况下才会生成MallocBlock,ARC下会默认把栈上的block复制到堆上

    BLOCK_EXPORT void * _NSConcreteMallocBlock[32];

    BLOCK_EXPORT void * _NSConcreteGlobalBlock[32];

    //  后面三种主要是用GC

    BLOCK_EXPORT void * _NSConcreteWeakBlockVariable[32];

    BLOCK_EXPORT void * _NSConcreteAutoBlock[32];

    BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32];

    2.block是oc对象,含有isa的结构体

    3.block只有在主动调用copy的时候, 才可能会对外部变量(主要讲oc对象)的引用计数器造成影响,会对变量进行retain操作,(__block修饰后的变量在MRC下不会对oc对象进行retain操作,需要手动管理内存),其他所有情况都不会对其引用计数器造成改变。

    但是这里需要明确的是,block结构体内部其实会const一份外部变量(以参数的形式传入结构体)作为其结构体的成员变量,也就是将变量的副本存到了block结构体,所以对原对象的引用计数器没有影响。

    然后在block触发函数指针调用的时候可以直接进行值传递(注意这里不是赋值的意思,是指函数方法内会生成一个局部变量,也就是外部对象,对它进行一个值传递,赋值为结构体内的变量值,也叫做截取捕捉变量),但是修改内容以及复制都不被允许,因为const化了。(全局与静态,以及__block修饰 除外)。

    其实道理上也可以理解,外部的变量的作用域与生命周期问题,即使block通过指针拿到该变量的修改复制权限,其实也很危险,但变量作用域到头释放的时候,block内部的指针就是野指针。

    但是%p打地址的时候你会发现对象地址还是同一个(因为本质上还是同一个对象,外部release了,Block内部也用不了)。

    4.__block修饰变量相当于在block结构体内做了一次指针引用。

    针对oc对象,__block其实可以看作一种提醒编译器优化的标识,加上的话该oc对象会在block结构体内会生成一个__block的对象(含有isa的结构体,含有该对象,flag = 131 = BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT,,意思是使用结构体内部的copy/dispose辅助函数手动管理,内部无需再次retain,直接指针赋值即可),并且可以使该__block对象在栈和堆上都可以得到(fowarding指针),所以也就是,即使block从栈复制到堆上,所以加上__block修饰的外部变量,当copy到堆上的时候,外部变量不会被复制(或retain),而是直接赋值指针地址,因此该变量的引用计数器保持不变。

    如果不加的话(flag = BLOCK_FIELD_IS_OBJECT ),当copy到堆上时,block会调用了[obj retain]方法,引用计数器会+1, 否则复制到堆上时,会对找不到该变量(因为局部变量受作用域影响,肯定比block的生命周期短),且在复制的过程中,通过fowarding指针可以找到堆上的该变量。

    非oc对象复制的话,加__block的话,flag = BLOCK_FIELD_IS_BYREF,即使复制多次也只会复制一次,后面只是将该变量的引用计数器+1(但其实对于基本数据类型的话,我们也不会去关心引用计数器)。不加__block的话,相当于block结构体对基本数据类型对象做了一次const化到了结构体内,只能使用,不能修改和赋值。

    5.block作为参数,或者作为返回值时,MRC下,一定要copy到堆上,否则会超出作用域会被释放,导致野指针。这里也是日常代码时常见场合,建议最好使用ARC,编译器会默认把block复制到堆上,也就是[[block copy] autorelease]。

    6.block对于以参数形式传进来的对象,不会强引用。因为传进来的参数,根本不会进入block的结构体,只是以形参的方式在block触发函数指针时出现。

    7.block的循环引用问题,主要发生在当把block作用成员变量时,切记,如果block内部使用了self,或者其他成员变量(基本数据类型也不行),都会引起循环引用,__weak(ARC)和__block(MRC)都可以解决该问题。

    e.g.:

    __weak _-typeof(self) weakSelf = self;

    ^{[weakSelf someMethod];}

    对block的理解不深的情况下使用,很容易造成循环引用的泛滥。其实在苹果原生的设计系统中,在View与ViewController间的通讯主要还是使用delegate。

    而使用block的场合主要是类方法,比如UIView:

    [UIView animateWithDuration:1.0 animations:^{

            // 动画效果

    }];

    8.全局变量,全部静态变量,静态变量,不需要加入__block也可以在block中被修改,全局的好理解, 作用域、生命周期都比block要大要长,所以在block内可以直接使用、修改。

    静态变量(存储在.data区)也是同理,但是方案上,block在结构体内部获取了静态变量的指针,所以可以直接修改。

    9.^{printf("Hello World!")} 在MRC下,clang后会发现,没有引入外部变量时,但是block结构体isa指针仍然为StackBlock,但开启ARC后,isa指针我GlobalStack指针。

    推荐几篇好文&源码:(源码难看,但是还得看,否则都是道听途说)

    1.http://www.jianshu.com/p/e03292674e60#

    2.http://www.jianshu.com/p/51d04b7639f1

    3.http://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/BlocksRuntime/runtime.c

    4.http://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/BlocksRuntime/Block_private.h

    5.http://blog.ibireme.com/2013/11/27/objc-block/

    6.http://mobile.51cto.com/hot-403914.htm

    7.http://mobile.51cto.com/hot-403931.htm

    8.http://mobile.51cto.com/hot-403935.htm

    相关文章

      网友评论

        本文标题:关于Block再啰嗦几句

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