深入理解oc中的block

苹果在Mac OS X10.6 和iOS 4之后引入了block语法。这一举动对于许多OC使用者的编码风格改变很大。就我本人而言,感觉block用起来还是很爽的,但一直以来,都是知其然,而不知所以然。这篇文章一共有两篇,其中基础篇讲解了block的基本的使用和创建,以及一些注意事项。在深入篇中,我将会对block的一些原理陈述出来,探讨block的内部。

基础篇

1.block是什么

首先,对于Block,我在苹果的文档中找到以下描述:

Block objects are a C-level language construct that you can incorporate into your C and Objective-C code. A block object is essentially an anonymous function and the data that goes with that function, something which in other languages is sometimes called a closure or lambda. Blocks are particularly useful as callbacks or in places where you need a way of easily combining both the code to be executed and the associated data. 

 block对象是一个C语言结构体,可以并入到C和Objective-C代码中。Block对象本质上是一个匿名函数,以及与该函数一起使用的数据,其他语言中有时称为闭包或lambda。 Block特别适用于回调,或者是在你为了让代码看起来具有更清晰的逻辑进行代码的组合时使用。

上面的解释,告诉我们: 首先,block是一个OC中的对象;并且,这个对象时一个C语言的结构体,它可以使用在C语言和OC中;同时,block本质上是一个匿名函数和其包含的数据集中。这应该就是block的定义了。之前经常在一些博客上看到,将block描述成一个结构体,或者是一个匿名函数,抑或是直接说成一个对象。这些描述都有道理,但是并不全面。(在第二部分,我会详细说明为什么)

2.为什么要使用blobk

苹果的文档中还描述:

In iOS, blocks are commonly used in the following scenarios:

    • As a replacement for delegates and delegate methods

    • As a replacement for callback functions

    • To implement completion handlers for one-time operations

    • To facilitate performing a task on all the items in a collection

    • Together with dispatch queues, to perform asynchronous tasks

在iOS中,在以下情况下通常使用block:

    • 代替代理和委托方法
    • 作为回调函数的替代
    • 一次性编写多次处理的任务
    • 方便对集合中的所有项执行任务
    • 与GCD队列一起执行异步任务

这里告诉我们在以上情况下,我们都能够使用block。我感受最深刻的是使用block进行回调。很多情况下,我们可能只需要对某个事件进行一个单一的回调,也许仅仅就一次,如果我使用代理的话,我需要创建类、编写协议,仅仅对于一个小地方的回调成本很高,那么block登场就恰到好处了。 除此之外,block的特性可以让代理集中在某处,我们只需要在一个地方就可以完成回调之前和回调时的代码,相比,使用回调函数和代理都没有这个优势!另外,我们可以想到,OC中封装好了一些集合的方法,比如,数组的排序,仔细会发现,这里就使用block进行回到操作的。

3.block怎么使用

首先我们看看怎么创建一个block吧。

    //这样创建 相当平常的block 有参数,有返回值
//申明一个block 名字叫oneFrom ,右边的float类型是参数类型,左边的float是返回值的类型。 当这两者中的任意一个为空的时候都可使用void代替
float (^oneFrom)(float); oneFrom = ^(float aFloat) {
float result = aFloat - 1.0;
NSLog(@"调用时"); return result;
};

如果我们需要创建一个全局block(进阶篇将会细说,全局block和其他的block的区别),应该像这样 :

#import <stdio.h>

int GlobalInt = ;
int (^getGlobalInt)(void) = ^{ return GlobalInt; }; //这种方式将申明和实现一起写

当我们调用的时候,我们应当这样做:

    //这样创建 相当平常的block 有参数,有返回值
//申明一个block 名字叫oneFrom ,右边的float类型是参数类型,左边的float是返回值的类型。 当这两者中的任意一个为空的时候都可使用void代替
float (^oneFrom)(float); oneFrom = ^(float aFloat) {
float result = aFloat - 1.0;
NSLog(@"调用时"); return result;
};
//调用
float par = 5.0;
NSLog(@"调用之前源对象:%f",par);
float result = oneFrom(par);
NSLog(@"调用的处理结果:%f",result);
NSLog(@"调用之后源对象:%f",par);

运行结果:

为了更加清楚的体现block的回调效果,我特意进行了打印。可以发现,尽管block的代码早就声明了,但是并没有立刻调用,而是在block调用的时候才被执行。 相信通过这里,对于block的回调应该有一定的理解了。

也许,这样用起来,不觉得太简单了嘛?根本就没什么卵用啊! 别急,再看看另外两种block的使用。之前说了,block时OC中的一个对象,既然是对象,我们就可以把它当作一个类的属性咯,应该是也可以很其他属性一样,被当作一个方法的参数吧。没错,是这样的,相信block之所以被大家认可也就在于这里吧。  看看这个例子。

