美文网首页
block资深解读【精品】

block资深解读【精品】

作者: swweaper5 | 来源:发表于2017-05-17 23:56 被阅读19次

    block也叫匿名函数,也叫闭包,很多语言都有闭包的概念,block是c语言的闭包演化而来
    block的格式一般是(void)(^blockname)(NSString *param); 或者 ^(void)(NSString *param){}
    block作为参数,property....http://fuckingblocksyntax.com/

    block的内存
    block有三种,分别存于不同的内存域,分别是NSGlobalBlock,NSStackBlock, NSMallocBlock
    默认是在NSGlobalBlock比如下面的形式,因为他初始化在栈上,而没有与其他内存发生任何交互,所以当代码结束时,block自动释放了,跟一个方法体没有区别,最后OC把他放到TEXT端。
    BlkSum blk1 = ^ long (int a, int b) {
    return a + b;
    };
    NSLog(@"blk1 = %@", blk1);

    第二种是NSStackBlock,这个是在栈上的内存,block创建时在栈上,并且用到了栈上定义的其他内存,所以这类block暂时在栈上,对外部变量也只有读的权限,没有写权限,因为这个时候只是copy了一份局部变量的值到栈上
    int base = 100;
    BlkSum blk2 = ^ long (int a, int b) {
    return base + a + b;
    };
    NSLog(@"blk2 = %@", blk2); // blk2 = <NSStackBlock: 0xbfffddf8>

    第三种是NSMallocBlock ,这个block是把栈上的block copy到了堆上
    BlkSum blk3 = [[blk2 copy] autorelease];
    NSLog(@"blk3 = %@", blk3); // blk3 = <NSMallocBlock: 0x902fda0>

    然而变量都有其作用域,那么跨作用域的变量值又怎么被block调用和修改呢呢?
    使用__block修饰符修改变量的作用域,而不是copy一份到栈上供block使用,
    对于static静态变量,由于其地址是固定的,所以block可以对其进行修改,__block修改基本类型的局部变量等效与静态变量
    @property (nonatomic, copy) void(^myBlock)(void);
    MyClass* obj = [[[MyClass alloc] init] autorelease];
    self.myBlock = ^ { [obj doSomething]; };
    这个主意几个点,
    1,定义block属性需要用copy,这样会把block对象从堆上移动到栈内存上,
    2,block引用了obj会让obj的内存引用计数加1

    block很容易出现retain cycle的,下面是常见的场景之一,reques引用了block,而block里面又持有request的引用,很多时候我们在self中copy了一个block,在block中又出现调用self的情况,这个时候就出现了retain cycle,解决办法是使用__block或者__weak打断强引用即可,其实以前争论的该用__block还是__weak其实是没有结果的,因为他们都能打断强引用,只要阻止了在block中调用外部OC对象并让引用计数增加就行了。出现retaincycle的情况不局限于两个object,有时候可能涉及到很多的Object,那么解决办法也是同理的,只要知道block调用外部OC变量会使其引用计数加1,还要遵守在block中调用外部变量时打断强引用就行了。
    ASIHTTPRequest request = [ASIHTTPRequest requestWithURL:url];
    [request setCompletionBlock:^{
    NSString
    string = [request responseString];
    }];
    或者:
    elf.myBlock = ^ { [self doSomething]; };

    还有个非常大的神坑,这次不会retaincycle,而是会crash,在携程代码中经常看到的坑如下:请注意,dispatchBlock跟OC定义的block又是两个概念,dispatch本身是一种安全的block,在进入block时会retain一次block的持有者也就是self,但是当碰到__block参数时,就不会retainself, 而dispatch的block又不属于self的属性,所以当block的任务还没有完成这个时候self已经释放了,那么dispatch的block就挂了,正常使用即可,但是比较坑的是,很多秀才在碰到block的地方就用__block,将Block作为参数传给dispatch_async时,系统会将Block拷贝到堆上,如果Block中使用了实例变量,还将retain self,因为dispatch_async并不知道self会在什么时候被释放,为了确保系统调度执行Block中的任务时self没有被意外释放掉,dispatch_async必须自己retain一次self,任务完成后再release self。但这里使用__block,使dispatch_async没有增加self的引用计数,这使得在系统在调度执行Block之前,self可能已被销毁,但系统并不知道这个情况,导致Block被调度执行时self已经被释放导致crash
    __block kkProducView* weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
    weakSelf.xx = xx;
    });

    防止retainCycle总结:
    真理:
    1,block引用外部基本数据类型使用__block.
    2,block引用外部对象使用__weak.
    3,block作为某对象的成员变量时使用copy,把block的内存复制到对上.//要不然block跑一遍就释放了

    改正:
    1,针对retainCycle问题,在arc之前无论对象还是基本数据类型用__block就不会错,但是到了arc的世界,一切都变了,arc下对象默认是strong的,那么在block里面调用__block修饰的对象会使对象的引用计数加1,如果不用__block修饰也会加1,使用__weak修饰就不会加1了,因为__weak把对象转成weak的,在block里面调用外部对象,引用计数不会加1.----理由:http://wufawei.com/2013/06/block-retain-cycle/

    二,block结构体
    struct
    Block_literal_1 {

    void
    *isa;

    int
    flags;

    int reserved;
    void (*invoke)(void *, …);//是一个函数指针,它指向的是Block被转换成函数的地址
    struct
    Block_descriptor_1 {

    unsigned long int
    reserved;

    unsigned long int
    size;

    // optional helper functions

    void (*copy_helper)(void *dst, void *src); // IFF (1<<25)

    void (*dispose_helper)(void *src); // IFF (1<<25)

    // required ABI.2014.5.25

    const char *signature; // IFF (1<<30)

         } *descriptor;
    // imported variables
    

    };
    1,isa:证明block是一个OC特性的对象,isa指针指向的是对象的地址,这个地址有两类,分别表示了存放在两种内存的block,
    _NSConcreteStackBlock/_NSConcreteGlobalBlock在没有开启ARC的情况下,如果Block中包含有局部变量则isa被初始化为前者,
    否则就被初始化为后者。而当ARC开启后,如果Block中包含有局部变量则isa被初始化为 _NSConcreteMallocBlock ,否则就被初始化为 _NSConcreteGlobalBlock
    2,invoke是一个函数指针,它指向的是我自己Block被转换成函数的地址
    3,最后的imported variables部分是Block需要访问的外部的局部变量,他们在编译就会被拷贝到Block中,这样一来Block就是成为一个闭包了
    4,闭包的解释:闭包是可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)

    阅读参考:
    http://tanqisen.github.io/blog/2013/04/19/gcd-block-cycle-retain/ 正确使用block

    相关文章

      网友评论

          本文标题:block资深解读【精品】

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