Block底层常见问题解析

循环引用

下面代码会产生循环引用吗?

    __weak typeof(self) weakSelf = self;
    self.doWork1 = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        weakSelf.doWork2 = ^{
            NSLog(@"%@", strongSelf);
        };
       weakSelf.doWork2();
    };
    self.doWork1();

显然是会的。原因在于strongSelf和doWork2产生了循环引用。
解决如下点击了解更多关于循环引用的问题和解决方案:

__weak typeof(self) weakSelf = self;
    self.doWork1 = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        
        __weak typeof(strongSelf) weakSelf2 = strongSelf;
        strongSelf.doWork2 = ^{
            __strong typeof(weakSelf2) strongSelf2 = weakSelf2;
            NSLog(@"%@", strongSelf2);
        };
        strongSelf.doWork2();
    };
    self.doWork1();

//或者
    __weak typeof(self) weakSelf = self;
    self.doWork1 = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        strongSelf.doWork2 = ^{
            __strong typeof(weakSelf) strongSelf2 = weakSelf;
            NSLog(@"%@", strongSelf2);
        };
        strongSelf.doWork2();
    };
    self.doWork1();

作用域问题

下面的self.mArray和arr分别打印出什么?

- (void)test
{
   NSMutableArray *arr = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
    self.mArray = arr;

    void (^block1)(void) = ^{
       [arr addObject:@"3"];
       [self.mArray addObject:@"a"];
       NSLog(@"arr: %@",arr);
       NSLog(@"mArray: %@",self.mArray);
    };
    [arr addObject:@"4"];
    [self.mArray addObject:@"5"];

    arr = nil;
    self.mArray = nil;
    block1();
}

arr正常打印,self.mArray打印为空,原因在于外面的变量arr和blcok里的arr作用域不一样,不是同一个arr,外面修改无效。而self.mArray的作用域是整个类的,自然包括block中。

block copy操作

1、这段程序能否正常运行?

  //block1也被释放
    NSObject *a = [NSObject alloc];
    void(^__weak block1)(void) = nil;
    {
        void(^block2)(void) = ^{
            NSLog(@"---%@", a);
        };
        block1 = block2;
        NSLog(@"1 - %@ - %@",block1,block2);
    }
    block1();

不能。block1 = block2没有发生copy操作,引用计数没有增加,block1和block2指向的是同一个block,当离开了作用域block2被释放,block1也跟着被释放。
2、下面的代码能否正常运行?

struct _CustomBlock {
    void (*invoke)(void);
};

- (void)customBlock
{
    int a = 0;
    void(^ __weak weakBlock)(void) = ^{
        NSLog(@"-----%d", a);
    };

    //这里不发生copy操作,blc指向weakBlock
    struct _CustomBlock *blc = (__bridge struct _CustomBlock *)weakBlock;
    //copy到堆区,strongBlock已经是新的block
    id __strong strongBlock = [weakBlock copy];
    //原先的weakBlock的invoke置为nil不影响strongBlock
    blc->invoke = nil;
    //这里strongBlock已经是堆区的block,不再发生copy
    void(^strongBlock1)(void) = strongBlock;
    strongBlock1();
}

显然是可以运行的。原因是blc=weakBlock时没有发生copy操作,blc指向的是weakBlock,而在执行strongBlock = [weakBlock copy]时才发生copy,此时strongBlock指向的是新的copy到堆区的block,因此在执行blc->invoke = nil只是将原来的block的invoke置为空,不影响新copy出来的block。这个demo说明了两个问题:1、weak修饰的block直接赋值是不会发生copy的;2、未发生过copy的block如果发生了copy操作就会生成新的block并copy到堆区。

__block的引用和copy & 普通对象的引用和copy 的比较

普通对象的引用和copy
    /**1、创建对象objc1*/
    NSObject *objc1 = [NSObject new];
    NSLog(@"%---ld",CFGetRetainCount((__bridge CFTypeRef)(objc1)));//1

    /**2、block1 引用对象objc1,引用计数+1, 发生copy操作引用计数再+1*/
    void(^block1)(void) = ^{//+2
        NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc1)));
    };
    block1();//打印的引用计数为3
    
    /**3、block2 引用对象objc1,block2是weak变量,赋值不发生copy*/
    void(^__weak block2)(void) = ^{//+1
        NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc1)));
    };
    block2();//打印的引用计数为4

    /**4、block2发生copy操作,所以objc1引用计数+1*/
    void(^block3)(void) = [block2 copy];//+1
    block3();//打印的引用计数为5
    
    /**5、block3虽然调用copy方法,但是此处copy未生效,原因是block3是block2执行copy出来的,在底层如果是已经copy到堆区的block再次进行copy的时候是不会生效的,所以objc1引用不会增加, 引用计数依然为5*/
    void(^block4)(void) = [block3 copy];//+0
    block4();//打印的引用计数为5
    

1、创建对象objc1,此时objc1的引用计数为1;
2、这里block1引用objc1对象,使得objc1引用计数加1。由于block1产生copy操作的同时会对objc1进行copy,所以引用计数再一次加1,所以为3;
3、这里block2引用objc1对象,使得objc1引用计数加1。由于block2是weak对象,没有发生copy操作,所以objc1引用计数没有在加1,所以为4;
4、由于block2发生copy操作的同时会对objc1进行copy,所以引用计数再一次加1,所以为5;
5、block3虽然调用copy方法,但是此处copy未生效,原因是block3是block2执行copy出来的,在底层如果是已经copy到堆区的block再次进行copy的时候是不会生效的,所以objc1引用不会增加, 引用计数依然为5;

__block的引用和copy
/**6、创建__block修饰的对象objc2, objc2引用计数为1*/
    __block NSObject *objc2 = [NSObject new];
    NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc2)));//1
    
    /**7、block5引用objc2,objc2引用计数为1*/
    void(^block5)(void) = ^{//+0
        NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc2)));//1
    };
    block5();//打印的引用计数为1
    
    /**8、block6引用objc2,objc2引用计数为1*/
    void(^__weak block6)(void) = ^{//+0
        NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc2)));
    };
    block6();//打印的引用计数为1
    
    /**9、block6发生copy操作,objc2引用计数为1*/
    void(^block7)(void) = [block6 copy];//+0
    block7();//打印的引用计数为1

6、创建__block修饰的对象objc2, objc2引用计数为1;
7、block5引用objc2,objc2引用计数为1;
8、block6引用objc2,objc2引用计数为1;
9、block6发生copy操作,objc2引用计数为1.

这里因为objc2是__block修饰的对象,所以block4引用的是对象objc2的指针,而不是引用objc2指向的对象,因此不管多少个block引用objc2,进行多少次copy,objc2引用计数不变。点击了解更多Block底层原理。

你可能感兴趣的:(Block底层常见问题解析)