《编写高质量OC代码》已顺利完成一二三四五六七八篇!
附上连接:
iOS 编写高质量Objective-C代码(一)—— 简介
iOS 编写高质量Objective-C代码(二)—— 面向对象
iOS 编写高质量Objective-C代码(三)—— 接口和API设计
iOS 编写高质量Objective-C代码(四)—— 协议与分类
iOS 编写高质量Objective-C代码(五)—— 内存管理机制
iOS 编写高质量Objective-C代码(六)—— block专栏
iOS 编写高质量Objective-C代码(七)—— GCD专栏
iOS 编写高质量Objective-C代码(八)—— 系统框架bash
本篇的主题是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被分红三种类型:async
类型 | 内存位置 | 介绍 |
---|---|---|
__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
修饰。不然修改不了。工具
咱们来看下打印:url
什么?竟然是
__NSMallocBlock__
(堆块)? 那是由于ARC环境下,编译器自动帮咱们加了copy操做。spa
这时咱们关掉ARC:设置Objective-C Automatic Reference Counting = NO
。再来看下打印:
经过上面的例子: 在ARC下,block会默认加上copy操做:变成
__NSMallocBlock__
。
例如:
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);
}];
复制代码
另外,内存泄漏检测相关详情请看:iOS 内存泄漏排查方法及缘由分析