美文网首页
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