iOS Block再学习
文章目录
- [iOS Block再学习](#iOS Block再学习)
-
- 前言
- Block的三种类型
-
- [__ NSGlobalBlock__](#__ NSGlobalBlock__)
- [__ NSMallocBlock__](#__ NSMallocBlock__)
- [__ NSStackBlock__](#__ NSStackBlock__)
- Block底层分析
- Block的copy时机
-
- block作为函数返回值
- 将block赋给__strong指针
- block作为函数中方法名含有usingBlock的方法参数时
- [block作为GCD API的方法参数时候](#block作为GCD API的方法参数时候)
- Block的三层拷贝
- Block循环引用
- 小结
前言
笔者之前学习过block的相关内容,但是掌握不牢,今天再重新学习一遍
Block的三种类型
__ NSGlobalBlock__
objc
void(^block)(void) = ^ {
NSLog(@"testBlock");
};
NSLog(@"%@", block);
此时block没有参数也没有返回值,属于全局block

如果访问全局变量:

从上面可以看出无论是否创建block变量,只要访问全局变量的话,他就会创建一个全局区的block
__ NSMallocBlock__
objc
int loaclA = 10;
void(^block)(void) = ^ {
NSLog(@"testBlock %ld", loaclA);
};
NSLog(@"%@", block);
此时block捕获了一个临时变量,就是底层拷贝a,所以是堆区block

__ NSStackBlock__
objc
NSLog(@"%@", [^{
NSLog(@"%d", loaclA);
} class]);

这里在自由变量没有处理之前是栈区block
处理之后是堆区block
,目前的栈区block就越来越少了
这里是不创建Block变量,且访问自由变量的时候才会出现这里的一个栈区Block
小结
- block如果不访问自由变量的话,都是存储在全局区的,如果访问全局变量的话,也是存储在全局区的Block
- block如果访问自由变量的话
- 如果没有创建block变量,才会创建一个栈区Block变量
- 创建了一个Block变量,且访问自由变量,才会创建出一个堆区的Block,这里创建出堆区Block的原因是 栈区的Block执行了拷贝操作
Block底层分析
Block的结构
先看这段代码:
objc
int main(int argc, const char * argv[]) {
@autoreleasepool {
extern NSString* nanString;
int localA = 3;
void(^block)(void) = ^{
NSLog(@"123%ld", localA);
};
}
return 0;
}
这里是一段司空见惯的Block捕获自由变量的代码.这里我们来分析一下这里的源码来认识一下底层内容:
objc
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int localA; // 捕获的自由变量
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _localA, int flags=0) : localA(_localA) {
impl.isa = &_NSConcreteStackBlock; // 设置isa指针
//&_NSConcreteStackBlock:标识 Block 初始类型为 栈 Block(未进行 copy 操作时)
impl.Flags = flags;
impl.FuncPtr = fp; // 代码块的函数赋值
Desc = desc;
} // block的一个构造函数,创建Block调用的内容
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int localA = __cself->localA; // bound by copy 这里是一个值拷贝
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_3d8947_mi_0, localA);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
extern NSString* nanString;
int localA = 3;
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, localA)); //相当于block等于__main_block_impl_0,是一个函数
}
return 0;
}
这里我们先看block的一个结构体,这里可以说明block是一个 __main_block_impl_0
类型的对象.从构造函数可以看出他又一个isa指针
总结
- block其实是一个对象,结构体,函数,又因为block没有名称,所以也被叫做,匿名函数

如果从更加底层的角度来看:

