美文网首页底层原理
block 底层原理分析(一)

block 底层原理分析(一)

作者: 晨曦的简书 | 来源:发表于2021-08-29 18:31 被阅读0次

    block 的类型

    在我们的日常开发过程中相信大家都会用到 block,但是 block 有哪些类型,你又是否知道呢?下面我们来看一下 block 的类型区分。

    block 三种类型

    通过代码演示,我们可以看出,输入了三种类型的 block,下面我们来介绍下这三种 block 类型:

    • GlobalBlock

      • 位于全局区
      • block 内部不使用外部变量,或者只使用静态变量和全局变量
    • MallocBlock

      • 位于堆区
      • block 内部使用变量或者 oc 属性,并且赋值给强引用或者 copy 修饰的变量
    • StackBlock

      • 位于栈区
      • MallocBlock 一样,可以在内部使用局部变量或者 oc 属性,但不能赋值给强引用或者 copy 修饰的变量

    block 拷贝到堆区的条件

    • 手动 copy
    • block 作为返回值
    • 被强引用或者 copy 修饰
    • 系统 api 包含 usingBlock

    相关案例

    • 案例一


    这里我们自定义了一个 _CXBlock 类型的结构体,我们对 weakBlock 进行强转并赋值给 blc,这样我们就可以对 block 内的数据结构进行修改,因为 weakBlock, strongBlock, strongBlock1 它们指向的是同一块内存区间,所有当我们把 blcinvoke 属性设置为 nil 后,strongBlock1() 的执行会报错,找不到函数执行。但是当我们把 id __strong strongBlock = weakBlock 改为 id __strong strongBlock = [weakBlock copy] 之后就可以解决这个问题,这是因为把栈 block 拷贝之后 blc 就为堆 block

    • 案例二


    因为 block 使用外部变量的时候会进行捕获,所以就会对引用计数加 1,第一次打印 3 是因为栈区 block 跟堆区 block 各引用了一次,打印 4 是因为这是一个栈区 block,所以引用计数加 1,打印 5 是因为 weakBlock 拷贝之后变为了堆区 block,又会对引用计数加 1。

    • 案例三


    这是一个关于 block 释放时机的问题,strongBlock 是栈区 block,所以 strongBlock 的作用域是中间的大括号结束,所以 weakBlock = strongBlock 可以赋值成功,这时候 weakBlock 也是栈区 block,所以它的作用域在最外层大括号结束的时候,所以 weakBlock() 可以执行成功。如果 strongBlock 是堆 blockweakBlock() 就不会执行。

    block 循环引用

    对象的释放条件

    循环引用条件

    循环引用案例

        // 循环引用
        self.name = @"chenxi";
        self.block = ^{
            NSLog(@"%@",self.name);
        };
    

    类似这样一段代码,这种情况下就会出现循环引用,我们可以用 weakSelf 的方式来解决这个问题,代码如下:

    • 解决循环引用方法一
        self.name = @"chenxi";
        __weak __typeof(self)weakSelf = self;
        self.block = ^{
            NSLog(@"%@",weakSelf.name);
        };
    

    虽然 weakSelf 的方式确实能解决循环引用问题,但是这种方式是不完善的,可以看如下案例:

    类似这种,我们在打印的时候加个延时,这时候有种场景,当我们页面返回过快的时候,还没来得及打印,name 就已经释放了,所以打印为 null。这时候解决这种问题我们需要用到 strongSelf,如下:

        self.name = @"xhenxi";
        __weak __typeof(self)weakSelf = self;
        self.block = ^{
            __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();
    

    这里 strongSelf 只是一个临时变量,延长了 weakSelf 的释放时间,但是 strongSelf 作用域只是在 block 函数里面,block 函数执行完 strongSelf 就会释放。

    • 解决循环引用方法二
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.name = @"xhenxi";
        __block ViewController *vc = self;
        self.block = ^{
            NSLog(@"%@",vc.name);
            vc = nil;
        };
        self.block();
    }
    

    这里用了一个临时变量 vc 接收 self,在打印完毕后把 vc 置为 nil

    • 解决循环引用方法三
    typedef void(^CXBlock)(ViewController *vc);
    @interface ViewController ()
    @property (nonatomic, copy) CXBlock block;
    @property (nonatomic, copy) NSString *name;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.name = @"xhenxi";
        self.block = ^(ViewController *vc) {
            NSLog(@"%@",vc.name);
        };
        self.block(self);
    }
    

    这里是把 self 作为一个参数传递给 block

    循环引用相关面试题

    • 试题一
    static ViewController *staticSelf_;
    
    - (void)blockWeak_static {
        __weak typeof(self) weakSelf = self;
        staticSelf_ = weakSelf;
    }
    
    - (void)dealloc {
        NSLog(@"dealloc 调用");
    }
    

    类似这样一段代码,我们把 weakSelf 赋值给了一个全局静态变量 staticSelf_,运行指挥,dealloc 方法并没有走,会导致不释放。这是因为 weakSelf 与 self 指向的是同一片内存空间,所以把 weakSelf 赋值给 staticSelf_ 之后会导致内存不释放。

    • 试题二
    typedef void(^CXBlock)(void);
    @interface ViewController ()
    @property (nonatomic, copy) CXBlock block;
    @property (nonatomic, copy) CXBlock doWork;
    @property (nonatomic, copy) CXBlock doStudent;
    @end
    
    - (void)block_weak_strong {
        __weak typeof(self) weakSelf = self;
        self.doWork = ^{
            __strong typeof(self) strongSelf = weakSelf;
            weakSelf.doStudent = ^{
                NSLog(@"%@", strongSelf);
            };
           weakSelf.doStudent();
        };
       self.doWork();
    }
    
    - (void)dealloc{
        NSLog(@"dealloc 调用");
    }
    

    这里也会出现循环引用问题,因为 doStudent 会对 strongSelf 进行捕获,所以 strongSelf 引用计数会加 1,所以出了 doWork 函数之后, strongSelf 会释放不掉。

    相关文章

      网友评论

        本文标题:block 底层原理分析(一)

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