假设 我有一个这样的类:包含一个block属性,testBlock。包含一个调用自己的block 属性的方法- blockDo.

我在另一个地方实现这样的代码

#import "ViewController.h"

@interface Computer ()

@property (nonatomic,copy) NSString* (^testBlock)(NSString*) ;  //将一个block作为 computer 的属性

- (void)blockDo;

@end

@implementation Computer

- (void)blockDo{

    NSString *testStr = @"testData_Old";
if(self.testBlock){ NSLog(@"%@",self.testBlock(testStr)); //调用并打印
}
}
@end

在另一个地方写下这些代码:

    Computer* computer = [[Computer alloc]init];
computer.testBlock = ^(NSString *parStr){ NSLog(@"%@",parStr);
parStr = @"testData_New";
return parStr;
};
[computer blockDo]; //执行block

运行结果:

仔细看看,这里的调用就比之前复杂了。 因为我给它添加了一个方法,并且将block自身的调用交给了Computer,我只是实现了block而已,最后启动调用他的方法。 是的,我在另一个地方对Computer类模拟了一个方法,这个方法没有在Computer类中实现,我甚至可以在任何地方去实现它,而最后我又可以在其他的地方调用,但是它确实具备了一些功能。 这就是block的神奇的地方。 或许这样你还是觉得它不过如此。

再来看一个:

#import "ViewController.h"

@interface Computer ()

- (void)doSomethingFeedBack:(NSString*(^)(NSString*))handle;

@end

@implementation Computer

- (void)doSomethingFeedBack:(NSString*(^)(NSString*))handle{

    NSString *handleStr = @"Old";
sleep(3.0);
NSLog(@"%@",handle(handleStr));
}
@end

其他地方调用:

   [computer doSomethingFeedBack:^NSString *(NSString *parStr) {
NSLog(@"%@",parStr);
NSString *returnStr = [[NSString alloc]initWithFormat:@"add %@",parStr];
return returnStr;
}];

打印结果:

这里将block作为一个参数放在 doSomethingFeedBack 函数面。体现了block 的对象性质,相比之下,代码量很是简洁。这种实现回调的方式的逻辑更加清晰,明朗。

上面三个例子,展示block的三种不同的使用方式。它们分别是:

    • 将block定义成变量
    • 将block定义成属性
    • 将block作为参数

4.block的使用注意点    __block和__week

block使用的过程中,并没有需要特别注意的地方,只需要注意两个关键词。__block和__weak修饰词。

我在讲解创建第一种block的时候,运行程序打印的结果看到一个现象,栈block(后续会解释什么是栈block,这里主要区分全局bolck)虽然拿到了外部变量,但是对与变量的修改确实没有效果的———前后的源数据的打印值没有发生改变。如果我们需要在block中对外部的变量进行修改,应该在这个变量之前加上__block修饰,原因在下一品文章中详解。

__weak关键词用于解决使用block导致强引用循环的问题。block在使用的过程中,当block属于某个对象,如果在block函数中,又包含了这个对象,或者包含其属性,都会因为block持有对象,对象又持有block导致对象得不到释放的情况。 这是只需要在申请一个用 weak修饰的对象替代源对象,将引用循环打断,保证正常释放,具体的内部原因在下篇中将会详细介绍。

5.总结:

通过上面演示block的用法发现,block每次的回调是通过它的匿名函数来进行的,也就是每一次最多执行一个回调,在需要进行大批量的回调的时候,就需要写很多不同的block回调,这样的方式显然是不合适的,相比之下,这是时候使用协议和代理的方式就自然多了。除此之外,block还比较适合用在线程之间的切换回调。GCD就是采用了多线程结合block来做的。在多数情况下,应当充分考虑block的可以携带环境的优点使代码的逻辑更加的清晰。

基础篇简单的介绍下block的使用。并没有深入研究其内部细节,在接下来的深入篇中,我将介绍:

  • 为什么说block是一个结构体,也是一个对象,同时还是携带数据的匿名函数
  • 全局block ,栈block以及堆区block的区别和他们之间的联系,探究block的内存管理
  • 为什么使用__block 就可以使得block可以修改外部变量
  • 引起强引用循环的原因是什么,我们解决它的方法和原理又是什么

2017-04-01   18:54:21

