美文网首页
OC底层原理探索—block(上)

OC底层原理探索—block(上)

作者: 十年开发初学者 | 来源:发表于2021-08-25 16:02 被阅读0次

    block分类

    • 全局block __NSGlobalBlock__:位于全局区,在Block内部不使用外部变量,或者只使用静态变量和全局变量
    • 栈区block __NSStackBlock__:位于堆区,在Block内部使用变量或者OC属性,并且赋值给强引用或者Copy修饰
    • 堆区block __NSMallocBlock__:位于栈区,Block内部使用变量或者OC属性,但不能赋值给强引用或者Copy修饰
        int a = 10;
        void (^blockA)(void) = ^{
            
        };
        
        void (^blockB)(void) = ^{
            NSLog(@"Cooci - %d",a);
        };
        void (^__weak blockC)(void) = ^{
            NSLog(@"Cooci - %d",a);
        };
     
        NSLog(@"----------------:%@",blockA);
        NSLog(@"----------------:%@",blockB);
        NSLog(@"----------------:%@",blockC);
    
    image.png
    ARC环境下,需要在block前进行__weak修饰,并且调用外部变量,才会是__NSStackBlock__

    block引用计数

    • NSGlobalBlock:,存储于全局数据区,由系统管理其内存,retain、copy、release操作都无效。如果访问了外部static或者全局变量也是这种类型。
    • NSStackBlock:retain、release操作无效,存储于栈区,变量作用域结束时,其被系统自动释放销毁。
    • NSMallocBlock:支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数,当引用计数为零的时候释放。
        NSObject *objc = [NSObject new];
        NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));
        void(^strongBlock)(void) = ^{
            NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));
        };
        strongBlock();
    
        void(^__weak weakBlock)(void) = ^{ // + 1
            NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));
        };
        weakBlock();
        
        void(^mallocBlock)(void) = [weakBlock copy];
        mallocBlock();
    
    image.png
    这里的正确答案为1,3,4,5,这里我们就有疑问了,为什么第二步引用计数直接加了2呢?
    • strongBlock中,首先先要捕获外部变量objc,这时候引用计数+1,然后由栈区copy到堆区,引用计数再+1,也就变为了3
    • weakBlock__weak修饰,所以是个栈区block,只是捕获外部变量+1,引用计数变为了4
    • mallocBlock进行了一步[weakBlock copy],相当于栈区copy到堆区,引用计数+1,引用计数变为了5

    block堆栈释放差异

    案例1
         int a = 0;
        void(^__weak weakBlock)(void) = nil;
        {
            void(^__weak strongBlock)(void) = ^{
                NSLog(@"---89899:%d", a);
            };
           
            weakBlock = strongBlock;
            NSLog(@"1 - %@ - %@",weakBlock,strongBlock);
        }
        weakBlock();
    
    
    image.png
    • weakBlock是个栈block
    • 在代码块中,定义strongBlock栈block
    • strongBlock赋值给weakBlock,此时两个block均指向同一个栈block
    • 因为两个栈block的生命周期到方法结束
    • 所以weakBlock能够正常调用
    案例2
        int a = 0;
        void(^__weak weakBlock)(void) = nil;
        {
            void(^strongBlock)(void) = ^{
                NSLog(@"---89899:%d", a);
            };
           
            weakBlock = strongBlock;
            NSLog(@"1 - %@ - %@",weakBlock,strongBlock);
        }
        weakBlock();
    
    image.png

    该案例是对案例1进行了小修改,将strongBlock改为堆block

    • weakBlock是个栈block
    • 在代码块中,定义strongBlock堆block
    • strongBlock赋值给weakBlock,进行指针,此时两个block均指向同一个堆block,但是没有强引用这个block
    • 代码块执行完毕后,该NSMallocBlock就会被释放,此时weakBlock指向的对象已经被释放,形成野指针,所以无法正常执行。

    接下来,将weakBlock和NSMallocBlock都去掉__weak,则能够正常执行,因为在对weakBlock进行了赋值时,weakBlock对堆中的block进行了强引用。所以代码块运行完后不会释放,能够正常运行

    image.png

    block循环引用

        self.name = @"SH";
    
        self.block = ^(void){
            NSLog(@"%@",self.name);
    
        };
        self.block();
    

    上述这种情况,会造成循环引用

    • self持有持有block
    • 在block内部持有self
    • 这样就造成了vc无法调用dealloc,而block接收不到release信号,导致retainCount不等于0,从而也无法释放
    循环引用解决方法
    • weak使用
        self.name = @"SH";
        __weak typeof(self) weakSelf = self;
        self.block = ^(void){
            NSLog(@"%@",weakSelf.name);
    
        };
        self.block();
    

    这个时候self持有block,而block弱引用self,弱引用会自动变为nil,强持有中断,所以不会引起循环引用。

    但是这样还会存在一个问题

    image.png
    这里虽然没有引起循环引用,但是block延迟2秒执行,这样就造成了vc释放,而block无法获取vc中的属性。
    • 强弱共舞
        self.name = @"SH";
        __weak typeof(self) weakSelf = self;
        self.block = ^(void){
            __strong __typeof(weakSelf)strongSelf = weakSelf;
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"%@",strongSelf.name);
            });
        };
        self.block();
    

    这里在block添加__strong __typeof(weakSelf)strongSelf = weakSelf;,添加strongSelf后这里的持有关系就变为了self->block-> strongSelf-> weakSelf->self,这里weakSelf被强引用了就不会被自动是释放,但是strongSelf是个临时变量,生命周期只在block内部,block执行完毕后strongSelf会被释放,而weakSelf也会被释放

    • 手动中断持有关系
        __block ViewController *vc = self;
        self.block = ^(void){
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"%@",vc.name);
                vc = nil;
            });
        };
        self.block();
    

    这里的持有关系为self-> block ->vc->self,当vc在block中被置为nil后,这个环就被打破了,也就不会造成循环引用

    • block传参
        self.name = @"SH";
    
        self.block = ^(ViewController *vc){
            NSLog(@"%@",vc.name);
        };
        self.block(self);
    

    self作为参数传入block中,这里是对self的指针进行拷贝,只是指向同一片内存,并没有对self进行持有

    image.png

    循环引用案例

    • 静态变量持有
    //全局声明
        static ViewController *staticSelf_;
    
        - (void)blockWeak_static {
            __weak typeof(self) weakSelf = self;
            staticSelf_ = weakSelf;
        }
    

    这种情况会导致循环引用,weakSelf虽然是弱引用,但是staticSelf_静态变量,并对weakSelf进行了持有,staticSelf_释放不掉,所以weakSelf也释放不掉!导致循环引用!

    • __strong持有问题
    - (void)block_weak_strong {
        __weak typeof(self) weakSelf = self;
        self.doWork = ^{
            __strong typeof(self) strongSelf = weakSelf;
    
            weakSelf.doStudent = ^{
                NSLog(@"%@", strongSelf);
            };
           weakSelf.doStudent();
        };
       self.doWork();
    }
    

    这种情况会引起循环引用,在doWork内部,__strong typeof(self) strongSelf = weakSelf;doStudent这个内部block调用了外部变量,所以他会从栈block copy到堆中,从而导致strongSelf的引用计数增加,无法释放掉,进而导致循环引用!

    相关文章

      网友评论

          本文标题:OC底层原理探索—block(上)

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