
目录
一,修改变量
二,基本数据类型的__block变量
三,__forwarding指针
四,对象类型的__block变量
五,循环引用
一,修改变量
1,在block
内部可以修改static
变量和全局变量,但不能修改auto
变量
- 实例代码
int age3 = 3;
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int age1 = 1;
static int age2 = 2;
void(^block)(void) = ^{
//age1 = 4;
age2 = 5;
age3 = 6;
NSLog(@"%d---%d---%d", age1, age2, age3);
};
block();
}
return 0;
}
// 打印
1---5---6
- 底层代码(用
clang
进行转换)
int age3 = 3;
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
auto int age1 = 1;
static int age2 = 2;
// 传递age1的值,传递age2的地址
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &age2, age1));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age1; // 保存age1的值
int *age2; // 保存age2的地址
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_age2, int _age1, int flags=0) : age2(_age2), age1(_age1) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
// 获取age1的值
int age1 = __cself->age1; // bound by copy
// 获取age2的地址
int *age2 = __cself->age2; // bound by copy
// 修改age2
(*age2) = 5;
// 修改age3
age3 = 6;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_0d_7m56tzw16qs_vsxqtxhnsq2m1myhmv_T_main_c767a8_mi_0, age1, (*age2), age3);
}
- 分析
auto
变量:age1是定义在main
函数里面的,block
只捕获了age1的值,在__main_block_func_0
函数中可以通过block
获取age1的值,但无法拿到age1,所以不能修改它
static
变量:age2虽然也定义在main
函数里面,但block
捕获了age2的地址,在__main_block_func_0
函数中可以通过block
获取age2的地址,所以可以修改它
全局变量:age3是定义在所有函数之外的,在任何函数中都可以修改它
2,在block
内部可以修改__block
修饰的auto
变量
- 实例代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int age = 1;
void(^block)(void) = ^{
age = 2;
NSLog(@"%d", age);
};
block();
}
return 0;
}
// 打印
2
- 底层代码
struct __Block_byref_age_0 {
void *__isa; // OC对象的标识
__Block_byref_age_0 *__forwarding; // 指向自身
int __flags;
int __size;
int age; // 存储变量值
};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
// 将age包装成对象
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {
(void*)0,
(__Block_byref_age_0 *)&age,
0,
sizeof(__Block_byref_age_0),
1
};
// 传递对象的地址
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // 保存对象的地址
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
// 获取对象的地址
__Block_byref_age_0 *age = __cself->age; // bound by ref
// 修改对象中存储的变量值
(age->__forwarding->age) = 2;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_0d_7m56tzw16qs_vsxqtxhnsq2m1myhmv_T_main_5332f7_mi_0, (age->__forwarding->age));
}
- 分析
1>系统会将
__block
修饰的auto
变量包装成对象,并将变量值存储在该对象中
2>block
会捕获该对象的地址
3>在__main_block_func_0
函数中先通过block
获取该对象的地址,然后再修改该对象中存储的变量值
- 注意点
1>__block
不能修饰static
变量和全局变量
2>不需要__block
修饰的情况
// 只是使用,并没有修改
NSMutableArray *array = [NSMutableArray new];
void(^block)(void) = ^{
[array addObject:@"111"];
};
Person *person = [Person new];
void(^block)(void) = ^{
person.name = @"111";
};
二,基本数据类型的__block变量
1,底层代码
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
// 指向__main_block_copy_0
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
// 指向__main_block_dispose_0
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
// 强引用__block变量
_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
// 释放__block变量
_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
}
2,分析
系统会将__block
修饰的auto
变量包装成对象,所以在__main_block_desc_0
函数中会多两个指针,在底层代码中会多两个函数,用来管理该对象的内存
- 当
block
复制到堆上时
1>同时将
__block
变量也复制到堆上
2>通过copy
指针调用__main_block_copy_0
函数,此函数会对__block
变量产生强引用


- 当
block
从堆上移除时
1>通过
dispose
指针调用__main_block_dispose_0
函数,此函数会释放引用的__block
变量
2>因为没有持有者,__block
变量也会从堆上移除


