一、__block修饰基本数据类型的局部变量
1、被__block修饰的局部变量可以在Block内部修改,__block不能修饰全局变量、静态变量(static)
- __block修饰的变量,可以在Block内部被修改
#import "ViewController.h"
#import "RevanPerson.h"
typedef void(^RevanBlock)(void);
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__block int autoA = 10;
RevanBlock revan_b = ^{
autoA = 20;
NSLog(@"局部变量autoA=%d", autoA);
};
revan_b();
}
@end
打印输出:
2018-07-07 23:53:14.791546+0800 01-Block本质[11691:578111] 局部变量autoA=20
- 源码分析
- 1、__ViewController__viewDidLoad_block_impl_0结构体,发现被__block修饰的基本数据类型autoA现在是一个__Block_byref_autoA_0类型的指针
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
//没有__block修饰时 int autoA
__Block_byref_autoA_0 *autoA; // by ref
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, __Block_byref_autoA_0 *_autoA, int flags=0) : autoA(_autoA->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
- 2、__Block_byref_autoA_0结构体如下
//结构体
struct __Block_byref_autoA_0 {
void *__isa;//NSObject对象特有
__Block_byref_autoA_0 *__forwarding;//这个__forwarding指向了__Block_byref_autoA_0类型的地址
int __flags;
int __size;
int autoA;//真正的autoA
};
- 3、__block int autoA = 10代码底层源码如下
__attribute__((__blocks__(byref))) __Block_byref_autoA_0 autoA = {
(void*)0,
(__Block_byref_autoA_0 *)&autoA,
0,
sizeof(__Block_byref_autoA_0),
10
};
RevanBlock revan_b =
((void (*)())&__ViewController__viewDidLoad_block_impl_0(
(void *)__ViewController__viewDidLoad_block_func_0,//Block的回调方法
&__ViewController__viewDidLoad_block_desc_0_DATA,//Block一些信息
(__Block_byref_autoA_0 *)&autoA,//__block修饰符对象
570425344)
);
- 5、__ViewController__viewDidLoad_block_func_0函数,Block回调代码块
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
//Block通过autoA来找到__block对象
__Block_byref_autoA_0 *autoA = __cself->autoA; // bound by ref
//__block对象再通过__forwarding找到__block对象具体地址(有可能从栈区复制到堆区),在找到autoA变量直接重新赋值
(autoA->__forwarding->autoA) = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ym_kg_kmbfn17v86dqrzyv26ww80000gn_T_ViewController_2e1ccd_mi_0, (autoA->__forwarding->autoA));
}
- 6、__ViewController__viewDidLoad_block_desc_0_DATA的结构体类型
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __ViewController__viewDidLoad_block_impl_0*, struct __ViewController__viewDidLoad_block_impl_0*);
void (*dispose)(struct __ViewController__viewDidLoad_block_impl_0*);
}
- __ViewController__viewDidLoad_block_desc_0_DATA函数内部实现
__ViewController__viewDidLoad_block_desc_0_DATA = {
0,
sizeof(struct __ViewController__viewDidLoad_block_impl_0),
//当Block从栈区copy到堆区时,自动会触发__ViewController__viewDidLoad_block_copy_0函数
__ViewController__viewDidLoad_block_copy_0,
//当Block销毁时,自动触发__ViewController__viewDidLoad_block_dispose_0函数
__ViewController__viewDidLoad_block_dispose_0
};
- 当Block从栈区copy到堆区时,自动触发__ViewController__viewDidLoad_block_copy_0函数
static void __ViewController__viewDidLoad_block_copy_0(struct __ViewController__viewDidLoad_block_impl_0*dst, struct __ViewController__viewDidLoad_block_impl_0*src) {
//调用_Block_object_assign函数,使Block内部强引用__block对象(__Block_byref_autoA_0)
_Block_object_assign((void*)&dst->autoA, (void*)src->autoA, 8/*BLOCK_FIELD_IS_BYREF*/);
}
- 当Block销毁时,会触发__ViewController__viewDidLoad_block_dispose_0函数,释放对__block对象的强引用
static void __ViewController__viewDidLoad_block_dispose_0(struct __ViewController__viewDidLoad_block_impl_0*src) {
//当Block销毁时,Block对__block对象(__Block_byref_autoA_0)的强引用要release
_Block_object_dispose((void*)src->autoA, 8/*BLOCK_FIELD_IS_BYREF*/);
}
- 小结:
-
1、使用了__block修饰的基本类型变量,在Block内部使用时,Block会把这个基本类型变量当成一个对象来捕获到结构体中,这个对象中的成员有一个是这个基本数据类型变量
__block底层数据结构.png
- 2、当block在栈上时,并不会对__block变量产生强引用
- 3、当block被copy到堆时
- 3.1、会调用block内部的copy函数
- 3.2、copy函数内部会调用_Block_object_assign函数
-
3.3、_Block_object_assign函数会对__block变量形成强引用
__block修饰的基本数据类型内存管理一.png
__block修饰的基本数据类型内存管理二.png
__block修饰的基本数据类型内存管理__forwarding.png
二、__block修饰对象类型的局部变量
#import <Foundation/Foundation.h>
@interface RevanPerson : NSObject
@end
#import "RevanPerson.h"
@implementation RevanPerson
- (void)dealloc {
NSLog(@"%s", __func__);
}
@end
#import "ViewController.h"
#import "RevanPerson.h"
typedef void(^RevanBlock)(void);
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__block RevanPerson *person = [[RevanPerson alloc] init];
RevanBlock revan_b = ^{
NSLog(@"局部变量autoA=%@", person);
};
revan_b();
NSLog(@"__block修饰对象");
}
@end
打印输出
2018-07-08 01:18:41.131610+0800 01-Block本质[13071:639577] 局部变量autoA=<RevanPerson: 0x600000017a60>
2018-07-08 01:18:41.131807+0800 01-Block本质[13071:639577] __block修饰对象
2018-07-08 01:18:41.131922+0800 01-Block本质[13071:639577] -[RevanPerson dealloc]
- 源码分析
- 1、__ViewController__viewDidLoad_block_impl_0结构体
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
//__block对象
__Block_byref_person_0 *person; // by ref
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, __Block_byref_person_0 *_person, int flags=0) : person(_person->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
- 2、__block对象__Block_byref_person_0结构体
struct __Block_byref_person_0 {
void *__isa;
__Block_byref_person_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
//在__block对象中有关于捕获的对象修饰类型
RevanPerson *__strong person;
};
当使用__block 和 __weak一起来修饰对象
#import "ViewController.h"
#import "RevanPerson.h"
typedef void(^RevanBlock)(void);
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__block __weak RevanPerson *person = [[RevanPerson alloc] init];
RevanBlock revan_b = ^{
NSLog(@"局部变量autoA=%@", person);
};
revan_b();
NSLog(@"__block修饰对象");
}
@end
- 源码分析
- 1、__ViewController__viewDidLoad_block_impl_0结构体
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
//__block对象
__Block_byref_person_0 *person; // by ref
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, __Block_byref_person_0 *_person, int flags=0) : person(_person->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
- 2、__block对象中person对象的修饰是__weak类型的
struct __Block_byref_person_0 {
void *__isa;
__Block_byref_person_0 *__forwarding;
int __flags;
int __size;
//copy用来决定__block对象对person对象是强引用还是弱引用
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
//使用__weak修饰的对象
RevanPerson *__weak person;
};
- 小结:
- 当block在栈上时
- 当block拷贝到堆上时
- 会调用__block变量内部的copy函数_Block_object_assign函数会根据所指向对象的修饰符做出相应的操作
- 如果是基本类型局部变量,__block 对象会强引用
- 如果是对象类型局部变量,会通过修饰对象有无__weak来决定__block对象对局部对象是强引用还是弱引用
- 如果__block变量从堆上移除
- 会调用__block变量内部的dispose函数
- dispose函数内部会调用_Block_object_dispose函数
-
_Block_object_dispose函数会自动释放指向的对象
__block和__weak修饰对象.png
二、Block的循环引用
1、ARC下的Block的循环引用
#import <Foundation/Foundation.h>
typedef void(^RevanBlock)(void);
@interface RevanPerson : NSObject
@property (nonatomic, copy) RevanBlock revan_b;
@end
#import "RevanPerson.h"
@implementation RevanPerson
- (void)dealloc {
NSLog(@"%s", __func__);
}
@end
#import "ViewController.h"
#import "RevanPerson.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
RevanPerson *person = [[RevanPerson alloc] init];
person.revan_b = ^{
NSLog(@"block中使用对象=%@", person);
};
person.revan_b();
NSLog(@"Block的循环引用");
}
@end
打印输出:
2018-07-08 10:24:58.030126+0800 01-Block本质[14588:705621] block中使用对象=<RevanPerson: 0x604000007f40>
2018-07-08 10:24:58.030317+0800 01-Block本质[14588:705621] Block的循环引用
- 发现程序已经运行完毕,但是RevanPerson对象并没有释放
- 源码分析
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
//block内部有一个person属性,是强引用
RevanPerson *__strong person;
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, RevanPerson *__strong _person, int flags=0) : person(_person) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
-
最开始内存图
使用Block造成循环引用.png
-
代码运行完,person销毁
使用Block循环引用.png
-
Block造成的循环引用
使用Block循环引用.png
2、解决循环引用
2.1、使用__weak修饰person对象,在Block中使用经过__weak修饰的person对象
#import <Foundation/Foundation.h>
typedef void(^RevanBlock)(void);
@interface RevanPerson : NSObject
@property (nonatomic, copy) RevanBlock revan_b;
@end
#import "RevanPerson.h"
@implementation RevanPerson
- (void)dealloc {
NSLog(@"%s", __func__);
}
@end
#import "ViewController.h"
#import "RevanPerson.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
RevanPerson *person = [[RevanPerson alloc] init];
__weak typeof(person) weakPerson = person;
person.revan_b = ^{
NSLog(@"block中使用对象=%@", weakPerson);
};
person.revan_b();
NSLog(@"Block的循环引用");
}
@end
打印输出:
2018-07-08 10:51:27.219829+0800 01-Block本质[15081:722988] block中使用对象=<RevanPerson: 0x604000206c50>
2018-07-08 10:51:27.220034+0800 01-Block本质[15081:722988] Block的循环引用
2018-07-08 10:51:27.220173+0800 01-Block本质[15081:722988] -[RevanPerson dealloc]
- 使用__weak来解决了Block的循环引用
- 源码分析
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
//Block中捕获的weakPerson对象是使用__weak修饰
RevanPerson *__weak weakPerson;
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, RevanPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
-
此时的内存图
Block中使用__weak修饰的对象.png
-
由于RevanPerson对象没有被强指针引用所以RevanPerson对象会销毁。
没有被任何强引用指向的Block.png
- 最后Block也销毁
2.2、使用__unsafe_unretained修饰person对象,在Block中使用经过__unsafe_unretained修饰的person对象
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
RevanPerson *person = [[RevanPerson alloc] init];
__unsafe_unretained typeof(person) weakPerson = person;
person.revan_b = ^{
NSLog(@"block中使用对象=%@", weakPerson);
};
person.revan_b();
NSLog(@"Block的循环引用");
}
@end
打印输出:
2018-07-08 11:15:09.196428+0800 01-Block本质[15436:739542] block中使用对象=<RevanPerson: 0x60400000d670>
2018-07-08 11:15:09.196622+0800 01-Block本质[15436:739542] Block的循环引用
2018-07-08 11:15:09.196723+0800 01-Block本质[15436:739542] -[RevanPerson dealloc]
2.3、使用__block修饰person对象,在Block中使用经过__block修饰的person对象
#import "ViewController.h"
#import "RevanPerson.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__block RevanPerson *person = [[RevanPerson alloc] init];
person.revan_b = ^{
NSLog(@"block中使用对象=%@", person);
person = nil;
};
person.revan_b();
NSLog(@"Block的循环引用");
}
@end
打印输出:
2018-07-08 11:18:03.735457+0800 01-Block本质[15505:742290] block中使用对象=<RevanPerson: 0x604000005590>
2018-07-08 11:18:03.735674+0800 01-Block本质[15505:742290] Block的循环引用
2018-07-08 11:18:03.735842+0800 01-Block本质[15505:742290] -[RevanPerson dealloc]
- 源码分析
- __ViewController__viewDidLoad_block_impl_0结构体
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
//使用__block修饰的weakPerson被包装一个__Block_byref_weakPerson_0类型的对象
__Block_byref_person_0 *person; // by ref
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, __Block_byref_person_0 *_person, int flags=0) : person(_person->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
- __block包装的对象__Block_byref_person_0结构体
struct __Block_byref_person_0 {
void *__isa;
__Block_byref_person_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
//__block构造的对象强引用person
RevanPerson *__strong person;
};
-
内存图
__block修饰对象造成循环引用.png
-
Block内部执行person = nil;后的内存图
__block修饰的对象在Block中执行person=nil.png
- 小结
- 推荐使用__weak,当释放对象后__weak会自动把曾经指向对象的指针赋值为nil
- __unsafe_unretained弊端在于释放对象后不会吧曾经指向对象的指针自动赋值为nil,这样会造成野指针访问
- __block的弊端在于要在Block回调方法中手动给对象赋值为nil,并且必须要执行这个Block,否则会存在内存泄露
3、MRC下,Block的循环引用
3.1、循环引用
#import <Foundation/Foundation.h>
typedef void(^RevanBlock)(void);
@interface RevanPerson : NSObject
@property (nonatomic, copy) RevanBlock revan_b;
@end
#import "RevanPerson.h"
@implementation RevanPerson
- (void)dealloc {
[super dealloc];
NSLog(@"%s", __func__);
}
@end
#import "ViewController.h"
#import "RevanPerson.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
RevanPerson *person = [[RevanPerson alloc] init];
person.revan_b = ^{
NSLog(@"block中使用对象=%@", person);
};
person.revan_b();
[person release];
NSLog(@"MRC Block的循环引用");
}
@end
打印输出:
2018-07-08 11:51:02.246755+0800 01-Block本质[16083:766749] block中使用对象=<RevanPerson: 0x600000206be0>
2018-07-08 11:51:02.246974+0800 01-Block本质[16083:766749] MRC Block的循环引用
3.2、解决Block循环引用
3.2.1、__unsafe_unretained修饰对象
tation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__unsafe_unretained RevanPerson *person = [[RevanPerson alloc] init];
person.revan_b = ^{
NSLog(@"block中使用对象=%@", person);
};
person.revan_b();
[person release];
NSLog(@"MRC Block的循环引用");
}
@end
打印输出:
2018-07-08 11:58:07.785725+0800 01-Block本质[16252:773066] block中使用对象=<RevanPerson: 0x60400000b8e0>
2018-07-08 11:58:07.785945+0800 01-Block本质[16252:773066] -[RevanPerson dealloc]
2018-07-08 11:58:07.786089+0800 01-Block本质[16252:773066] MRC Block的循环引用
3.2.2、__block修饰对象
#import "ViewController.h"
#import "RevanPerson.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
__block RevanPerson *person = [[RevanPerson alloc] init];
person.revan_b = ^{
NSLog(@"block中使用对象=%@", person);
};
person.revan_b();
[person release];
NSLog(@"MRC Block的循环引用");
}
@end
打印输出:
2018-07-08 12:00:22.196453+0800 01-Block本质[16323:775301] block中使用对象=<RevanPerson: 0x60000001a160>
2018-07-08 12:00:22.196673+0800 01-Block本质[16323:775301] -[RevanPerson dealloc]
2018-07-08 12:00:22.197707+0800 01-Block本质[16323:775301] MRC Block的循环引用
- 在MRC下使用__block时,对对象包装成的__Block对象不会对对象(person)产生强引用,所以不会造成循环引用。
网友评论