iOS block从零开始
iOS block从零开始
在iOS4.0之后,block横空出世,它本身封装了一段代码并将这段代码当做变量,通过block()的方式进行回调。
block的结构
先来一段简单的代码看看:
void (^myBlock)(int a) = ^(int a){
NSLog(@"%zd",a);
};
NSLog(@"旭宝爱吃鱼");
myBlock(999);
输出结果:
2016-05-03 11:27:18.571 block[5340:706252] 旭宝爱吃鱼
2016-05-03 11:27:18.571 block[5340:706252] 999
下面我们解析一下:
- void :返回的参数 void为没有返回值
- (^myBlock):myBlock 为 block 的名称
- (int a):此为参数列表
- ^(int a):传入参数
通过上面的简单介绍可以简单了解到block的结构那么下面便产生了四种格式的block。
四种block
有返回值无参:
int (^myBlock)() = ^(){
return 999;
};
有返回值有参:
int (^myBlock)(int a) = ^(int a){
NSLog(@"%zd",a);
return a;
};
无返回值无参:
void (^myBlock)() = ^(){
NSLog(@"旭宝爱吃鱼");
};
无返回值有参:
void (^myBlock)(int a) = ^(int a){
NSLog(@"%zd",a);
};
block 捕获外界局部变量
示例代码:
int a = 10;
void (^myBlock)() = ^(){
NSLog(@"旭宝爱吃鱼");
NSLog(@"%zd",a);
};
NSLog(@"旭宝爱吃鱼");
myBlock();
运行结果:
2016-05-03 11:43:32.680 block[5406:713702] 旭宝爱吃鱼
2016-05-03 11:43:32.681 block[5406:713702] 旭宝爱吃鱼
2016-05-03 11:43:32.681 block[5406:713702] 10
通过示例代码不难发现我们可以获取局部变量,那么改变局部变量是否也会改变block内的值呢。
示例代码:
int a = 10;
void (^myBlock)() = ^(){
NSLog(@"旭宝爱吃鱼");
NSLog(@"%zd",a);
};
a = 20;
NSLog(@"旭宝爱吃鱼");
myBlock();
运行结果:
2016-05-03 11:47:07.669 block[5425:715861] 旭宝爱吃鱼
2016-05-03 11:47:07.670 block[5425:715861] 旭宝爱吃鱼
2016-05-03 11:47:07.670 block[5425:715861] 10
正如结果显示,block内部的值是不会改变的,为什么呢????
示例代码:
int a = 10;
a = 20;
void (^myBlock)() = ^(){
NSLog(@"旭宝爱吃鱼");
NSLog(@"%zd",a);
};
NSLog(@"旭宝爱吃鱼");
myBlock();
运行结果:
2016-05-03 11:49:19.749 block[5450:717309] 旭宝爱吃鱼
2016-05-03 11:49:19.750 block[5450:717309] 旭宝爱吃鱼
2016-05-03 11:49:19.750 block[5450:717309] 20
情理之中的答案。
之所以block内部的值不会改变是因为block copy了局部变量。
这个问题解决后尝试在block中改变局部变量。
很不幸失败了,当我在block内改变外界的局部变量时,报错了。
可视化我想改变外界的局部变量我该怎么办呢???
改变外界的局部变量
示例代码:
__block int a = 10;
void (^myBlock)() = ^(){
NSLog(@"旭宝爱吃鱼");
NSLog(@"%zd",a);
a = 20;
};
NSLog(@"旭宝爱吃鱼");
myBlock();
NSLog(@"%zd",a);
运行结果:
2016-05-03 11:55:02.736 block[5490:721033] 旭宝爱吃鱼
2016-05-03 11:55:02.737 block[5490:721033] 旭宝爱吃鱼
2016-05-03 11:55:02.737 block[5490:721033] 10
2016-05-03 11:55:02.737 block[5490:721033] 20
我们只需要在局部变量前加__block 即可。
循环引用(我在前面的博客有所提及)
block在iOS开发中被视作是对象,因此其生命周期会一直等到持有者的生命周期结束了才会结束。另一方面,由于block捕获变量的机制,使得持有block的对象也可能被block持有,从而形成循环引用,导致两者都不能被释放:
@implementation CXObject
{
void (^_cycleReferenceBlock)(void);
}
- (void)viewDidLoad
{
[super viewDidLoad];
_cycleReferenceBlock = ^{
NSLog(@"%@", self); //引发循环引用
};
}
@end
遇到这种代码编译器只会告诉你存在警告,很多时候我们都是忽略警告的,这最后会导致内存泄露,两者都无法释放。跟普通变量存在block关键字一样的,系统提供给我们weak的关键字用来修饰对象变量,声明这是一个弱引用的对象,从而解决了循环引用的问题:
__weak typeof(*&self) weakSelf = self;
_cycleReferenceBlock = ^{
NSLog(@"%@", weakSelf); //弱指针引用,不会造成循环引用
};
使用block
在block出现之前,开发者实现回调基本都是通过代理的方式进行的。比如负责网络请求的原生类NSURLConnection类,通过多个协议方法实现请求中的事件处理。而在最新的环境下,使用的NSURLSession已经采用block的方式处理任务请求了。各种第三方网络请求框架也都在使用block进行回调处理。这种转变很大一部分原因在于block使用简单,逻辑清晰,灵活等原因。接下来我会完成一次网络请求,然后通过block进行回调处理。这些回调包括请求完成、下载进度
按照returnValue(^blockName)(parameters)的方式进行block的声明未免麻烦了些,我们可以通过关键字typedef来为block起类型名称,然后直接通过类型名进行block的创建:
@interface CXDownloadManager: NSObject
//block重命名
typedef void(^CXDownloadHandler)(NSData * receiveData, NSError * error);
typedef void(^CXDownloadProgressHandler)(CGFloat progress);
- (void)downloadWithURL: (NSString *)URL parameters: (NSDictionary *)parameters handler: (CXDownloadHandler)handler progress: (CXDownloadProgressHandler)progress;
@end
@implementation CXDownloadManager
{
CXDownloadProgressHandler _progress;
}
- (void)downloadWithURL: (NSString *)URL parameters: (NSDictionary *)parameters handler: (CXDownloadHandler)handler progress: (CXDownloadProgressHandler)progress
{
//创建请求对象
NSURLRequest * request = [self postRequestWithURL: URL params: parameters];
NSURLSession * session = [NSURLSession sharedSession];
//执行请求任务
NSURLSessionDataTask * task = [session dataTaskWithRequest: request completionHandler: ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (handler) {
dispatch_async(dispatch_get_main_queue(), ^{
handler(data, error);
});
}
}];
[task resume];
}
//进度协议方法
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten // 每次写入的data字节数
totalBytesWritten:(int64_t)totalBytesWritten // 当前一共写入的data字节数
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite // 期望收到的所有data字节数
{
double downloadProgress = totalBytesWritten / (double)totalBytesExpectedToWrite;
if (_progress) { _progress(downloadProgress); }
}
@end
上面通过封装NSURLSession的请求,传入一个处理请求结果的block对象,就会自动将请求任务放到工作线程中执行实现,我们在网络请求逻辑的代码中调用如下:
#define QQMUSICURL @"https://www.baidu.com/link?url=UTiLwaXdh_-UZG31tkXPU62Jtsg2mSbZgSPSR3ME3YwOBSe97Hw6U6DNceQ2Ln1vXnb2krx0ezIuziBIuL4fWNi3dZ02t2NdN6946XwN0-a&wd=&eqid=ce6864b50004af120000000656fe235f"
[[CXDownloadManager alloc] downloadWithURL: QQMUSICURL parameters: nil handler ^(NSData * receiveData, NSError * error) {
if (error) { NSLog(@"下载失败:%@", error) }
else {
//处理下载数据
}
} progress: ^(CGFloat progress) {
NSLog(@"下载进度%lu%%", progress*100);
}];
总结
block捕获变量、代码传递、代码内联等特性赋予了它多于代理机制的功能和灵活性,尽管它也存在循环引用、不易调试追溯等缺陷,但无可置疑它的优点深受码农们的喜爱。如何更加灵活的使用block需要我们对它不断的使用、探究了解才能完成
iOS block从零开始的更多相关文章
- iOS Block界面反向传值
在上篇博客 <iOS Block简介> 中,侧重解析了 iOS Block的概念等,本文将侧重于它们在开发中的应用. Block是iOS4.0+ 和Mac OS X 10.6+ 引进的对C ...
- iOS block 机制
本文要将block的以下机制,并配合具体代码详细描述: block 与 外部变量 block 的存储域:栈块.堆块.全局块 定义 块与函数类似,只不过是直接定义在另一个函数里,和定义它的那个函数共享同 ...
- ios Block详细用法
ios Block详细用法 ios4.0系统已开始支持block,在编程过程中,blocks被Obj-C看成是对象,它封装了一段代码,这段代码可以在任何时候执行.Blocks可以作为函数参数或者函数的 ...
- iOS Block的本质(四)
iOS Block的本质(四) 上一篇文章iOS Block的本质(三)中已经介绍过block变量的捕获,本文继续探寻block的本质. 1. block内修改变量的值 int main(int ar ...
- # iOS Block的本质(三)
iOS Block的本质(三) 上一篇文章iOS Block的本质(二)中已经介绍过block变量的捕获,本文继续探寻block的本质. 1. block对对象变量的捕获,ARC 环境 block一般 ...
- iOS Block的本质(二)
iOS Block的本质(二) 1. 介绍引入block本质 通过上一篇文章Block的本质(一)已经基本对block的底层结构有了基本的认识,block的底层就是__main_block_impl_ ...
- iOS Block的本质(一)
iOS Block的本质(一) 1.对block有一个基本的认识 block本质上也是一个oc对象,他内部也有一个isa指针.block是封装了函数调用以及函数调用环境的OC对象. 2.探寻block ...
- (译)IOS block编程指南 1 介绍
Introduction(介绍) Block objects are a C-level syntactic and runtime feature. They are similar to stan ...
- [IOS Block和delegate的对比]
原文:http://blog.sina.com.cn/s/blog_9e8867eb0102uykn.html 这篇文章建议和前一篇一起看, 另外先弄清楚IOS的block是神马东东. 委托和bloc ...
随机推荐
- for of 与 for in的区别
遍历数组通常使用for循环,ES5的话也可以使用forEach,ES5具有遍历数组功能的还有map.filter.some.every.reduce.reduceRight等,只不过他们的返回结果不一 ...
- a[href$=".pdf"]解释
看过书上的解释,其中$的意思其实是ends with的意思,解释起来就是说选择所有链接指向PDF文件的链接标签,当然还可以是其他类型的文件(.mp4,.doc,.mp4): 当然,这个需要你的浏览器支 ...
- [水煮 ASP.NET Web API2 方法论](3-7)默认 Action 请求方式以及 NonActionAttribute
问题 在 Controller 中有一个 public 的方法,但是又不想将这个 publlic 方法暴露成为一个 API. 解决方案 ASP.NET Web API 中,正常是通过 HTTP 谓词来 ...
- mysql之TIMESTAMP(时间戳)用法详解
一.TIMESTAMP的变体 TIMESTAMP时间戳在创建的时候可以有多重不同的特性,如: 1.在创建新记录和修改现有记录的时候都对这个数据列刷新: ? 1 TIMESTAMP DEFAULT CU ...
- Verilog学习笔记设计和验证篇(四)...............状态机的置位与复位
1)状态机的异步置位和复位 异步置位与复位是于时钟无关的.当异步置位或复位信号来临时,他们立即分别置触发器的输出为1或0,不需要等待时钟沿的到来.要将他们列入always块的事件控制信号内就能触发al ...
- SpringFramework的简介
一.前言 Spring提供了一种轻量级的解决方案,用于建立"快装式企业应用".在此基础上,Spring还提供了包括声明式事务管理,RMI或Web Services远程访问业务逻辑, ...
- PDF.NET SOD 开源框架红包派送活动 && 新手快速入门指引
一.框架的由来 快速入门 有关框架的更多信息,请看框架官方主页! 本套框架的思想是借鉴Java平台的Hibernate 和 iBatis 而来,兼有ORM和SQL-MAP的特性,同时还参考了后来.N ...
- SQL Server 跨库同步数据
最近有个需求是要跨库进行数据同步,两个数据库分布在两台物理计算机上,自动定期同步可以通过SQL Server代理作业来实现,但是前提是需要编写一个存储过程来实现同步逻辑处理.这里的存储过程用的不是op ...
- 安装配置php5.4 win2003
php php-5.4.38 1.下载 http://windows.php.net/downloads/releases/php-5.4.38-Win32-VC9-x86.zip 解压到 D:\we ...
- vue安装
条件:已安装 node&npm 1.安装 cnpm : $ npm install -g cnpm --registry=https://regis ...