简单了解Block

来源:互联网 发布:淘宝基金在哪里看 编辑:程序博客网 时间:2024/06/03 21:00
block一般作为回调函数,形式:
int (^blk_t)(int)=^int (int count){return count+1;},对应的是^ 返回类型(参数列表){表达式};
用typedef如下:
typedef int (^blk)(int );
blk elk_t=^(int count){return count+1};
一、截获变量
block能截获变量,但不能修改变量,想修改变量,变量前面加__block,即使是静态变量,静态全局变量也不能修改。
block截获了指针可以使用它,不能截获C数组。
block可截获:变量(int a,double b),对象(实际是指针)
(block将截获的变量作为自己的成员变量进行访问,超出作用域释放)
二、Block实质
白皮书上说block是OC类对象,我觉得它更像结构体实例对象。结构体实例变量和普通变量一样,在栈上分配空间,超出作用域会被释放。
类:类是引用类型在堆上分配,类的实例进行赋值只是复制了引用,都指向同一段实际对象分配的内存
结构:结构是值类型在栈上分配。
Block结构体:
struct__main_block_impl_0
{
   
struct __block_impl impl;
   
struct __main_block_desc_0 *Desc;
    //截获的自动变量,作为block的成员变量
    id array;//截获对象

    //结构体的函数
    __main_block_impl_0(void*fp,struct__main_block_desc_0 *desc,intflags=0)
    {
        impl.isa=&NSConcreteStackBlock;
        impl.Flags=flags;
        impl.FuncPtr=fp;
        Desc=desc;
    }
};
定义完block之后,其实是创建了一个函数,在创建结构体的时候把函数的指针一起传给了block,所以之后可以拿出来调用。
有__block修饰的变量,实际上也是结构体实例。
struct__Block_byref_val_0
{
    __isa;
    __forwarding;
//指向自己的指针
    __flags;
    __size;
    val;        
//使用值
}
Block的struct__main_block_impl_0结构体实例持有指向__block变量的struct__Block_byref_val_0结构体实例指针。
三、Block的存储
Block实例和__block实例一般默认创建出来都是存储在栈上的,就像普通变量,
当作用域结束时,Block和__block会被复制到堆上,栈上__block变量的forwarding指针指向堆上的变量,这样就保证了变量同步。
需要复制的情况:
手动调用copy方法时;
Block作为函数的返回值;
将Block赋值给strong类型变量时;
在方法名中有usingBlock的Cocoa框架方法或GCD的API中传递Block时;
以上情况都会调用_Block_copy函数,除了以上情况下,推荐手动调用copy

复制实际调用的是_Block_copy方法,需要废弃时调用dispose方法。
Block截获两种变量,一种是_block修饰的变量,一种是对象(指针)

当Block调用copy函数时,Block持有截获对象的强引用,Block复制后强引用一直存在,所以被截获的对象不会被释放。

四、循环引用
最常见的是,类的成员变量有Block,Block函数里面引用self
破坏循环引用的方法:用__block变量tmp=self,Block里面用tmp,用完以后tmp置nil
                                    用__weak变量tmp=self,Block里面用tmp
在ARC无效时,__block变量用完以后不用赋值nil,因为ARC无效,Block复制到堆上时,不会对__block变量retain。






0 0
原创粉丝点击