本文共 3652 字,大约阅读时间需要 12 分钟。
《编写高质量OC代码》已经顺利完成一二三四五六七篇!
附上链接:
本篇的主题是iOS中的 “Block的原理及应用”。
先简单介绍一下今天的主角:block
。
通过clang命令行工具(OC转C++),我们先来看一下block
的内部数据结构大概是什么样子的?
struct Block_descriptor { unsigned long int reserved; unsigned long int size; void (*copy)(void *dst, void *src); void (*dispose)(void *);};struct Block_layout { void *isa; int flags; int reserved; void (*invoke)(void *, ...); struct Block_descriptor *descriptor; /* Imported variables. */};
解析:很显然,Block_layout是一个结构体:里面有一个isa指针,指向Class对象。还有一个函数指针,指向了块的实现代码。
根据block在内存中的位置,block被分成三种类型:
类型 | 内存位置 | 介绍 |
---|---|---|
__NSStackBlock__ | 栈区 | 栈内有效,出栈后销毁。 |
__NSMallocBlock__ | 堆区 | copy到堆空间上。可以在定义的那个范围之外使用。 |
__NSGlobalBlock__ | 全局区 | 不捕捉任何外部变量,全部信息在编译器就已确定。 |
block
以及声明的_block
都会被销毁。例如:
__block NSString *name = @"QiShare";void (^block)(void) = ^{ NSLog(@"%@ is an iOS team which loves to share technology.", name);};NSLog(@"block = %@", block);
小知识点:当block内部需要修改或访问外部变量时,外部变量需要额外用
__block
修饰。否则修改不了。
我们来看下打印:
什么?居然是
__NSMallocBlock__
(堆块)?
那是因为ARC环境下,编译器自动帮我们加了copy操作。
这时我们关掉ARC:设置Objective-C Automatic Reference Counting = NO
。再来看下打印:
通过上面的例子:
在ARC下,block会默认加上copy操作:变成__NSMallocBlock__
。
类似于“单例”,copy是一个空操作。
例如:
void (^qiShare)(void) = ^{ NSLog(@"We love sharing.");};NSLog(@"%@",qiShare);
为了增加代码的可读性 和 可拓展性,
需要为常用的block起个别名。以typedef
为块起别名,也可令块变量用起来更加简单~
- (void)getDataWithToken:(NSString *)token success:(void (^)(id responseDic))success;//! 以上要改成下面这种typedef void (^SuccessBlock)(id responseDic);- (void)getDataWithToken:(NSString *)token success:(SuccessBlock)success;
在我们iOS开发中,经常会异步执行一些任务,等待任务执行结束后再通知对象调用相关方法。
一般有两种做法:NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
举个例子:AFNetworking的API设计及使用就是block回调
- (NSURLSessionDataTask *)POST:(NSString *)URLString parameters:(id)parameters success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure { return [self POST:URLString parameters:parameters progress:nil success:success failure:failure];}
AFHTTPSessionManager *manger =[AFHTTPSessionManager manager]; NSString *urlString = @""; NSMutableDictionary *parameter= @{@"":@"",@"":@""}; [manger POST:urlString parameters:parameter success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { NSLog(@"成功"); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"%@",error); }];}
在我们日常开发中,如果block使用不当,很容易导致内存泄漏。
block
被当前ViewController(self
)持有,这时,如果block内部再持有ViewController(self
),就会造成循环引用。block
外部弱化self
,再在block内部强化已经弱化的weakSelf
For Example:
__weak typeof(self) weakSelf = self;[self.operationQueue addOperationWithBlock:^{ __strong typeof(weakSelf) strongSelf = weakSelf; if (completionHandler) { KTVHCLogDataStorage(@"serial reader async end, %@", request.URLString); completionHandler([strongSelf serialReaderWithRequest:request]); }}];
当然,也不是所有block中使用到self
都要先弱化成weakSelf
,再强化成strongSelf
,
block
没有被self
所持有的,在block
中就可以使用self
。比如下面: [QiNetwork requestBlock:^(id responsObject) { NSLog(@"%@",self.name); }];
另外,内存泄漏检测相关详情请看:
转载地址:http://lwwsx.baihongyu.com/