首先抛出来一些问题
block{}内部会捕捉哪些类型的变量 ?
block{}内部是怎样捕获变量的?整个copy的流程是怎样实现的 ?
block{}内部捕获到"__block NSObject *obj;"时 obj的引用计数为什么会是1 ?
block{}内部捕获到"NSObject *obj;" 时 obj的引用计数为什么会是3 ?
block{}内部如何调用NSObject (NSDelayedPerforming)的api (performSelector:withObject: afterDelay:) ?
【 block源码请移步 opensource.apple.com "libclosure-xx" 】
block怎么使用?
(1) block的基本用法:
{
void (^hello)(char *);
hello = ^(char *str) {
NSLog(@"hello:%s", str);
}; // 不要忘记这个分号
hello("Avery~");
}
{
int multiplier = 7;
int (^myBlock)( int ) = ^( int num ) {
return num * multiplier;
};
int result = myBlock(3);
}
{
typedef void (^CompletionBlock)(NSDictionary * _Nullable info);
- (void)testMethod:(CompletionBlock)completion {
// do somthing ...
NSDictionary *info = [NSDictionary dictionary];
if (completion) {
completion(info);
}
}
TestObject *object = [TestObject new];
[object testMethod:^(NSDictionary * _Nullable info) {
// do somthing...
}];
}
(2) 稍微复杂一点的用法:
(1) TestObject:
- (void)testMethodWithAllow:(BOOL(^)(void))allow
completion:(void(^)(void))completion
canceled:(void(^)(NSString *reason))canceled
failed:(void(^)(NSError *error))failed;
- (void)testMethodWithAllow:(BOOL(^)(void))allow
completion:(void(^)(void))completion
canceled:(void(^)(NSString *reason))canceled
failed:(void(^)(NSError *error))failed {
if (allow) {
if (allow() == YES) {
NSLog(@"allowed");
// Do your thing...
if (completion) {
completion();
}
/**
if (canceled) {
canceled(@"canceled msg");
}
if (failed) {
NSDictionary *errorInfo = [NSDictionary dictionaryWithObjectsAndKeys:@"error-message", @"info", nil];
NSError *error = [NSError errorWithDomain:@"Wrong-Domain" code:-1 userInfo:errorInfo];
failed(error);
}
*/
}
else {
NSLog(@"not allowed");
}
}
else {
}
}
(2) ViewController:
TestObject *object = [TestObject new];
[object testMethodWithAllow:^BOOL{
return [self isAllowed];
} completion:^{
NSLog(@"completion ~~~");
} canceled:^(NSString * _Nonnull reason) {
NSLog(@"canceled!");
} failed:^(NSError * _Nonnull error) {
NSLog(@"failed!");
}];
- (BOOL)isAllowed {
// 在这里加入你的判断逻辑
return YES;
}
- (void)searchText:(NSString * _Nonnull)text
resetResultInfo:(NSDictionary * _Nullable (^_Nullable)(void))searchResultInfo;
- (void)searchTexts:(NSArray * _Nonnull)texts resetSearchResultInfo:(NSDictionary * _Nullable (^_Nullable)(void))searchResultInfo {
if (!texts || texts.count == 0) {
return;
}
NSMutableArray *searchRanges = [NSMutableArray array];
[self.attributedText searchTexts:texts saveWithRangeArray:&searchRanges];
if (searchRanges.count > 0) {
NSDictionary *info = searchResultInfo();
self.attributedText.searchRanges = searchRanges;
self.attributedText.searchAttributeInfo = info;
if (info && [info isKindOfClass:[NSDictionary class]] && info.count > 0) {
QAAttributedLayer *layer = (QAAttributedLayer *)self.layer;
[layer drawHighlightColorInRanges:searchRanges attributeInfo:info];
}
}
}
// 详细的使用可以在这里找到:https://github.com/Avery-AN/TableView
(3) 用block{}实现链式编程:
(1) ViewController:
Person *person = [Person new];
person.studyBlock().exerciseBlock(2).studyBlock();
(2) Person:
- (Person * (^)(int))exerciseBlock;
- (Person * (^)(void))studyBlock;
- (Person * (^)(int))exerciseBlock {
Person * (^averyBlock)(int) = ^(int count) {
NSLog(@"++++++ exercise: %d", count);
return self;
};
return averyBlock;
}
- (Person * (^)(void))studyBlock {
Person * (^averyBlock)(void) = ^() {
NSLog(@"++++++ study");
return self;
};
return averyBlock;
}
block{}可以取消吗?
用下面这两种方式创建的block有什么优势吗?如果没有那么为什么要用这些api呢?
本篇暂不详细讲解、不了解的同学还请自行查找相关知识点。
dispatch_block_t dispatch_block_create(dispatch_block_flags_t flags, dispatch_block_t block);
dispatch_block_t dispatch_block_create_with_qos_class(dispatch_block_flags_t flags,
dispatch_qos_class_t qos_class, int relative_priority,
dispatch_block_t block);
取消block{}的api (只能取消尚未执行的任务):
dispatch_block_cancel();
dispatch_block_testcancel();
那么正在执行中的block{}怎么取消?看下面的代码:
__block BOOL blockCanceledFlag = NO;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (NSUInteger i = 0; i < 10; i++) {
NSLog(@"正在执行第'%d'次",i);
[NSThread sleepForTimeInterval:1];
if (blockCanceledFlag == YES) {
NSLog(@"block被终止");
return;
}
};
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
NSLog(@"准备停止block~~~");
blockCanceledFlag = YES;
});
编译器对block{}做了哪些事?
int main(int argc,const char *argv[]) {
@autoreleasepool {
// insert code here...
void(^averyBlock)(void) = ^{
NSLog(@"hello Avery");
};
averyBlock();
}
return 0;
}
/**
struct __block_impl {
void *isa; // (表明block本身也是一个OC对象) _NSConcreteStackBlock、_NSConcreteGlobalBlock、_NSConcreteMallocBlock
int Flags; // 按位存储一些 block 的附加信息
int Reserved;
void *FuncPtr; // 函数指针 (指向 Block 要执行的函数、即__main_block_func_0)
};
*/
// block结构体 (包含两个成员变量、一个构造函数)
struct __main_block_impl_0 {
struct__block_impl impl; //block实现结构体
struct __main_block_desc_0* Desc;
__main_block_impl_0(void*fp,struct__main_block_desc_0 *desc,intflags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// block代码块中的实现
static void__main_block_func_0(struct__main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_v2_jnjsfbtj5qq7x5wc059cl8n00000gn_T_main_b244b8_mi_0);
}
// block描述结构体
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(intargc,const char *argv[]) {
/* @autoreleasepool */
{
__AtAutoreleasePool __autoreleasepool;
// block的实现:
void(*averyBlock)(void) = ((void(*)())&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA));
// block的调用:
((void(*)(__block_impl *))((__block_impl *)averyBlock)->FuncPtr)((__block_impl *)averyBlock);
}
return 0;
}
block{}内部会捕捉哪些类型的变量?
int globaleA = 1;
static int staticGlobaleB = 2;
int main(int argc,const char *argv[]) {
int tmpC =3;
static int staticD =4;
NSMutableString *string = [[NSMutableString alloc] initWithString:@"Hello "];
void(^averyBlock)(void) = ^{
globaleA++;
staticGlobaleB++;
staticD++;
[string appendString:@"avery~"];
NSLog(@"globaleA: %d; staticGlobaleB: %d; tmpC: %d; staticD: %d; string: %@", globaleA, staticGlobaleB, tmpC, staticD, string);
};
averyBlock();
return 0;
}
int globaleA = 1;
static int staticGlobaleB = 2;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *staticD;
NSMutableString *__strong string;
int tmpC;
__main_block_impl_0(void*fp,struct__main_block_desc_0 *desc,int*_staticD, NSMutableString *__strong _string,int_tmpC,intflags=0) : staticD(_staticD), string(_string), tmpC(_tmpC) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct__main_block_impl_0 *__cself) {
int *staticD = __cself->staticD; // bound by copy
NSMutableString *__strong string = __cself->string; // bound by copy
int tmpC = __cself->tmpC; // bound by copy
globaleA++;
staticGlobaleB++;
(*staticD)++;
((void(*)(id, SEL, NSString *_Nonnull__strong))(void*)objc_msgSend)((id)string, sel_registerName("appendString:"), (NSString *)&__NSConstantStringImpl__var_folders_v2_jnjsfbtj5qq7x5wc059cl8n00000gn_T_main_9a6e9f_mi_1);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_v2_jnjsfbtj5qq7x5wc059cl8n00000gn_T_main_9a6e9f_mi_2, globaleA, staticGlobaleB, tmpC, (*staticD), string);
}
static void __main_block_copy_0(struct__main_block_impl_0*dst,struct__main_block_impl_0*src) {
_Block_object_assign((void*)&dst->string, (void*)src->string,3/*BLOCK_FIELD_IS_OBJECT*/);
}
static void __main_block_dispose_0(struct__main_block_impl_0*src) {
_Block_object_dispose((void*)src->string,3/*BLOCK_FIELD_IS_OBJECT*/);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void(*copy)(struct__main_block_impl_0*,struct__main_block_impl_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};
int main(int argc,const char *argv[]) {
int tmpC =3;
static int staticD =4;
NSMutableString *string = ((NSMutableString *(*)(id, SEL, NSString *_Nonnull__strong))(void*)objc_msgSend)((id)((NSMutableString *(*)(id, SEL))(void*)objc_msgSend)((id)objc_getClass("NSMutableString"), sel_registerName("alloc")), sel_registerName("initWithString:"), (NSString *)&__NSConstantStringImpl__var_folders_v2_jnjsfbtj5qq7x5wc059cl8n00000gn_T_main_9a6e9f_mi_0);
void(*averyBlock)(void) = ((void(*)())&__main_block_impl_0((void*)__main_block_func_0, &__main_block_desc_0_DATA, &staticD, string, tmpC,570425344));
((void(*)(__block_impl *))((__block_impl *)averyBlock)->FuncPtr)((__block_impl *)averyBlock);
return 0;
}
通过__main_block_impl_0结构体中的构造函数可知:自动变量tmpC & string和静态变量staticD被截获为block的成员变量:
截获时机:在main函数中实现block时,会将栈参数传入__main_block_impl_0的构造函数中进行初始化,所以block会在实现的地方截获变量,而截获的变量的值也是实现时刻的变量值:
那么block{}为什么没有捕获全局变量呢?
因为全局变量存储在内存的.data全局区、并不是存储在栈上的、其作用域是全局的。
让我们用一张图来标示一下这几个结构体之间的关系:
block{}内部是怎样捕获变量的?整个copy的流程是怎样实现的?
我们用下面的代码来讲解,具体流程请看代码处的注释:
- (void)testBlockAction {
/**
"__block NSObject *testObj = [NSObject new];": {
(1) "[NSObject new]":
在堆上开辟一块内存来存放NSObjec通过new生成的对象;
(2)"__block NSObject *testObj":
指针testObj存放在栈空间上;
编译器会对testObj指针的数据类型做封装:"__block NSObject *testObj;" -> "__Block_byref_object_0 *testObj;";
由于testObj是用__block修饰的,编译器会将指针testObj的数据类型NSObject封装成结构体__Block_byref_testObj_0、
并在这个结构体内部创建强引用类型的指针NSObject *__strong testObj;
(3) 对保存在栈空间上的testObj的结构体__Block_byref_testObj_0 *testObj进行初始化赋值:
((void*)0,(__Block_byref_testObj_0 *)&testObj, 33554432, sizeof(__Block_byref_testObj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, testObj);
结构体__Block_byref_testObj_0中的testObj指针指向了保存数据的那块内存地址;
}
*/
__block TestObject *testObj = [TestObject new];
NSLog(@"testObj (block out): %p; %@",&testObj, testObj);
NSLog(@"retainCount (block out): %ld", (long)CFGetRetainCount((__bridge CFTypeRef) (testObj))); // 1
/**
"averyBlock = ^(void) {}":
ARC下对block的赋值操作会使block被拷贝到堆空间;
(1) 当block{}内部捕捉到用__block修饰的变量时、编译器会合成用于Block_copy和Block_release的助手函数,称为copy和dispose助手:
(__AveryBlockManager__testBlockAction_block_copy_0 & __AveryBlockManager__testBlockAction_block_dispose_0);
同样当block{}内部捕捉到用__block修饰的变量时、编译器会根据捕捉到的变量来决定Block_byref结构体中的flags的值,以及在
__Block_byref_testObj_0结构体内部是否包含"void (*__Block_byref_id_object_copy)(void*, void*);" & "void (*__Block_byref_id_object_dispose)(void*);"两个函数;
(2) 在ARC下这种赋值操作会使block被拷贝到堆空间;
_Block_copy函数是负责block的拷贝的、会申请堆空间的block并用栈block进行初始化;
block拷贝的倒数第二步会调用_Block_call_copy_helper函数、此函数会触发"__AveryBlockManager__testBlockAction_block_copy_0"函数 随后会再触发函数"_Block_object_assign((void*)&dst->testObj, (void*)src->testObj, 8);" 随后会再触发函数"_Block_byref_copy(src->testObj)",如果已经被拷贝到堆空间则会直接返回、不再对__Block_byref_testObj_0进行拷贝;
在该函数中会malloc内存空间并完成对src->testObj的栈空间到堆空间的拷贝,_Block_byref_copy这个函数中还有一个重要的操作、那就是通过下面这个操作
"copy->forwarding = copy; src->forwarding = copy;"完成栈上的forwarding指向堆、堆上的forwarding指向自己。
_Block_byref_copy方法的最后一步操作是如果(src->flags(src->flags的值是在编译器确定的) & BLOCK_BYREF_HAS_COPY_DISPOSE)值为YES、 即__Block_byref_testObj_0结构体内部包含有__Block_byref_id_object_copy & __Block_byref_id_object_dispose两个函数、 那么则调用__Block_byref_id_object_copy(copy, src);对__Block_byref_testObj_0内部的"TestObject *__strong testObj;"进行相应的内存管理,
调用函数__Block_byref_id_object_copy_131即_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131); 会根据结构体__Block_byref_testObj_0内部的"TestObject *__strong testObj;"的修饰符来决定该结构体对其进行强引用还是弱引用。
(3) 整个block copy的最后一步操作是"copyBlock->isa = _NSConcreteMallocBlock;"即把新copy的block标记为堆block,并返回这个堆block。
"averyBlock();": {
调用block、执行block内部的相关操作;
"[testObj testMethod];":
block{}内部使用testObj指针、会使block捕捉到testObj指针;
然后执行testMethod方法中的相关操作。
}
*/
void(^averyBlock) (void);
averyBlock = ^(void) {
[testObj testMethod];
NSLog(@"testObj->isa: %@",object_getClass(testObj));
NSLog(@"testObj (in block): %p; %@",&testObj, testObj);
NSLog(@"retainCount (in block): %ld", (long)CFGetRetainCount((__bridgeCFTypeRef) (testObj))); // 1
};
NSLog(@"averyBlock类型: %@",averyBlock); // __NSMallocBlock__
averyBlock();
NSLog(@"testObj (after call block): %p; %@", &testObj, testObj);
NSLog(@"retainCount (after call block): %ld", (long)CFGetRetainCount((__bridge CFTypeRef) (testObj))); // 1
}
不用__block 或者 __weak修饰对象时、引用计数为什么会是3?
我们用下面的代码来讲解,具体流程请看代码处的注释:
/**
将block{}拷贝到堆空间的步骤:
_Block_copy -> _Block_call_copy_helper -> xxx_block_copy_0 -> _Block_object_assign((void*)&dst->obj, (void*)src->obj, 3);
_Block_object_assign函数中会触发两个操作"_Block_retain_object(object);" & "*dest = object;"
综上:
当block{}内部捕获到临时变量NSObject *obj时会生成一个指针指向obj;
当给averyBlock赋值时会将block{}copy到堆区,在堆中又持有了一个指向obj的指针;
所以在block{}内部obj的retainCount的值为3;
block{}执行完毕后、block{}所在的堆空间并不会被立即释放,所以after call block后obj的retainCount的值仍为3;
*/
int main(int argc,const char *argv[]) {
/**
NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new"));
*/
NSObject *obj = [NSObject new];
NSLog(@"(block out) obj: %p; obj: %@", &obj, obj);
NSLog(@"(block out) retainCount: %ld", (long)CFGetRetainCount((__bridge CFTypeRef) (obj))); // 1
/**
void (*averyBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, obj, 570425344));
*/
void(^averyBlock)(void) = ^{
NSLog(@"(in block) obj: %p; obj: %@", &obj, obj);
NSLog(@"(in block) retainCount: %ld", (long)CFGetRetainCount((__bridge CFTypeRef) (obj))); // 3
};
averyBlock();
NSLog(@"(after call block) obj: %p; obj: %@", &obj, obj);
NSLog(@"(after call block) retainCount: %ld", (long)CFGetRetainCount((__bridge CFTypeRef) (obj))); // 3
return 0;
}
用__block修饰对象时、引用计数为什么会是1?
我们用下面的代码来讲解,具体流程请看代码处的注释:
/**
将block{}拷贝到堆空间的步骤 ("void *"表示通用指针类型):
_Block_copy -> _Block_call_copy_helper -> xxx_block_copy_0 -> _Block_object_assign((void*)&dst->obj, (void*)src->obj, 8);
-> _Block_byref_copy(src->obj) -> __Block_byref_id_object_copy(copy, src); -> __Block_byref_id_object_copy_131
-> _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131); -> *dest = obj;
综上:
obj在初始化之后其引用计数为1;
block{}内部捕捉到obj时:获取到的是__Block_byref_obj_0结构体的地址;(__main_block_impl_0->obj保存的是指向obj的地址)、所以不会使obj的引用计数加1;
当给averyBlock赋值时会将block{}copy到堆区、会将__Block_byref_obj_0结构体一同拷贝到堆区、并将栈区中保存的obj的地址赋值给堆区的指针*dest;
整个过程中都没有对obj进行强引用、所以obj的引用计数仍然为1。
*/
int main(int argc,const char *argv[]) {
/**
__attribute__((__blocks__(byref))) __Block_byref_obj_0 obj = {(void*)0,(__Block_byref_obj_0 *)&obj, 33554432, sizeof(__Block_byref_obj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new"))};
*/
__block NSObject *obj = [NSObject new];
NSLog(@"(block out) obj: %p; obj: %@", &obj, obj);
NSLog(@"(block out) retainCount: %ld", (long)CFGetRetainCount((__bridge CFTypeRef) (obj))); // 1
/**
void (*averyBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_obj_0 *)&obj, 570425344));
*/
void(^averyBlock)(void) = ^{
NSLog(@"(in block) obj: %p; obj: %@", &obj, obj);
NSLog(@"(in block) retainCount: %ld", (long)CFGetRetainCount((__bridge CFTypeRef) (obj))); // 1
};
averyBlock();
NSLog(@"(after call block) obj: %p; obj: %@", &obj, obj);
NSLog(@"(after call block) retainCount: %ld", (long)CFGetRetainCount((__bridge CFTypeRef) (obj))); // 1
return 0;
}
AveryBlock[65473:21936013] (block out) obj: 0x7ffeefbff4f8; obj: <NSObject: 0x1005269b0>
AveryBlock[65473:21936013] (block out) retainCount: 1
AveryBlock[65473:21936013] (in block) obj: 0x1005074f8; obj: <NSObject: 0x1005269b0>
AveryBlock[65473:21936013] (in block) retainCount: 1
AveryBlock[65473:21936013] (after call block) obj: 0x1005074f8; obj: <NSObject: 0x1005269b0>
AveryBlock[65473:21936013] (after call block) retainCount: 1
Program ended with exit code: 0
用__weak修饰对象时的情况
int main(int argc,const char *argv[]) {
/**
NSObject *tmp = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new"));
__attribute__((objc_ownership(weak))) NSObject * obj = tmp;
*/
NSObject *tmp = [NSObject new];
__weak NSObject *obj = tmp;
NSLog(@"(block out) obj: %p; obj: %@", &obj, obj); // obj在栈空间
NSLog(@"(block out) retainCount: %ld", (long)CFGetRetainCount((__bridge CFTypeRef) (obj))); // 1 (2)
/**
void (*averyBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, obj, 570425344));
*/
void(^averyBlock)(void) = ^{
NSLog(@"(in block) obj: %p; obj: %@", &obj, obj); // 堆空间新申请了__weak指针指向保存obj的那块内存
NSLog(@"(in block) retainCount: %ld", (long)CFGetRetainCount((__bridge CFTypeRef) (obj))); // 1 (2)
};
averyBlock();
NSLog(@"(after call block) obj: %p; obj: %@", &obj, obj); // obj仍然在栈空间
NSLog(@"(after call block) retainCount: %ld", (long)CFGetRetainCount((__bridge CFTypeRef) (obj))); // 1 (2)
return0;
}
block{}内部如何使用"performSelector:withObject:afterDelay:" ?
当然你也可以用dispatch_source_t来实现相关的需求。
至于如何使用NSObject (NSDelayedPerforming)的api、请参考另外一篇博文:iOS RunLoop中你应该知道的那些事
block{}与函数指针
int addNumber(int x, int y) {
return x + y;
}
// 声明函数指针:
int (*fun) (int x, int y);
int main(int argc, const char * argv[]) {
fun = &addNumber;
int a = 1;
int b = 2;
int result = (*fun) (a, b);
NSLog(@"result: %d", result);
return 0;
}
函数指针本质是一个指针,其指向代码区的一段可执行代码,函数地址是在编译链接时就已经确定下来了;
block本质就是一个Objective-C对象;
block{}与delegate相比有哪些优缺点 ?
(1) delegate运行成本较低,block的运行成本比delegate要高一些;
block出栈需要将使用的数据从栈内存拷贝到堆内存,有可能会涉及到数据类型的转换或者操作引用计数;delegate只是保存了一个对象指针,回调的时候没有额外的消耗;
(2) delegate相比block有时会关注事件的处理过程、而block大多数时候只是关注结果、是否完成、是否已取消、是否失败等;
比如发起一个网络请求,我们需要知道请求是否已经开始、是否收到了数据、已收到多少数据(请求进度)、数据是否已接受完毕、数据是否接收失败等,这时候还是用delegate比较好;
(3) block的代码相比于delegete显得更加的紧凑、实现方式更简练,不需要写protocol、函数等等;
(4) 使用block的时候需要更加注意防止循环引用的发生;
我们来聊一个老生常谈的话题-循环引用
关于什么是循环引用以及如何去避免发生循环引用本篇不再赘述、下面我们来看一段代码:
// ViewController:
- (void)blockAction {
self.obj = [[TestObject alloc] init];
[self.obj testMethodSuccess:^(NSDictionary *info) {
[self methodAction];
} failed:^(NSError* error) {
NSLog(@" error : %@", error.description);
}];
}
- (void)methodAction {
}
// TestObject:
@implementation TestObject
- (void)testMethodSuccess:(_Nullable TestObjectSuccessBlock)successBlock
failed:(_Nullable TestObjectFailedBlock)failedBlock {
[self doSomething];
NSLog(@"successBlock: %@",successBlock);
if (successBlock) {
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:@"success", @"info", nil];
successBlock(info);
}
}
- (void)doSomething {
}
@end
// Log:
AveryTest[15194:2053884] successBlock: <__NSStackBlock__: 0x7ffedfcea958>
如上所示上面的这段中在self.obj的实例方法中的block{}内部强引用了self、那么这段代码会引起循环引用吗?其实并不会引起循环引用的,因为successBlock是一个NSStackBlock出了作用域之后就被释放了,虽然在block{}内部会使self的retainCount加1、但是出了作用域之后位于栈上的block{}就会被回收、self的retainCount也就会减1,所以在block{}前后self的retainCount始终保持一致、也就不会引起循环引用。
所以说不要一见到类似于"[self.obj method:^{[self otherAction];}];"这样的代码就觉得会引起循环引用,还需要确认下block{}是否被copy (如果在TestObject中对successBlock进行了copy操作、那么这么写就会引起循环引用)。
【结束语】那么到此、你搞明白了Block是怎么回事没有
如下所示的代码:
block{}内部中的where_Obj是__Block_byref_testObj_0类型的还是TestObject类型的?
block{}内部中的where_Obj保存在栈上还是堆上?
block{}内部中的where_Obj的生命周期是谁来管理的?
- (void)testBlockAction {
__block TestObject *testObj = [TestObject new];
void (^averyBlock) (void);
averyBlock = ^(void) {
TestObject *where_Obj = testObj; // Who am I? where am I?
};
averyBlock();
}
下面的代码中的number_b的值是多少?
NSInteger number_a = 1;
void (^averyBlock)(void) = ^{
NSInteger number_b = number_a + 10;
NSLog(@"number_b: %ld",number_b);
};
number_a = 10;
averyBlock();
下面的代码中的number_c的值是多少?
static NSInteger number_a = 1;
void (^averyBlock)(void) = ^{
NSInteger number_c = number_a + 10;
NSLog(@"number_c: %ld",number_c);
};
number_a = 10;
averyBlock();
下面两组代码的输出和你想象的一样吗?
typedef void (^AveryBlock)(void);
NSString *string = @"avery_default";
AveryBlock block = ^(void){
// 在非主队列中执行主线程上的调用
dispatch_sync(dispatch_queue_create("com.avery.queue", DISPATCH_QUEUE_CONCURRENT), ^{
NSLog(@"string: %@",string);
NSLog(@"currentThread: %@",[NSThread currentThread]);
});
};
string = @"avery_update";
block();
typedef void (^AveryBlock)(void);
NSMutableString *string = [[NSMutableString alloc] initWithString:@"avery_default"];
AveryBlock block = ^(void) {
// 在非主队列中执行主线程上的调用
dispatch_sync(dispatch_queue_create("com.avery.queue", DISPATCH_QUEUE_CONCURRENT), ^{
[string appendString:@"world"];
NSLog(@"string: %@",string);
NSLog(@"currentThread: %@",[NSThread currentThread]);
});
};
[string appendString:@"hello"];
block();
下面的两组代码中为什么第一组可以正常执行、而第二组就编译不通过呢?
NSMutableString *string = [[NSMutableString alloc] initWithString:@"Avery"];
void (^averyBlock) (void);
averyBlock = ^(void) {
[string appendString:@"_append"];
};
averyBlock();
NSMutableString *string = [[NSMutableString alloc] initWithString:@"Avery"];
void (^averyBlock) (void);
averyBlock = ^(void) {
string = @"Avery_update";
};
averyBlock();