美文网首页
Block(一)

Block(一)

作者: 紫荆秋雪_文 | 来源:发表于2018-07-06 01:54 被阅读18次

    一、Block本质

    Block本质上是一个OC对象,内部也有个isa指针

    二、Block的底层源码

    • 1、测试代码
    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //定义一个无参无返回值的block revanBlock
        void(^revanBlock)(void) = ^{
            NSLog(@"定义一个block");
        };
        
        //调用block
        revanBlock();
    }
    @end
    打印输出:
     定义一个block
    
    • 2、进入ViewController.m文件的目录下载命令行中执行一下命令转成C++代码
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc ViewController.m -o ViewController.cpp
    
    • 3、ViewController.cpp¡ Block底层源码.png
    • 4、经过简化后的代码
    • 4.1、定义revanBlock
    //定义revanBlock
        void(*revanBlock)(void) =
        &__ViewController__viewDidLoad_block_impl_0(
            __ViewController__viewDidLoad_block_func_0,
            &__ViewController__viewDidLoad_block_desc_0_DATA
      );
    
    • 4.2、调用block
    //调用revanBlock
    (revanBlock->FuncPtr)(revanBlock);
    
    • 4.3、__ViewController__viewDidLoad_block_impl_0结构体
    /**
        1、__ViewController__viewDidLoad_block_impl_0:是一个结构体
        2、C++中的结构体是可以存放函数
        3、第一个元素:impl是一个struct __block_impl类型的结构体
         struct __block_impl {
             void *isa;//OC本质isa执行class对象
             int Flags;
             int Reserved;//保留字段
             void *FuncPtr;//block代码地址
         };
        4、第二个元素:Desc是一个struct __ViewController__viewDidLoad_block_desc_0类型结构体指针
         static struct __ViewController__viewDidLoad_block_desc_0 {
             size_t reserved;//保留字段
             size_t Block_size;//revanBlock的占用内存大小
         }
        5、C++中的构造函数(类似OC初始化方法)
            5.1、第一个参数fp指针
            5.2、第二个参数desc指针
            5.3、第三个参数flags默认值为0
            5.4、给结构体赋值
                 impl.isa = &_NSConcreteStackBlock;
                 impl.Flags = flags;
                 impl.FuncPtr = fp;
                 Desc = desc;
     */
    struct __ViewController__viewDidLoad_block_impl_0 {
      struct __block_impl impl;
      struct __ViewController__viewDidLoad_block_desc_0* Desc;
        
      //C++中的构造函数(类似OC初始化方法)
      __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    • 4.4、定义revanBlock时传入的第一个参数__ViewController__viewDidLoad_block_func_0是一个函数,所以从上面的分析可以知道,在定义block的时候把__ViewController__viewDidLoad_block_func_0这个函数当成参数fp指针传给了impl中的FuncPtr
    static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_ym_kg_kmbfn17v86dqrzyv26ww80000gn_T_ViewController_54ed6a_mi_0);
        }
    
    • 4.5、定义revanBlock时传入的第二个参数__ViewController__viewDidLoad_block_desc_0_DATA是一个函数
    static struct __ViewController__viewDidLoad_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};
    
    • 5、调用revanBlock
    //调用revanBlock
        (revanBlock->FuncPtr)(revanBlock);
    
    • 由定义revanBlock可知,revanBlock对象存储这__ViewController__viewDidLoad_block_impl_0的地址,又由于__ViewController__viewDidLoad_block_impl_0结构体中第一位元素是struct __block_impl类型的结构体impl,并且通过构造函数把传进来的fp指针赋值给了impl元素中的元素FuncPtr。
    • 小结:block是封装了函数调用以及函数调用环境的OC对象 block底层结构.png

    三、Block变量捕获

    1、局部变量

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    void(^revanBlock)(void);
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 1、定义一个局部变量
        int var = 10;
        
        // 2、定义一个无参无返回值的block revanBlock
        revanBlock = ^{
            NSLog(@"block中使用var=%d", var);
        };
        
        // 3、重新给var赋值
        var = 22;
        
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        // 调用block
        revanBlock();
    }
    
    @end
    打印输出
    2018-07-06 00:46:45.153676+0800 01-Block本质[7329:464430] block中使用var=10
    
    • 为什么输出的age不是22?
    • 1、此时revanBlock的底层源码
    struct __ViewController__viewDidLoad_block_impl_0 {
      struct __block_impl impl;
      struct __ViewController__viewDidLoad_block_desc_0* Desc;
      //多了一个var变量
      int var;
        //C++构造函数多了一个_var参数
      __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int _var, int flags=0) : var(_var) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    • 发现在这里捕获了block中使用的外界局部变量

    2、使用static修饰的局部变量

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    void(^revanBlock)(void);
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 1、定义一个局部变量
        static int var = 10;
        
        // 2、定义一个无参无返回值的block revanBlock
        revanBlock = ^{
            NSLog(@"block中使用var=%d", var);
        };
        
        // 3、重新给var赋值
        var = 20;
        
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        // 调用block
        revanBlock();
    }
    
    @end
    打印输出:
    2018-07-06 01:00:10.983673+0800 01-Block本质[7514:473562] block中使用var=20
    
    • 这次问什么输出的是20?
    struct __ViewController__viewDidLoad_block_impl_0 {
      struct __block_impl impl;
      struct __ViewController__viewDidLoad_block_desc_0* Desc;
        //var现在是一个int类型的指针
      int *var;
      __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int *_var, int flags=0) : var(_var) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    • 即使是使用static修饰的局部变量依然会被block捕获

    3、全局变量

    
    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    void(^revanBlock)(void);
    // 1、定义一个全局变量
    int var = 10;
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 2、定义一个无参无返回值的block revanBlock
        revanBlock = ^{
            NSLog(@"block中使用var=%d", var);
        };
        
        // 3、重新给var赋值
        var = 20;
        
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        // 调用block
        revanBlock();
    }
    
    @end
    打印输出:
    2018-07-06 01:05:36.830416+0800 01-Block本质[7579:478837] block中使用var=20
    
    • block没有捕获全局变量
    struct __ViewController__viewDidLoad_block_impl_0 {
      struct __block_impl impl;
      struct __ViewController__viewDidLoad_block_desc_0* Desc;
      __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    • 小结
      • 如果block中使用局部变量,局部变量一定会被block捕获
      • 如果block中使用全局变量,不会被block捕获 block的变量捕获.png

    四、MRC下的Block的类型

    1、block中不使用任何变量,Block为NSGlobalBlock类型

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    void(^revanBlock)(void);
    
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        revanBlock = ^{
            NSLog(@"block中输出");
        };
        
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        // 调用block
        revanBlock();
        NSLog(@"%@", [revanBlock class]);
        [revanBlock release];
    }
    
    @end
    打印输出:
    2018-07-06 01:19:07.472305+0800 01-Block本质[7791:489628] block中输出
    2018-07-06 01:19:07.472491+0800 01-Block本质[7791:489628] __NSGlobalBlock__
    

    2、block中使用局部变量,block是NSStackBlock类型

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    void(^revanBlock)(void);
    
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // 1、定义一个局部变量
        int var = 10;
        // 2、定义一个无参无返回值的block revanBlock
        revanBlock = ^{
            NSLog(@"block中使用var=%d", var);
        };
        
        // 3、重新给var赋值
        var = 20;
        
        revanBlock();
        NSLog(@"%@", [revanBlock class]);
        [revanBlock release];
    }
    
    @end
    打印输出:
    2018-07-06 01:29:34.738826+0800 01-Block本质[7960:498531] block中使用var=10
    2018-07-06 01:29:34.739160+0800 01-Block本质[7960:498531] __NSStackBlock__
    

    3、block中使用static修饰的局部变量,block是NSGlobalBlock类型

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    void(^revanBlock)(void);
    
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // 1、定义一个局部变量
        static int var = 10;
        // 2、定义一个无参无返回值的block revanBlock
        revanBlock = ^{
            NSLog(@"block中使用var=%d", var);
        };
        
        // 3、重新给var赋值
        var = 20;
        
        revanBlock();
        NSLog(@"%@", [revanBlock class]);
        [revanBlock release];
    }
    
    @end
    打印输出:
    2018-07-06 01:31:44.566522+0800 01-Block本质[7989:500158] block中使用var=20
    2018-07-06 01:31:44.566791+0800 01-Block本质[7989:500158] __NSGlobalBlock__
    

    4、block中使用全局变量,block是NSGlobalBlock类型

    
    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    void(^revanBlock)(void);
    
    // 1、定义一个全局变量
    static int var = 10;
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // 2、定义一个无参无返回值的block revanBlock
        revanBlock = ^{
            NSLog(@"block中使用var=%d", var);
        };
        
        // 3、重新给var赋值
        var = 20;
        
        revanBlock();
        NSLog(@"%@", [revanBlock class]);
        [revanBlock release];
    }
    
    @end
    打印输出:
    2018-07-06 01:34:44.150311+0800 01-Block本质[8041:502465] block中使用var=20
    2018-07-06 01:34:44.150554+0800 01-Block本质[8041:502465] __NSGlobalBlock__
    

    五、在ARC下的Block的类型

    1、block中不使用任何变量,block是NSGlobalBlock类型

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    void(^revanBlock)(void);
    
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        revanBlock = ^{
            NSLog(@"block中输出");
        };
        // 调用block
        revanBlock();
        NSLog(@"%@", [revanBlock class]);
    }
    @end
    打印输出:
    2018-07-06 01:39:14.251938+0800 01-Block本质[8119:505911] block中输出
    2018-07-06 01:39:14.252169+0800 01-Block本质[8119:505911] __NSGlobalBlock__
    

    2、block中使用局部变量,block是NSMallocBlock类型

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    void(^revanBlock)(void);
    
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 1、定义一个局部变量
        int var = 10;
        // 2、定义一个无参无返回值的block revanBlock
        revanBlock = ^{
            NSLog(@"block中使用var=%d", var);
        };
        
        // 3、重新给var赋值
        var = 20;
        
        revanBlock();
        NSLog(@"%@", [revanBlock class]);
    }
    
    @end
    打印输出:
    2018-07-06 01:40:46.853263+0800 01-Block本质[8147:507357] block中使用var=10
    2018-07-06 01:40:46.853484+0800 01-Block本质[8147:507357] __NSMallocBlock__
    

    3、block中使用static修饰的局部变量,block是NSGlobalBlock类型

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    void(^revanBlock)(void);
    
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 1、定义一个局部变量
        static int var = 10;
        // 2、定义一个无参无返回值的block revanBlock
        revanBlock = ^{
            NSLog(@"block中使用var=%d", var);
        };
        
        // 3、重新给var赋值
        var = 20;
        
        revanBlock();
        NSLog(@"%@", [revanBlock class]);
    }
    
    @end
    打印输出:
    2018-07-06 01:42:20.169110+0800 01-Block本质[8171:508554] block中使用var=20
    2018-07-06 01:42:20.169337+0800 01-Block本质[8171:508554] __NSGlobalBlock__
    
    

    4、block中使用全局变量,block是NSGlobalBlock类型

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    void(^revanBlock)(void);
    
    // 1、定义一个全局变量
    int var = 10;
    
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 2、定义一个无参无返回值的block revanBlock
        revanBlock = ^{
            NSLog(@"block中使用var=%d", var);
        };
        
        // 3、重新给var赋值
        var = 20;
        
        revanBlock();
        NSLog(@"%@", [revanBlock class]);
    }
    
    @end
    打印输出:
    2018-07-06 01:44:10.719455+0800 01-Block本质[8194:509956] block中使用var=20
    2018-07-06 01:44:10.719677+0800 01-Block本质[8194:509956] __NSGlobalBlock__
    
    • 小结
    • block是基础NSBlock
    • 当block中使用了局部变量(auto修饰)时,在MRC下block是NSStackBlock类型,在ARC下是NSMallockBlock类型
    • 在其他情况下都是NSGlobalBlock类型
    • 在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上
      • block作为函数返回值时
      • 将block赋值给__strong指针时
      • block作为Cocoa API中的方法名含有usingBlock的方法参数时
      • block作为GCD API的方法参数时


        block类型.png block不同的类型内存分配.png 不同类型的block执行copy操作.png

    六、Block访问对象类型

    1、block中访问局部对象类型

    • 1、RevanPerson
    #import <Foundation/Foundation.h>
    
    @interface RevanPerson : NSObject
    @property (nonatomic, assign) int age;
    
    @end
    
    #import "RevanPerson.h"
    
    @implementation RevanPerson
    
    @end
    
    • 2、测试代码
    #import "ViewController.h"
    #import "RevanPerson.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // 1、定义一个RevanPerson对象
        RevanPerson *revan_p = [[RevanPerson alloc] init];
        revan_p.age = 20;
        
        // 2、定义一个无参无返回值的block revanBlock
        void (^revanBlock)(void) = ^{
            NSLog(@"block中使用对象%@ 年龄为%d", revan_p, revan_p.age);
        };
        
        // 3、调用block
        revanBlock();
    }
    
    @end
    打印输出:
    2018-07-07 17:42:43.844470+0800 01-Block本质[5511:325252] block中使用对象<RevanPerson: 0x6000000135a0> 年龄为20
    
    • 从底层的数据结构窥探一下在block中使用对象和在block中使用基本数据类型变量有什么区别
      -1、定义block
    //定义block
        void (*revanBlock)(void) = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, revan_p, 570425344));
    
    • 2、__ViewController__viewDidLoad_block_impl_0结构体,依然会会把用到的局部对象变量捕获到结构体中
    struct __ViewController__viewDidLoad_block_impl_0 {
      struct __block_impl impl;
      struct __ViewController__viewDidLoad_block_desc_0* Desc;
      RevanPerson *__strong revan_p;
      __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, RevanPerson *__strong _revan_p, int flags=0) : revan_p(_revan_p) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    • 3、struct __ViewController__viewDidLoad_block_desc_0结构体,与基本数据类型相比,多了copy函数和dispose函数
    static struct __ViewController__viewDidLoad_block_desc_0 {
      size_t reserved;
      size_t Block_size;
        //多了一个copy函数
      void (*copy)(struct __ViewController__viewDidLoad_block_impl_0*, struct __ViewController__viewDidLoad_block_impl_0*);
        //多了一个dispose函数
      void (*dispose)(struct __ViewController__viewDidLoad_block_impl_0*);
        
    }
    
    • 4、__ViewController__viewDidLoad_block_desc_0_DATA函数
    
    static struct __ViewController__viewDidLoad_block_desc_0 {
      size_t reserved;
      size_t Block_size;
        //多了一个copy函数指针
      void (*copy)(struct __ViewController__viewDidLoad_block_impl_0*, struct __ViewController__viewDidLoad_block_impl_0*);
        //多了一个dispose函数指针
      void (*dispose)(struct __ViewController__viewDidLoad_block_impl_0*);
        
    } __ViewController__viewDidLoad_block_desc_0_DATA = {
        0,
        sizeof(struct __ViewController__viewDidLoad_block_impl_0),
        __ViewController__viewDidLoad_block_copy_0,
        __ViewController__viewDidLoad_block_dispose_0
        
    };
    把__ViewController__viewDidLoad_block_copy_0函数的地址赋值给copy
    把__ViewController__viewDidLoad_block_dispose_0函数的地址赋值给dispose
    
    • 4.1、__ViewController__viewDidLoad_block_copy_0函数中调用_Block_object_assign函数
    static void __ViewController__viewDidLoad_block_copy_0(struct __ViewController__viewDidLoad_block_impl_0*dst, struct __ViewController__viewDidLoad_block_impl_0*src) {
        //copy函数指针会调用_Block_object_assign函数
        _Block_object_assign((void*)&dst->revan_p, (void*)src->revan_p, 3/*BLOCK_FIELD_IS_OBJECT*/);
        
    }
    
    • 4.2、__ViewController__viewDidLoad_block_dispose_0函数调用_Block_object_dispose函数
    static void __ViewController__viewDidLoad_block_dispose_0(struct __ViewController__viewDidLoad_block_impl_0*src) {
        //dispose函数指针会调用_Block_object_dispose
        _Block_object_dispose((void*)src->revan_p, 3/*BLOCK_FIELD_IS_OBJECT*/);
        
    }
    
    • 小结:在block中访问对象类型变量和访问基本数据类型变量的区别
      • 相同点:block中都会捕获变量
      • 不同点:block中使用对象时,会多2个函数指针(copy,dispose)

    2、没有强指针指向的Block对象中引用对象变量

    • 1、RevanPerson
    #import <Foundation/Foundation.h>
    
    @interface RevanPerson : NSObject
    
    @end
    
    #import "RevanPerson.h"
    
    @implementation RevanPerson
    
    - (void)dealloc {
        NSLog(@"RevanPerson -dealloc");
    }
    
    @end
    
    • 2、测试代码
    #import "ViewController.h"
    #import "RevanPerson.h"
    
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        {
            // 定义一个RevanPerson对象
            RevanPerson *revan_p = [[RevanPerson alloc] init];
            //使用block
            ^{
                NSLog(@"没有强指针的block对象中使用revan_p对象:%@", revan_p);
            }();
            
            NSLog(@"%@", [^{
                NSLog(@"没有强指针的block对象中使用revan_p对象:%@", revan_p);
            } class]);
        }
        NSLog(@"--- RevanPerson对象作用域之外 ---");
    }
    
    @end
    打印输出:2018-07-07 19:32:37.465583+0800 01-Block本质[7527:406694] 没有强指针指向的block对象中使用revan_p对象:<RevanPerson: 0x60000000a630>
    2018-07-07 19:32:37.465839+0800 01-Block本质[7527:406694] __NSStackBlock__
    2018-07-07 19:32:37.465989+0800 01-Block本质[7527:406694] RevanPerson -dealloc
    2018-07-07 19:32:37.466193+0800 01-Block本质[7527:406694] --- RevanPerson对象作用域之外 ---
    
    • 小结:没有强指针指向的Block对象,并且在block中使用局部对象变量时,可以知道这个Block是一个NSStackBlock类型的,存储在栈区。所以当代码执行过作用域之外首先Block的栈内存会被系统回收,同时revan_p对象也没有强指针引用,所以revan_p对象也会被释放。
    • 源码可知revan_p对象依然会被这个NSStackBlock类型的block捕获
    struct __ViewController__viewDidLoad_block_impl_0 {
      struct __block_impl impl;
      struct __ViewController__viewDidLoad_block_desc_0* Desc;
      RevanPerson *__strong revan_p;
      __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, RevanPerson *__strong _revan_p, int flags=0) : revan_p(_revan_p) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    

    3、有强指针指向的Block对象中引用对象变量

    • 1、RevanPerson
    #import <Foundation/Foundation.h>
    
    @interface RevanPerson : NSObject
    
    @end
    #import "RevanPerson.h"
    
    @implementation RevanPerson
    
    - (void)dealloc {
        NSLog(@"RevanPerson -dealloc");
    }
    
    @end
    
    • 测试代码
    #import "ViewController.h"
    #import "RevanPerson.h"
    
    typedef void(^RevanBlock)(void);
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        RevanBlock revan_block;
        {
            // 定义一个RevanPerson对象
            RevanPerson *revan_p = [[RevanPerson alloc] init];
            //使用block
            revan_block = ^{
                NSLog(@"有强指针指向的block对象中使用revan_p对象:%@", revan_p);
            };
            
            NSLog(@"%@", [revan_block class]);
        }
        NSLog(@"--- RevanPerson对象作用域之外 ---");
        revan_block();
    }
    
    @end
    打印输出:
    2018-07-07 19:46:07.460595+0800 01-Block本质[7749:417687] __NSMallocBlock__
    2018-07-07 19:46:07.460891+0800 01-Block本质[7749:417687] --- RevanPerson对象作用域之外 ---
    2018-07-07 19:46:07.461208+0800 01-Block本质[7749:417687] 有强指针指向的block对象中使用revan_p对象:<RevanPerson: 0x60400001ab30>
    2018-07-07 19:46:07.461326+0800 01-Block本质[7749:417687] RevanPerson -dealloc
    
    • 小结:revan_block是NSMallocBlock类型的Block,存储在堆中。从这次的打印输出可以看出RevanPerson在出作用并没有立即释放,而是等到 revan_block()调用之后, revan_block销毁了之后,RevanPerson对象随后销毁。
    • 源码分析
    struct __ViewController__viewDidLoad_block_impl_0 {
      struct __block_impl impl;
      struct __ViewController__viewDidLoad_block_desc_0* Desc;
      RevanPerson *__strong revan_p;
      __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, RevanPerson *__strong _revan_p, int flags=0) : revan_p(_revan_p) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    

    4、有强指针指向的Block对象中引用使用__week修饰的对象变量

    • 1、RevanPerson
    #import <Foundation/Foundation.h>
    
    @interface RevanPerson : NSObject
    
    @end
    #import "RevanPerson.h"
    
    @implementation RevanPerson
    
    - (void)dealloc {
        NSLog(@"RevanPerson -dealloc");
    }
    
    @end
    
    • 2、测试代码
    #import "ViewController.h"
    #import "RevanPerson.h"
    
    typedef void(^RevanBlock)(void);
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        RevanBlock revan_block;
        {
            // 定义一个RevanPerson对象
            RevanPerson *revan_p = [[RevanPerson alloc] init];
            __weak RevanPerson *weakRevan_p = revan_p;
            //使用block
            revan_block = ^{
                NSLog(@"有强指针指向的block对象中使用weakRevan_p对象:%@", weakRevan_p);
            };
            
            NSLog(@"%@", [revan_block class]);
        }
        NSLog(@"--- RevanPerson对象作用域之外 ---");
        revan_block();
    }
    
    @end
    打印输出:
    2018-07-07 19:52:19.223148+0800 01-Block本质[7853:421860] __NSMallocBlock__
    2018-07-07 19:52:19.223364+0800 01-Block本质[7853:421860] RevanPerson -dealloc
    2018-07-07 19:52:19.223496+0800 01-Block本质[7853:421860] --- RevanPerson对象作用域之外 ---
    2018-07-07 19:52:19.223626+0800 01-Block本质[7853:421860] 有强指针指向的block对象中使用weakRevan_p对象:(null)
    
    • 小结:revan_block这个Block依然是NSMallocBlock类型的Block,但是发现使用__weak修饰的对象weakRevan_p在作用域一过就立即释放。
    • 源码分析
    struct __ViewController__viewDidLoad_block_impl_0 {
      struct __block_impl impl;
      struct __ViewController__viewDidLoad_block_desc_0* Desc;
      RevanPerson *__weak weakRevan_p;
      __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, RevanPerson *__weak _weakRevan_p, int flags=0) : weakRevan_p(_weakRevan_p) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    

    5、小结

    • 当block的存储在栈上
      • block不会对内部使用的对象变量产生强引用
    • 当block的内存在堆上
      • block在堆区时,在block内部使用对象变量时,会调用block内部的copy函数,copy函数会调用_Block_object_assign函数,_Block_object_assign函数会根据捕获的对象的修饰符类型做出相应的操作,最终决定block是对对象强引用还是若引用
      • block销毁时,会调用block内部的dispose函数,根据dispose函数内部的_Block_object_dispose函数会自动释放引用的对象变量

    相关文章

      网友评论

          本文标题:Block(一)

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