美文网首页
Block的理解

Block的理解

作者: hyq1101 | 来源:发表于2020-12-21 12:59 被阅读0次

一、block的本质

block本质上也是一个OC对象,它内部也有一个isa指针
block是封装了函数调用以及函数调用环境的OC对象
block内部代码会封装到_block_func_0函数中,函数地址保存在FuncPtr中
执行block内部代码时是通过FuncPtr找到函数地址进行调用

block的底层结构如下:

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

static struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
}

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    //构造函数(类似于OC的init方法),返回结构体对象
    __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, 
    int _age, int flags=0) : age(_age) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};

二、block的变量捕获机制

为了保证block内部能够正常访问外部的变量,block有个变量捕获机制


block变量捕获.jpg

1、block内部访问auto变量时会将auto变量捕获到block内部,block外部修改auto变量的值并不会影响block内部,所以是值捕获

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int age = 10;
        void (^block)(void) = ^ {
            NSLog(@"age = %d", age);  // age = 10
        };
        age = 20;
        block();
    }
    return 0;
}
// 底层C++代码
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int age;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

2、block访问static变量时会将auto变量捕获到block内部,block外部修改static变量的值会影响block内部,所以是指针捕获

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        static int age = 10;
        void (^block)(void) = ^ {
            NSLog(@"age = %d", age); // age = 20
        };
        age = 20;
        block();
    }
    return 0;
}
// 底层C++代码
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int *age;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_age, int flags=0) : age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

3、block访问全局变量时不会将全局变量捕获到block内部,而是直接访问

int age = 10;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void (^block)(void) = ^ {
            NSLog(@"age = %d", age); // age = 20
        };
        age = 20;
        block();
    }
    return 0;
}
//底层C++代码
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

三、block的类型

block类型 环境
NSGlobalBlock 没有访问auto变量
NSStackBlock 访问了auto变量
NSMallocBlock NSStackBlock调用了copy

四、block的copy

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况:
1、block作为函数的返回值时
2、将block赋值给__strong指针时
3、block作为Cocoa API中方法名含有usingBlock的方法参数时,例如

NSArray *array = @[];
[array enumerateObjectsUsingBlock:^(id  _Nonnull obj, 
NSUInteger idx, BOOL * _Nonnull stop) {
            
}];

4、block作为GCD API的方法参数时,例如:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 
(int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            
});

四、解决block的循环引用

1、用__weak、__unsafe_unretained
__weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil

__weak typeof(self)weakSelf = self;
self.block = ^ {
    NSLog(@"%p", weakSelf);
};

__unsafe_unretained:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变

__unsafe_unretained id weakSelf = self;
self.block = ^ {
    NSLog(@"%p", weakSelf);
};

2、用__block解决(必须要调用block)

__block QLPerson *person = [[QLPerson alloc] init];
person.age = 10;
person.block = ^ {
    NSLog(@"age is %d", person.age);
    person = nil;
};
person.block();

相关文章

  • Block原理探究(上篇)-Block本质及存储域问题

    主要内容:1.理解Block的本质2.理解Block的存储域分类3.理解Block的Copy原理 一、探究Bloc...

  • iOS Swift 模型数组排序(仿写 sortedArrayU

    目的:为了加深对 block 的理解 个人理解:block 的返回值 NSComparisonResult 略带有...

  • block用法大全

    block语句块 如何解决block循环引用 高逼格理解block循环引用 block相关

  • block 的理解

    block语法 ^ 返回值类型 ( 参数列表 ) {表达式}; 返回值类型可省略: block的声明:返回值类型...

  • block的理解

    block的理解参照文章http://www.cnblogs.com/flyFreeZn/p/4264220.ht...

  • block的理解

    1..block的原理是什么?本质是什么? block本质上也是一个OC对象,它内部也有个isa指针,block是...

  • block 的理解

    实质上Block也是一个对象 二、内存分配区域: OC中的各个主要的内存分配区域:包括有--栈区、堆区、常量区、全...

  • Block的理解

    一、block的本质 block本质上也是一个OC对象,它内部也有一个isa指针block是封装了函数调用以及函数...

  • 理解Block

    一、block其实是有类型的, 且一共有3种类型, 全局块, 栈块, 堆块: 1.__NSGlobalBlock_...

  • 理解Block

    堆栈的区别:经典解释 原作者不详,未详细查询,从其它地方转载并修改部分叙述,特此说明 预备知识:程序的内存分配一个...

网友评论

      本文标题:Block的理解

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