深入理解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 = 0;
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

转载于:https://www.cnblogs.com/FBiOSBlog/p/6667371.html

iOS开发 - OC - block的详解 - 基础篇相关推荐

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

    一. 概要 在 iOS 设备中,照片和视频是相当重要的一部分.最近刚好在制作一个自定义的 iOS 图片选择器,顺便整理一下 iOS 中对照片框架的使用方法.在 iOS 8 出现之前,开发者只能使用 A ...

  2. IOS开发学习笔记-----UILabel 详解

    IOS开发学习笔记-----UILabel 详解 01 //创建uilabel 02 UILabel *label1 = [[UILabel alloc] initWithFrame:CGRectMa ...

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

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

  4. Openharmony应用NAPI详解--基础篇

    NAPI是什么? 简单点理解就是在Openharmony里,实现上层js或ets应用与底层C/C++之间交互的框架. Openharmony里的官方解释:NAPI(Native API)组件是一套对外 ...

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

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

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

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

  7. 物联网之MQTT协议详解基础篇(一)—— MQTT介绍

    MQTT: The Standard for IoT Messaging 开场白 大概是这么个背景 比如小米智能家居目前是这么个流程: 物联网设备通过家里的wifi连接到外网服务器 在外网通过米家ap ...

  8. ios开发---URL Schemes 使用详解-app协议

    用原生 iOS 的人分两种,懂 URL Schemes 的和不懂的. 前者是「魔法师」,后者是「麻瓜」. URL Schemes 应用在 iOS 上已经很久了.对于使用者来说,在沙盒机制下的 iOS ...

  9. 【iOS开发必收藏】详解iOS应用程序内使用IAP/StoreKit付费、沙盒(SandBox)测试、创建测试账号流程!2012-6-25日更新iap恢复

    转载自:http://www.himigame.com/iphone-cocos2d/550.html 本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原 ...

最新文章

  1. 玩“剪刀石头布“的脑机!密歇根大学开发由大脑意识精密控制的假肢
  2. 普及组模板——线性筛素数
  3. vuex第三弹vuex之actions(前端网备份)
  4. 安装vmware 6.52 Red Hat Enterprise Linux 5(rhel-5.1-server-i386-dvd) openldap2.4
  5. 全国计算机等级考试题库二级C操作题100套(第92套)
  6. android开发蓝牙自动连接电脑上,Android蓝牙开发之自动连接设备
  7. Go语言之父带你重新认识字符串、字节、rune和字符
  8. 利用TICK搭建Docker容器可视化监控中心
  9. 使用PL/SQL对表进行解锁
  10. 黑马程序员——String类总结
  11. MATLAB R2013 a版及序列号
  12. 【Multisim仿真】用555定时器+CD4017实现流水灯
  13. spring-security实现权限管理
  14. 中国大陆手机号码如何注册谷歌账号?完美解决收不到验证码的问题
  15. Linux 平台上的软件包管理
  16. 基于mysql的应用程序设计[j] 兰旭辉_熊家军_邓刚_高校C语言自动考试系统参考文献...
  17. 4月5号-4月11号
  18. Java学习——逻辑运算、双分支结构(if\else)
  19. 3999美元掀价格战,Velodyne宣布16线激光雷达降价50%
  20. acrh17华硕固件_华硕路由器Asus RT-ACRH17 OpenWrt,刷openwrt教程

热门文章

  1. python函数做菜单_PYTHON图形化操作界面的编程七__创建菜单
  2. 吴裕雄--天生自然 人工智能机器学习实战代码:线性判断分析LINEARDISCRIMINANTANALYSIS...
  3. 关于引用与指针实现多态的一些记录
  4. UWP 查找模板中的控件
  5. linux高级编程补充知识
  6. 转载---虚拟机类加载机制
  7. DWZ+Uploadify +JSON 多文件上传
  8. h5实现手机端等级进度条
  9. 解决XP系统启动慢的问题
  10. CentOS设置服务开机启动的方法