⚠️注意⚠️:当block
在栈上时,对__block
变量不会产生强引用
3,__block
变量与对象类型auto
变量的比较
- 相同点
1>当
block
在栈上时,对它们都不会产生强引用
2>当block
复制到堆上时,都会通过copy
指针来引用它们
3>当block
从堆上移除时,都会通过dispose
指针来释放它们
- 不同点
1>
__block
变量传值是8(BLOCK_FIELD_IS_BYREF
);auto
变量传值是3(BLOCK_FIELD_IS_OBJECT
)
2>block
对__block
变量产生的一定是强引用;block
对auto
变量产生的可以是强引用,也可以是弱引用
- 实例代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int age = 1;
NSObject *object = [NSObject new];
__weak typeof(object) weakObject = object;
void(^block)(void) = ^{
NSLog(@"%d", age);
NSLog(@"%@", object);
NSLog(@"%@", weakObject);
};
block();
}
return 0;
}
- 底层代码
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
// 强引用
_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
// 强引用
_Block_object_assign((void*)&dst->object, (void*)src->object, 3/*BLOCK_FIELD_IS_OBJECT*/);
// 弱引用
_Block_object_assign((void*)&dst->weakObject, (void*)src->weakObject, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
_Block_object_dispose((void*)src->object, 3/*BLOCK_FIELD_IS_OBJECT*/);
_Block_object_dispose((void*)src->weakObject, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
三,__forwarding指针
1,指向
- 当
block
在栈上时,__forwarding
指针指向自身 - 当
block
复制到堆上时,栈上的__forwarding
指针指向堆上的结构体,堆上的__forwarding
指针指向自身

2,原因
- 底层代码
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
// 修改变量值
(age->__forwarding->age) = 2;
}
- 分析
无论访问的是栈上的还是堆上的结构体,通过__forwarding
指针来修改变量值,最终都能将变量值存储到堆上的结构体中
四,对象类型的__block变量
1,访问强指针
- 实例代码
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
Block block;
{
__block Person *person = [Person new];
block = ^{
NSLog(@"%@", person);
};
}
block();
}
return 0;
}
// 打印
<Person: 0x1006361e0>
Person dealloc
- 底层代码
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*);
Person *__strong person; // 指向外部person
};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
Block block;
{
// 创建结构体person
__attribute__((__blocks__(byref))) __Block_byref_person_0 person = {
(void*)0,
(__Block_byref_person_0 *)&person,
33554432,
sizeof(__Block_byref_person_0),
__Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose_131,
((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"),
sel_registerName("new"))
};
block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_person_0 *)&person, 570425344));
}
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_person_0 *person; // 指向结构体person
__main_block_impl_0(void *fp, struct __main_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,访问弱指针
- 实例代码
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
Block block;
{
Person *person = [Person new];
__block __weak Person *weakPerson = person;
block = ^{
NSLog(@"%@", weakPerson);
};
}
block();
}
return 0;
}
// 打印
Person dealloc
(null)
- 底层代码
struct __Block_byref_weakPerson_0 {
void *__isa;
__Block_byref_weakPerson_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__weak weakPerson; // 指向外部weakPerson
};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
Block block;
{
Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("new"));
// 创建结构体weakPerson
__attribute__((__blocks__(byref))) __attribute__((objc_ownership(weak))) __Block_byref_weakPerson_0 weakPerson = {
(void*)0,
(__Block_byref_weakPerson_0 *)&weakPerson,
33554432,
sizeof(__Block_byref_weakPerson_0),
__Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose_131,
person
};
block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_weakPerson_0 *)&weakPerson, 570425344));
}
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_weakPerson_0 *weakPerson; // 指向结构体weakPerson
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : weakPerson(_weakPerson->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
3,分析
- 图解


- 结论
1>
block
对结构体一定是强引用
2>结构体对外部变量可以是强引用,也可以是弱引用
4,copy
和dispose
- 底层代码
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
// 指向__main_block_copy_0
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
// 指向__main_block_dispose_0
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
// 强引用结构体
_Block_object_assign((void*)&dst->person, (void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
// 释放结构体
_Block_object_dispose((void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
}
// ---------------------------
struct __Block_byref_person_0 {
void *__isa;
__Block_byref_person_0 *__forwarding;
int __flags;
int __size;
// 指向__Block_byref_id_object_copy_131
void (*__Block_byref_id_object_copy)(void*, void*);
// 指向__Block_byref_id_object_dispose_131
void (*__Block_byref_id_object_dispose)(void*);
Person *__strong person;
};
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
// 强/弱引用外部变量
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
// 释放外部变量
_Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
- 分析
当block
复制到堆上时
1>同时将结构体也复制到堆上(此时结构体会通过
__Block_byref_id_object_copy
指针调用__Block_byref_id_object_copy_131
函数,此函数会对外部变量产生强/弱引用)
2>通过copy
指针调用__main_block_copy_0
函数,此函数会对结构体产生强引用
当block
从堆上移除时
1>通过
dispose
指针调用__main_block_dispose_0
函数,此函数会释放引用的结构体
2>因为没有持有者,结构体也会从堆上移除(此时结构体会通过__Block_byref_id_object_dispose
指针调用__Block_byref_id_object_dispose_131
函数,此函数会对释放引用的外部变量)
5,MRC环境下结构体不会对外部变量产生强引用
// MRC环境
typedef void(^Block)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block Person *person = [Person new];
Block block = [^{
NSLog(@"%@", person);
} copy];
[person release];
NSLog(@"block未释放:%@", [block class]);
}
return 0;
}
// 打印
Person dealloc
block未释放:__NSMallocBlock__
五,循环引用
1,循环引用
- 实例代码
// Person
@interface Person : NSObject
// person持有block
@property (nonatomic, copy) void(^block)(void);
- (void)test;
@end
@implementation Person
- (void)test {
// 循环引用
self.block = ^{
NSLog(@"%@", self);
};
}
- (void)dealloc {
//[super dealloc]; // MRC环境打开
NSLog(@"Person dealloc");
}
@end
// main
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [Person new];
[person test];
//[person release]; // MRC环境打开
}
NSLog(@"person作用域结束");
return 0;
}
// 打印
person作用域结束
- 底层代码
struct __Person__test_block_impl_0 {
struct __block_impl impl;
struct __Person__test_block_desc_0* Desc;
Person *const __strong self; // block持有person
__Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *const __strong _self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
- 图解

2,ARC环境下的解决方法
-
__unsafe_unretained
和__weak
1>实例代码
- (void)test {
//__unsafe_unretained typeof(self) weakSelf = self;
__weak typeof(self) weakSelf = self;
self.block = ^{
NSLog(@"%@", weakSelf);
};
}
// 打印
Person dealloc
person作用域结束
2>底层代码
struct __Person__test_block_impl_0 {
struct __block_impl impl;
struct __Person__test_block_desc_0* Desc;
//Person *const __unsafe_unretained weakSelf;
Person *const __weak weakSelf; // block弱引用person
__Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *const __unsafe_unretained _weakSelf, int flags=0) : weakSelf(_weakSelf) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
3>图解

4>注意点
注意一:对象释放后,__unsafe_unretained
指针不会自动置nil
,易引起野指针crash,而__weak
指针会自动置nil
,不会出现野指针crash,所以一般都使用__weak
注意二:在某些情况下,__weak
需要结合__strong
一起使用,以保证在block
整个执行过程中对象都不会释放
// 不用__strong
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [Person new];
__weak typeof(person) weakPerson = person;
person.block = ^{
NSLog(@"1---%@", weakPerson);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2---%@", weakPerson);
});
};
person.block();
}
// 打印
1---<Person: 0x6000035c5140>
Person dealloc
2---(null)
// 用__strong
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [Person new];
__weak typeof(person) weakPerson = person;
person.block = ^{
__strong typeof(weakPerson) strongPerson = weakPerson;
NSLog(@"1---%@", strongPerson);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2---%@", strongPerson);
});
};
person.block();
}
// 打印
1---<Person: 0x600001ad7990>
2---<Person: 0x600001ad7990>
Person dealloc
__block
1>实例代码
- (void)test {
__block Person *weakSelf = self;
self.block = ^{
NSLog(@"%@", weakSelf);
weakSelf = nil; // 必须执行
};
self.block(); // 必须执行
}
// 打印
<Person: 0x1020338c0>
Person dealloc
person作用域结束
2>底层代码
struct __Person__test_block_impl_0 {
struct __block_impl impl;
struct __Person__test_block_desc_0* Desc;
__Block_byref_weakSelf_0 *weakSelf; // block持有__block变量
__Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, __Block_byref_weakSelf_0 *_weakSelf, int flags=0) : weakSelf(_weakSelf->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __Block_byref_weakSelf_0 {
void *__isa;
__Block_byref_weakSelf_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__strong weakSelf; // __block变量持有person
};
static void __Person__test_block_func_0(struct __Person__test_block_impl_0 *__cself) {
__Block_byref_weakSelf_0 *weakSelf = __cself->weakSelf; // bound by ref
NSLog((NSString *)&__NSConstantStringImpl__var_folders_0d_7m56tzw16qs_vsxqtxhnsq2m1myhmv_T_Person_3a900e_mi_0, (weakSelf->__forwarding->weakSelf));
(weakSelf->__forwarding->weakSelf) = __null; // 释放__block变量对person的持有
}
3>图解


3,MRC环境下的解决方法
-
__unsafe_unretained
(MRC不支持__weak
)
// MRC环境
- (void)test {
__unsafe_unretained typeof(self) weakSelf = self;
self.block = ^{
NSLog(@"%@", weakSelf);
};
}
// 打印
Person dealloc
person作用域结束
-
__block
(MRC环境下__block
变量不会强引用对象)
1>实例代码
// MRC环境
- (void)test {
__block typeof(self) weakSelf = self;
self.block = ^{
NSLog(@"%@", weakSelf);
};
}
// 打印
Person dealloc
person作用域结束
2>图解

4,不会出现循环引用的情况
// block引用了self,但self没有引用block
[UIView animateWithDuration:0.3 animations:^{
NSLog(@"%@", self);
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@", self);
});
[self.twoView mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(self.oneView);
}];
网友评论