捕获自由变量
可以看到Block的实现结构体里面新增了localA
变量,它在上面这是一个单纯的一个值拷贝
这里我看一下这段核心代码:
objc
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int localA = __cself->localA; // bound by copy 这里是一个值拷贝
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_3d8947_mi_0, localA);
}
这里我们可以看到这里如果对于他的值其实是只读的,它并不会修改原来block中持有的变量的值,所以是有问题的
block捕获外界变量时,在内部会自动生成同一个属性来保存
捕获全局(静态)变量
这里我们看一下如果是一个全局变量会是什么形式?
objc
int localA = 10;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_4d4ace_mi_0, localA++);
}
这里我们可以看到它对于全局变量就是直接引用了全局变量的内容
捕获静态变量
这里我们看一下如果是一个静态局部变量:
objc
int main(int argc, const char * argv[]) {
@autoreleasepool {
extern NSString* nanString;
static int loaclB = 10;
void(^block)(void) = ^{
loaclB++;
NSLog(@"123%ld", localA++);
};
}
return 0;
}
如果是一个静态局部变量的话他是通过指针来获取的,同时静态局部变量与Block建立关联的是指针(int *)
,也就是说Block捕获的静态局部变量捕获的是变量的指针,因此当我们对静态局部变量进行修改时,Block内部的静态局部变量的值也会随之改变.
objc
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *loaclB;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_loaclB, int flags=0) : loaclB(_loaclB) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
__block修饰符
如果给localA添加一个修饰符__block
,然后在block
中对localA
进行一个加加操作
objc
int main(int argc, const char * argv[]) {
@autoreleasepool {
extern NSString* nanString;
__block int localA = 3;
void(^block)(void) = ^{
NSLog(@"123%ld", localA++);
};
}
return 0;
}
这时候我们在把它重写成我们的结构体:
objc
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_localA_0 *localA; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_localA_0 *_localA, int flags=0) : localA(_localA->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->localA, (void*)src->localA, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->localA, 8/*BLOCK_FIELD_IS_BYREF*/);}
//重新看一下函数部分
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_localA_0 *localA = __cself->localA; // bound by ref
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_fd8242_mi_0, (localA->__forwarding->localA)++);
}
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
extern NSString* nanString;
//创建一个__Block_byref_a_0 是一个结构体,相当于把外界对象村对象
__attribute__((__blocks__(byref))) __Block_byref_localA_0 localA = {(void*)0,(__Block_byref_localA_0 *)&localA, 0, sizeof(__Block_byref_localA_0), 3};
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_localA_0 *)&localA, 570425344));
}
return 0;
}
这里我们发现原先的localA
变成了__Block_byref_localA_0
的部分
objc
struct __Block_byref_localA_0 { //__block修饰的外界变量的结构体
void *__isa;
__Block_byref_localA_0 *__forwarding;
int __flags;
int __size;
int localA;
};
总结:
外界变量
如果被__block修饰会变成这个结构体- 结构体用来保存变量的指针和值
- 将变量生成的结构体对象中的指针地址 传递给block, 然后在block内部就可以对外界变量进行操作了
两种拷贝对比如下
值拷贝
- 深拷贝,只是拷贝数值,且拷贝的值不可更改,指向不同的内存空间,案例中普通变量loacaA
就是值拷贝
指针拷贝
- 浅拷贝,生成的对象指向同一片内存空间,案例中经过__block
修饰的变量localA
就是指针拷贝
forwarding指针
这里我们首先看下面这段代码:
objc
__block int val = 0;
void (^block)(void) = ^{
val++;
};
++val;
block();
NSLog(@"%ld", val);
这里我们看一下这里的一个val在堆区上面,一个在栈区上面,如果我们不做处理的话对于他的数据计算会有问题:
所以这就是forwarding
这个指针的使命所在--确保可以正确的访问__block变量
我们把它转译成c++源码来看一下:
objc
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
extern NSString* nanString;
__attribute__((__blocks__(byref))) __Block_byref_loaclB_0 loaclB = {(void*)0,(__Block_byref_loaclB_0 *)&loaclB, 0, sizeof(__Block_byref_loaclB_0), 10};
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_loaclB_0 *)&loaclB, 570425344));
(loaclB.__forwarding->loaclB)++;
}
return 0;
}
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_loaclB_0 *loaclB = __cself->loaclB; // bound by ref
(loaclB->__forwarding->loaclB)++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_589979_mi_0, localA++);
}
从上面我们可以看出对于这种自由变量的加减操作,都是通过我们的(loaclB->__forwarding->loaclB)++
这一句指令来执行的,无论是堆区的block
还是栈区的block
都是这样操作的,这时候我们看一下Block copy的相关源码:
这部分源码内容会在下面的Block的拷贝部分讲一下,这里先不提起,先让笔者了解一下有关于__Block变量拷贝的图
当block变量从stack copy到heap上面的时候,stack上的forwarding被修改程指向heap上的block变量,通过这个机制,保证我们无论是在stack和heap上面都可以访问到同一个block变量

