美文网首页
OC总结篇 - Block

OC总结篇 - Block

作者: 亲爱的大倩倩 | 来源:发表于2019-10-17 20:46 被阅读0次
    int multiplier = 6
    int(^ Block)(int) = ^int(int num){
          return num* multiplier
    };
    Block(2);
    
    Block
    "Block是什么"
    是将函数及其执行上下文封装起来的对象
    内部有isa指针和FuncPtr函数指针
    isa说明他是个对象,FuncPtr指针指向了函数实现
    
    "Block调用是什么"
    就是函数调用
    当我们调用block(2)时,内部实现是
    通过block结构体里面的函数指针,取出对应的执行体.将参数传递进来(block本身,2),然后进行内部调用
    
    源码分析
    "Block源码结构体"中包括以下内容,说明了Block为什么是对象以及是怎么将函数和执行上下文封装的
    
    -block_impl结构体
                isa指针
                FuncPtr函数指针,指向我们在block花括号中的执行体
    -block相关描述的结构体
    -block中传进来的参数(局部变量)
    
    "截获变量的特性的内部实现"
    最上面的示例中,当截获了变量multiplier,是把它传到上述的block结构体中
    注意❤️:一旦在block结构体中赋值了,再操作时就是对结构体中的变量操作了,不是对block外的变量操作了
    
    若传进来:
    局部变量基本数据类型                    - 在block结构体中,直接截获这个值,赋值给block内部使用
    局部变量对象类型(不知道为啥成员变量也截获) - 在block结构体中,连同对象的修饰符一起截获,赋值给block内部使用  
    注意❤️:block的循环引用,就是因为局部对象是联通修饰符一起截获的
    静态局部变量                           - 在block结构体中,以指针形式截获,也就是说,如果在block的定义之后,对静态局部变量值进行修改,再调用block时,用的是最新的值
    全局变量                              - 不在block结构体中
    静态全局变量                           - 不在block结构体中
    
    "截获变量总结"
    局部变量基本数据类型  - 直接截获,传进来是什么就是什么,不会改变
    局部变量对象类型     - 连同所有权修饰符一起截获
    静态局部变量        -指针截获,值会改变
    全局变量           -不截获,值会改变
    静态全局变量        -不截获,值会改变
    
    截获变量示例
    __block修饰符

    对截获变量进行赋值操作时需要添加__block
    注意❤️:赋值不等于使用!!!

    array = [NSMutableArray array]就是赋值,若在block内部调用的话,需要为外部的array声明添加__block修饰符
    [array addObject:@!23]就是使用而不是赋值
    
    "如何使用__block修饰符"
    
    "以下变量的赋值操作,需要使用__block修饰的情况" 
    添加__block之后,当外部变量值改了之后,block内部调用时也会更改
    1. 在block内部对局部变量基本数据类型进行赋值操作时
    2. 在block内部对局部变量对象数据类型进行赋值操作时
    
    "以下变量的赋值操作,不需要__block修饰" 
    1. 在block内部对静态局部变量进行赋值操作时
    2. 在block内部对全局变量进行赋值操作时
    3. 在block内部对静态全局变量进行赋值操作时
    
    因为全局变量和静态全局变量都不涉及截获操作
    静态局部变量是通过指针来使用的,操作的是block外部的变量,所以不需要修饰
    
    "__block做了什么"
    __block修饰的变量会变成对象
    举个🌰,当我们执行这句代码"__block int num"之后 ,num不再是个int型,而是变成个结构体,包含如下
    1. void* isa
    2. int num
    3. __forwarding指针
    
    "_forwarding指针"
    存在的意义
    不论在任何内存位置,我们都可以通过_forwarding指针顺利的访问同一个__block变量
    若没有对__block进行copy,那么操作的是栈上的__block变量
    如果copy后,不论是在栈还是堆,我们对__block的修改活赋值,都是对堆上的__block进行的
    
    栈上的__block变量的__forwarding指针,是指向__block自身
    当block外部的num改变时,__forwarding指针会去block结构体中找到里面的num对象进行赋值,但要注意这是栈上的block才会这样
    如下图
    
    如果对__block变量进行copy操作后,会在堆上面产生完全一样的block变量
    栈上的__forwarding指针指向堆上的__block变量,而堆上的__forwarding指针指向自身的__block
    所以说,在经过了copy之后,只要对这个值进行了修改,__forwarding指针改的都是堆上的值
    如下图
    
    Block的内存管理
    "block有哪几类"
    impl.isa = NSConcteteStackBlock, isa会标记block是哪种类型
    
    全局block = NSConcreteGlobalBlock        存放在内存的已初始化数据区域中
    栈block = NSConcreteStackBlock           存放在内存的栈上面
    堆上面的block = NSConcreteMallocBlock     存放在内存的堆上面
    
    "对于block的copy操作"
    全局block - copy后什么也不做
    栈block - copy后会在堆上产生一个block
    堆block - copy后会增加其引用计数
    
    问题🛫:
    P类中有个assign修饰的block,假如在方法A中,我们P.block = ^(int){***}
    因为方法A是在栈上,执行完在内存中就销毁了,假如在后面我们又调用了P.block
    就会崩溃!!!
    
    "block的销毁"
    栈block变量在作用域结束后就销毁了
    
    当我们对栈上block进行copy之后,会在堆上产生一模一样的blcok,但分占了栈和堆两块内存空间,当作用域结束后,栈上的block会销毁,但堆上的不销毁
    在MRC环境下,对栈上block进行copy后,会内存泄露,因为如果堆上的block没有其他变量指向,就会产生内存泄露
    

    Block的循环引用
    "为什么会产生循环引用"
    在截获变量中,若对象P强引用block,block内部又截获了strong类型的数组对象,就会循环引用
    
    "怎么打破循环引用"
    __weak
    

    __block修饰符引起的循环引用


    注意MCBlock不是个block,只是个类名字
    上图的问题是,在MRC下不会产生循环引用,但在ARC下会产生循环引用,引起内存泄露
    

    解决方式
    下图这个解决方案有个弊端,也就是如果我们很长时间都没有调用这个block的话,这个循环引用就一直存在

    相关文章

      网友评论

          本文标题:OC总结篇 - Block

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