美文网首页
Block 使用总结

Block 使用总结

作者: Lefe | 来源:发表于2017-06-23 22:20 被阅读55次

    Block 内存管理:

    Block 内存主要分派到 NSGlobalBlock(data area),NSMallocBlock(堆 区 )和 NSConcreteStackBlock(栈区)。在 ARC 环境下,Lefe没有遇到过栈区的 Block,因为 Block 会自动拷贝到栈区。

    屏幕快照 2017-06-23 下午10.06.35.png
    - (void)testBlockMemoryManagerCase1
    {
        self.logId = @"Hello login";
        
        // __NSGlobalBlock__  全局,保存到数据区(data area) block
        /**
         1、当 block 字面量写在全局作用域时,即为 global block;
         2、当 block 字面量不获取任何外部变量时,即为 global block;
         */
        void(^block1)(void) = ^(){
        };
        block1();
        NSLog(@"block1: %@", block1);
        
        NSLog(@"finsh block: %@", self.finshBlock);
        
        // __NSMallocBlock__ 堆区的block,捕获了变量
        /**
         当 block 从栈拷贝到堆后,当栈上变量作用域结束时,仍然可以继续使用 block
         
         */
        void(^block2)(void) = ^(){
            NSLog(@"Block2: %@", self.logId);
        };
        block2();
        NSLog(@"block2: %@", block2);
        
        /**
         _NSConcreteStackBlock 栈区的 block
         如果其变量作用域结束,这个 block 就被废弃block 上的 __block 变量也同样会被废弃。
         为了解决这个问题,block 提供了 copy 的功能,将 block 和 __block 变量从栈拷贝到堆,就是下面要说的 _NSConcreteMallocBlock。
         */
    

    循环引用一:

    屏幕快照 2017-06-23 下午9.44.37.png
    - (void)testMemoryLeakCase1
    {
        self.logId = @"Hello logId";
        
        /**
         这种情况最容易发现,因为编译器会自动提示出现循环引用
         Why?
         self(SecondViewController)持有了 finshBlock,你可以把它当作一个普通的属性,是强引用
         而 finshBlock 又引用了 self,这样就形成了一个闭环。
         How?
         既然是因为出现了闭环,我们只需要打破这层闭环就可以,让 finshBlock 持有一个弱引用,这样 self(SecondViewController)持有了 finshBlock,但是 finshBlock 没有持有 self
         */
    
            /**
         修改前的:
         self.finshBlock = ^(BOOL isSuccess) {
            [self loginTest];
         };
         */
    
        // __weak typeof(self) weakSelf = self; 一般的宏定义是这样的
        __weak SecondViewController *wSelf = self;
        
        self.finshBlock = ^(BOOL isSuccess) {
            [wSelf loginTest];
        };
        
        /**
         在我们的应用中一般是下面这种方式写,为啥使用了 __weak 和 __strong ?
         有人可能会问,先 weak 后 strong,那相当于还是强引用了 self,你确定 strong的是 self?
         */
        
        /**
         打印:
         (lldb) p weakSelf
         (SecondViewController *) $0 = 0x0000000101c16f10
         (lldb) p self
         (SecondViewController *) $1 = 0x0000000101c16f10
         (lldb) p strongSelf
         (SecondViewController *) $2 = 0x0000000101c16f10
         (lldb)
         
         发现 weakSelf self 和 strongSelf 的内存地址是一样的,只是一次浅拷贝;
         */
        __weak typeof(self) weakSelf = self;
        
        self.finshBlock = ^(BOOL isSuccess) {
            // 如果没有这句话,当 self 被释放后,weakSelf 就变为了空,所以关于 weakSelf 的一些操作也就没什么意义了,如果还想让 weakSelf 所调用的一些方法有意义那么久需要强引用 weakSelf;
            __strong typeof(self) strongSelf = weakSelf;
            
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"weakSelf.logId: %@", strongSelf.logId);
                NSString *name = strongSelf.logId;
                if (name.length > 0) {
                    NSLog(@"Hello world");
                }
                [strongSelf loginTest];
            });
        };
        self.finshBlock(YES);
    }
    

    循环引用二:

    屏幕快照 2017-06-23 下午9.52.27.png
    - (void)testMemoryLeakCase2
    {
        /**
         这里面出现了两个对象的内存泄漏: task 和 self
         task的内存泄漏:
         task 有个属性叫 blcok,但是在 block 中又捕获了 task,这样就形成了一个闭环
         self 的内存泄漏:
         因为这个 block 中捕获了 self,block 没有释放那么 self 咋么能释放呢?
         所以只要打破这个闭环,self 就释放了。
         
         */
        AsyncTask *task = [AsyncTask new];
        
        __weak AsyncTask *wTask = task;
        task.block = ^(BOOL isFinish) {
            NSString *name = wTask.lastLoginId;
            self.logId = name;
        };
        [task sendLogin];
        
        /**
         AsyncTask *task = [AsyncTask new];
         task.block = ^(BOOL isFinish) {
         NSString *name = task;
         self.logId = name;
         };
         [task sendLogin];
         */
    }
    

    循环引用三:

    屏幕快照 2017-06-23 下午9.44.37.png
    - (void)testMemoryLeakCase3
    {
        /**
         这里可能不太容易看出来,访问 name 实例变量相当于 self->name
         这样 self 持有 finshBlock, finshBlock 持有 self,形成闭环,造成循环引用
         */
        
        __weak SecondViewController *wSelf = self;
        self.finshBlock = ^(BOOL isFinish) {
            /*
             Dereferencing a __weak pointer is not allowed due to possible null value caused by race condition, assign it to strong variable first
             */
            // 发现这样写不行,还报错,它的意思是 __weak 指针可能为空,必须要强引用
            // wSelf->name = @"Hello lefe";
            
            /**
             那么为什么在 testMemoryLeakCase1 中 wSelf.logId = @"Hello logId"; 没有编译错误呢?我想
             估计 wSelf.logId 等价于 [wSelf logId],相当于调用了一个方法,
             nil 调用方法是没有错误的。你知道属性和实例变量的区别吗?
             
             下面这行代码也会报错的:
             __weak AsyncTask *task;
             task->_sex;
             
             */
            wSelf.logId = @"Hello logId";
            
            __strong SecondViewController *strongSelf = wSelf;
            strongSelf->_name = @"Hello lefe";
        };
        
        /**
         修改前的代码:
         self.finshBlock = ^(BOOL isFinish) {
            name = @"Hello lefe";
         };
         */
    }
    ```
    
    ===== 我是有底线的 ======
    [喜欢我的文章,欢迎关注我的新浪微博 Lefe_x,我会不定期的分享一些开发技巧](http://www.weibo.com/5953150140/profile?rightmod=1&wvr=6&mod=personnumber&is_all=1)
    ![](https://img.haomeiwen.com/i1664496/e409f16579811101.jpg)

    相关文章

      网友评论

          本文标题:Block 使用总结

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