Block的copy时机
在 Objective-C 中,Block 最初是在栈上创建的。栈上的 Block(NSStackBlock)生命周期与其定义的作用域相关联,一旦该作用域结束,栈上的 Block 将不再有效。这意味着如果你需要在 Block 的定义作用域外使用它,比如将它作为回调传递或保存为后续使用,你需要将它复制到堆上(成为 NSMallocBlock)。
就好比我们在子线程回调到主线程的时候,我们的Block如果在栈上的话,那么超出作用域就会被销毁,无法回到主线程被调用,因此需要拷贝到堆上
在ARC中进行了很多优化的内容,Block的copy操作会在下面这些情况下执行:
block作为函数返回值
objc
- (nxBlock)test {
int a = 0;
return ^{
NSLog(@"1233123 %d", a);
};
}
nxBlock testBlock = [self test];
NSLog(@"%@", [testBlock class]);

将block赋给__strong指针
objc
int loaclA = 10;
void(^block)(void) = ^ {
NSLog(@"testBlock %ld", loaclA);
};
NSLog(@"%@", block);

block作为函数中方法名含有usingBlock的方法参数时
objc
NSArray *array = @[@1,@4,@5];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
block作为GCD API的方法参数时候
objc
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
Block的三层拷贝
这里有一个Block的三层copy,我们先分析学习有关于
objc
static void *_Block_copy_internal(const void *arg, const int flags)
{
struct Block_layout *aBlock;
const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE;
if (!arg) return NULL;
aBlock = (struct Block_layout *)arg; // 强制转化成Block_layout对象,防止对外界造成影响
if (aBlock->flags & BLOCK_NEEDS_FREE) // NSConcreteMallocBlock
//是否需要释放
{
latching_incr_int(&aBlock->flags);
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) // NSConcreteGlobalBlock
//如果是全局block,直接返回
{
return aBlock;
}
// 为栈block或者是堆区block,由于堆区需要申请内存,所以是栈区的block的操作
// Its a stack block. Make a copy.
struct Block_layout *result = malloc(aBlock->descriptor->size);
if (!result) return (void *)0;
//通过memmove内存拷贝,将aBlock拷贝到result
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 1;
result->isa = _NSConcreteMallocBlock;
if (result->flags & BLOCK_HAS_COPY_DISPOSE)
{
(*aBlock->descriptor->copy)(result, aBlock); // do fixup
}
return result;
}
下面简单讲述一下这个步骤:
- 如果需要释放,如果需要就直接释放
- 如果是
globalBlock
不需要copy,直接返回 - 否则就只有两种情况,栈区block和堆区block,但是由于需要拷贝,所以堆区Block需要申请内存,所以最后是有关于栈区Block的拷贝代码
- 通过malloc申请内存来接受block
- 通过
memmove
将block拷贝到新申请的内存中 - 设置block对象的类型为堆区block,就是
result->isa = _NSConcreteMallocBlock;
外界变量的类型
objc
// Runtime support functions used by compiler when generating copy/dispose helpers
// Values for _Block_object_assign() and _Block_object_dispose() parameters
enum {
// see function implementation for a more complete description of these fields and combinations
//普通对象,即没有其他的引用类型,也就是我们任何的一个Object都是这个逻辑
BLOCK_FIELD_IS_OBJECT = 3, // id, NSObject, __attribute__((NSObject)), block, ...
//block类型作为变量
BLOCK_FIELD_IS_BLOCK = 7, // a block variable
//经过__block修饰的变量和对象
BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable
//weak 弱引用变量
BLOCK_FIELD_IS_WEAK = 16, // declared __weak, only used in byref copy helpers
//返回的调用对象 - 处理block_byref内部对象内存会加的一个额外标记,配合flags一起使用
BLOCK_BYREF_CALLER = 128, // called from __block (byref) copy/dispose support routines.
};
这里我们在看一下这里的_Block_object_assign
的源码,以及和下面这段Block拷贝的代码进行对比:
objc
int main(int argc, const char * argv[]) {
@autoreleasepool {
extern NSString* nanString;
int loaclB = 10;
__block NSObject* obj = [[NSObject alloc] init];
NSMutableArray* ary = [NSMutableArray array];
void(^block)(void) = ^{
[ary addObject:@"123"];
NSLog(@"123%ld %@", loaclB, obj);
};
block();
[ary addObject:@"23333"];
NSLog(@"%@", ary);
loaclB++;
}
return 0;
}
//反编译之后变成下面这样:
//block的拷贝函数
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->ary, (void*)src->ary, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->obj, (void*)src->obj, 8/*BLOCK_FIELD_IS_BYREF*/);} //这里调用了这个_Block_object_assign方法
//block里面的匿名函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_obj_0 *obj = __cself->obj; // bound by ref
NSMutableArray *ary = __cself->ary; // bound by copy
int loaclB = __cself->loaclB; // bound by copy
((void (*)(id, SEL, ObjectType _Nonnull))(void *)objc_msgSend)((id)ary, sel_registerName("addObject:"), (id _Nonnull)(NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_d58752_mi_0);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_rb_0ts7dvhs3zg9fc6kk2nvtlr00000gn_T_main_d58752_mi_1, loaclB, (obj->__forwarding->obj));
}
//block的结构
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSMutableArray *ary;
int loaclB;
__Block_byref_obj_0 *obj; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSMutableArray *_ary, int _loaclB, __Block_byref_obj_0 *_obj, int flags=0) : ary(_ary), loaclB(_loaclB), obj(_obj->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
这里我们看到了这里有两种类型:
BLOCK_FIELD_IS_OBJECT
和BLOCK_FIELD_IS_BYREF
这里在这三层拷贝的最后一层进行一个讲解
这里如果有__block
修饰的变量就会先调用一次这个方法:
objc
static struct Block_byref *_Block_byref_copy(const void *arg) {
//强转为Block_byref结构体类型,保存一份
struct Block_byref *src = (struct Block_byref *)arg;
if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
// src points to stack 申请内存
struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
copy->isa = NULL;
// byref value 4 is logical refcount of 2: one for caller, one for stack
copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
//block内部持有的Block_byref 和 外界的Block_byref 所持有的对象是同一个,这也是为什么__block修饰的变量具有修改能力
//copy 和 scr 的地址指针达到了完美的同一份拷贝,目前只有持有能力
copy->forwarding = copy; // patch heap copy to point to itself // 把堆区的指针指向自己
src->forwarding = copy; // patch stack to point to heap copy // 把栈区的指针指向堆区,保证数值一致
copy->size = src->size;
//如果有copy能力
if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) { // 有自己的一个copy的逻辑
// Trust copy helper to copy everything of interest
// If more than one field shows up in a byref block this is wrong XXX
//Block_byref_2是结构体,__block修饰的可能是对象,对象通过byref_keep保存,在合适的时机进行调用
struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
copy2->byref_keep = src2->byref_keep; // 调用自定义复制逻辑
copy2->byref_destroy = src2->byref_destroy;
if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
copy3->layout = src3->layout;
}
//等价于 __Block_byref_id_object_copy
(*src2->byref_keep)(copy, src);
}
else {
// Bitwise copy.
// This copy includes Block_byref_3, if any.
memmove(copy+1, src+1, src->size - sizeof(*src)); //如果为简单类型就直接进行一个内存拷贝
}
}
// already copied to heap
else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
latching_incr_int(&src->forwarding->flags);
}
return src->forwarding;
}
这个函数完成了copy之后会调用我们的最后一个函数_Block_object_assign
这个函数由编译器来处理
第三层拷贝是block对传入对象的变量进行_Block_object_assign,将block内部将要使用的对象的变量拷贝到block内部。
上面我们自己给出的那段代码中有BLOCK_FIELD_IS_OBJECT
和BLOCK_FIELD_IS_BYREF
这两种都会进入我们的最后的_Block_object_assign
然后进行不同的处理
- 如果是普通对象,则交给系统ARC处理,并拷贝对象指针,就是引用计数甲一,所以外界变量不能释放
- 如果是
block类型
的变量,则通过_Block_copy
操作,将block拷贝到堆区 - 如果是
__block修饰
的变量,就通过_Block_byref_copy
函数进行内存拷贝以及常规处理
三层拷贝总结:
第一层通过_Block_copy
实现对象的自身拷贝,从栈区拷贝到堆区
第二层通过调用_Block_byref_copy
这个来实现对于对象拷贝成Block_byref
类型
第三次调用_Block_object_assign
对于__block修饰
的当前变量内部对象的内存管理
当且仅当用__block变量的时候才会有三次拷贝.
Block循环引用
先认识一下什么是循环引用:
- 正常使用: 是指A持有B的引用,当A调用dealloc方法,给B发送release信号,B收到release信号,如果此时B的retainCount为0的时候,则调用B的dealloc方法
- 循环引用:A, B互相持有,所以导致A无法调用dealloc方法给Breleasse信号,所以B也无法接受到release信号,所以A,B此时都无法释放

