聊聊block

block本质

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

如下例子 脑海中要有此图。

一个block
底层实现

block 就是一个对象, 有一个isa指针, funcptr 存放代码地址, 有外部局部变量
补充内存分布知识:
全局变量 存放数据段,
局部变量 栈区间
实例对象 堆区间
类对象 数据段 >

block类型

block 内存其实在栈上的, 但我们常常需要对其持有长时间,用copy修饰。 从栈区间拷贝到堆区间。栈区间自动释放后, 不会影响堆区间。
在ARC模式下 编译器会自动将栈上的block内存copy到堆区间上.
1. block作为函数参数返回。
2. 被 strong指针修饰。
3 . GCD 参数
4. 作为Cocoa API 中方法名有usingBlock方法参数时

block 类型

  • 栈空间上的block 不能持有外部的对象。不能保住对象的命
    堆空间上的block 可以引用外部对象。 retain计数+1,release -1 可以 保住对象的命

  • 如果block 在栈上, block内部不会对外部的auto变量强引用

  • 在堆上, block内部自动进行copy工作 从底层结构体 main_block_desc_0 内部就可以看出 会自动调用一个 main_block_copy 调用一个 block_object_assgin会根据atuo变量的修饰符(__strong __weak ,_unsafe_unretained)做出操作 类似于retain(形成强引用, 若引用)。

  • 如果block从堆中移除, 会调用dispose函数
    dispose函数内部会调用_block_object_dispose函数, 会自动释放引用的auto变量, 类似于release.

为什么我们常用weak修饰block呢 也就是说。 当对象想死的时候 。 block不会强行占有对象。 对象可以死去。哈哈哈

底层结构图

  • oc 转C++命令
    clang -x objective-c -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m

** 为什么在block内部不能修改局部外部变量呢?
内部结构体 属性age 和block 在两个函数里。 在block函数里访问不到外部局部变量

int main(int argc, const char * argv[]) {
    @autoreleasepool {
      
        int age = 10;
        tsBlock block = ^{
            
            //age = 20;
            NSLog(@"age is %d", age);
        };
        
        block();
        
    }
    return 0;
}
外部局部变量无法修改
  • 1.static 修饰局部变量(一般不采用.内存无法释放) 可以在如下图片中看到 age前边加了指针 就可以访问了


    staic 修饰后
__block  int age = 10;
        tsBlock block = ^{
            
            age = 20;
            NSLog(@"age is %d", age);
        };
        
        block();
        
    }
  • 2.__block 修饰局部变量. 可以解决无法修改局部变量问题
    看内部c++ 代码. 多了一个包装结构体block
    block内部有一个指针 指向结构体, 通过指针找到那个结构体内存,
    把结构体里的变量改掉.


    __block修饰后
__block的 _forwarding指针

3.block 修饰对象.

int main(int argc, const char * argv[]) {
    @autoreleasepool {
       
      __block  MJPerson *p = [[MJPerson alloc] init];
       
        tsBlock block = ^{
            
            NSLog(@"%@", p);
        };
        
        block();
        
    }
    return 0;
}
修饰对象
block

block

循环引用问题

int main(int argc, const char * argv[]) {
 @autoreleasepool {
    
     MJPerson *p = [[MJPerson alloc] init];
    
     p.block = ^{
        
         NSLog(@"%@", p.name);
         
     };
     
     NSLog(@"111111");
     
 }
 return 0;
}
[循环引用)]
循环引用

block内部结构体生成一个强引用的指针 指向类对象, 对象里边有个变量block 会访问 block. 也就是有个强引用指针.
本质:两个强引用, 你引用我. 我引用你. 导致无法释放

解决循环引用问题
      不会产生强引用, 不安全, 指向的对象销毁, 指针存储的地址值不变.
           1.   __unsafe_unretained  MJPerson *p = [[MJPerson alloc] init];
               不会产生强引用.指向对象销毁时候,会自动让置为 nil
           2.  __weak MJPerson *weakPerson = p;
                p.block = ^{
        
                    NSLog(@"%@", p.name);
        
                };
        
                NSLog(@"111111");

3.说说第三种用block解决方式 对象 - block - block变量. block变量里边有一个指针指向对象. 形成三者紧密联系. 当我们把block变量里边的这个对象指针置位nil. 就解决了引用. 但不是最好的方式.

解决循环引用
MRC 解决block
block 本质是什么

-封装了函数调用以及调用环境的oc对象

__block修饰符 干了什么
  • __block可以用于解决block内部无法修改auto变量值得问题
    不能修饰全局变量,静态变量(static)
    -编辑器会将__block变量包装成一个对象
block属性修饰符词为什么是copy,使用block哪些注意点
  • copy 把内存拷贝到堆上.
block在修改NSmutablearray用不用加上 __block
  • 不用. 加上后.反而内部结构变复杂.

你可能感兴趣的:(聊聊block)