iOS开发 - OC - block的详解 - 基础篇的更多相关文章

  1. iOS 开发之照片框架详解(2)

    一. 概况 本文接着 iOS 开发之照片框架详解,侧重介绍在前文中简单介绍过的 PhotoKit 及其与 ALAssetLibrary 的差异,以及如何基于 PhotoKit 与 AlAssetLib ...

  2. iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)

    本文链接:http://kayosite.com/ios-development-and-detail-of-photo-framework-part-three.html 这里接着前文<iOS ...

  3. iOS 开发之照片框架详解

    转载自:http://kayosite.com/ios-development-and-detail-of-photo-framework.html 一. 概要 在 iOS 设备中,照片和视频是相当重 ...

  4. iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)

    转载自:http://kayosite.com/ios-development-and-detail-of-photo-framework-part-two.html 一. 概况 本文接着 iOS 开 ...

  5. bt协议详解 基础篇(下)

    bt协议详解 基础篇(下) 最近开发了一个免费教程的网站,产生了仔细了解bt协议的想法,所以写了这一篇文章,后续还会写一些关于搜索和索引的东西,都是在开发这个网站的过程中学习到的技术,敬请期待. 1 ...

  6. bt协议详解 基础篇(上)

    bt协议详解 基础篇(上) 最近开发了一个免费教程的网站,产生了仔细了解bt协议的想法,所以写了这一篇文章,后续还会写一些关于搜索和索引的东西,都是在开发这个网站的过程中学习到的技术,敬请期待. 1 ...

  7. iOS开发 - OC - block的详解 - 深入篇

    深入理解oc中的block 苹果在Mac OS X10.6 和iOS 4之后引入了block语法.这一举动对于许多OC使用者的编码风格改变很大.就我本人而言,感觉block用起来还是很爽的,但一直以来 ...

  8. IOS开发中单例模式使用详解

    第一.基本概念 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问. 第二.在IOS中使用单例模式的情 ...

  9. 【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!【2012-12-11日更新获取”产品付费数量等于0的问题”】

    转的别人的 看到很多童鞋问到,为什么每次都返回数量等于0?? 其实有童鞋已经找到原因了,原因是你在 ItunesConnect 里的 “Contracts, Tax, and Banking”没有完成 ...

随机推荐

  1. IOS获取手机设备所有应用

    //返回一个数组 1 NSMutableArray *applist = [[NSMutableArray alloc]init]; NSString *pathOfApplications = @& ...

  2. HDU - 4552 怪盗基德的挑战书 (后缀数组)

    Description "在树最漂亮的那天,当时间老人再次把大钟平均分开时,我会降临在灯火之城的金字塔前.带走那最珍贵的笑容."这是怪盗基德盗取巴黎卢浮宫的<蒙娜丽莎的微笑& ...

  3. swift开发多线程篇 - NSThread 线程相关简单说明(一些使用和注意点)

    一 说明 本文涉及代码可以从https://github.com/HanGangAndHanMeimei/Code地址获得. 二 NSThread的基本使用和创建 1)基本用法(主线程|当前线程) 1 ...

  4. php课程 8-32 如何使用gd库进行图片裁剪和缩放

    php课程 8-32 如何使用gd库进行图片裁剪和缩放 一.总结 一句话总结:图片缩放到图片裁剪就是改变原图截取的位置以及截取的宽高. 1.电商网站那么多的图片,如果全部加载卡得慢的很,所以他们是怎么 ...

  5. java学习顺序

    作者:阿弎 链接:http://www.zhihu.com/question/19851109/answer/91201815 来源:知乎 著作权归作者所有,转载请联系作者获得授权. ======== ...

  6. php curl header头

    工作中第一次用到header做个记录 工作中需要在heaer里面加上 Authorization 用来验证身份 public function index() { $url = "http: ...

  7. (十二)RabbitMQ消息队列-性能测试

    原文:(十二)RabbitMQ消息队列-性能测试 硬件配置 宿主机用的联想3850X6的服务器四颗E7-4850v3的处理器,DDR4内存,两块1.25TB的pcie固态.在宿主机上使用的事esxi5 ...

  8. eclipse设置jvm

    设置参数: -Xverify:none -Xms512m -Xmx512m -Xmn128m -XX:PermSize=96m -XX:MaxPermSize=96m -XX:+UseConcMark ...

  9. 一个Exception catch不住的【异常】

    现象: Controller中的方法接收到前台ajax请求后开始执行, 当执行到某一行时程序终止,前台回调进Error方法. 后台明明catch了Exception异常,但异常并没有被捕获(不进cat ...

  10. jquery ajax跨域 thinkphp getjson

    jquery中post的应该是不能跨域,网上说get的可以跨域,但是我试了一下也不行,然后就进行最后的拼搏getjson,结果成功,哈哈 js处写作 $.getJSON( "/index.p ...