解决循环引用
objc
self.name = @"123";
self.testBlock = ^(void){
NSLog(@"%@", self.name);
};
UIView animateWithDuration:1 animations:^{
NSLog(@"%@",self.name);
};
代码第一种发生了一个循环引用的问题,因为在block
中持有了外部变量name
,导致block也持有了self,而self本来是持有block的,所以导致了self和block的互相持有
代码二中没有循环引用,因为self中没有持有animation的block,不构成互相持有
weak-strong-dance
如果block内部并未嵌套block,直接使用__weak
修饰self即可
objc
self.name = @"123";
__weak typeof (self) weakSelf = self;
self.testBlock = ^(void){
NSLog(@"%@", weakSelf.name);
};
此时的weakSelf
和self
指向同一片内存空间,且使用__weak不会导致self的引用计数发生变化
objc
__weak typeof (self) weakSelf = self;
self.testBlock = ^(void){
NSLog(@"%@", weakSelf.name);
};
如果block里面嵌套了block,那么就要同时使用__weak
和__strong
objc
self.testBlock = ^(void){
__strong typeof(weakSelf) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@", weakSelf.name);
});
};
如果不采用强弱共舞的话,可能会出现一个过了一会self被释放导致我们获取不到对应的数据,所以要采用__strong
修饰一下,
其中strongSelf
是一个临时变量,在cjlBlock的作用域内,即内部block执行完
就释放strongSelf
,所以并不会出现block
持有self
的一个情况导致这些问题
__block修饰符
我们可以采用__block修饰符,在主动调用完后手动释放self
objc
__block PersonViewController* vc = self;
self.testBlock = ^(void){
NSLog(@"%@", vc.name);
vc = nil;
};
self.testBlock();
需要注意的是这里的block必须调用
如果不调用block的话,vc就不会主动置为nil,那么仍旧是循环引用,self和block都不会被释放
对象self作为参数
主要是把对象self作为参数,提供给block内部使用,不会有引用计数问题
objc
self.testBlock = ^(PersonViewController* vc){
NSLog(@"%@", vc.name);
};
小结
笔者这里简单总结了有关于Block的一个知识,复习了有关于Block捕获变量,以及使用__block
修饰的时候forwarding
指针的有一个变化,还有一个它发生拷贝的一个时机,以及复习了几种有关于解决Block循环引用的内容.