iOS学习笔记之Block
写在前面
学习iOS开发的过程中,在很多场合都遇到了Block。说实话,虽然自己依葫芦画瓢的将Block“拿来”用着,但这种“拿来主义”与学习时应持有的探索精神是背道而驰的。所以还是决定花些时间整理一下这个知识点。总的来说,我感觉Block的行为还是很复杂的,本文中的整理也不一定正确,欢迎指正。
Block简介
Block是一种特殊的数据类型,它本质上就是一个代码片段,跟普通的OC变量相似,它可以被声明、赋值和传递。
下面就Block的各种使用形式做一个我理解到的归纳。
Block作为局部变量
Block格式
Block基本声明格式为:
返回值类型(^block名称)(参数列表);
例如:
int (^squareBlock)(int);
上述代码声明了一个返回值和参数类型都为int的,名为squareBlock的Block变量,其中^符号就是Block变量的标识。(如同*是指针的标识)
Block的赋值:
下面的代码演示了为squareBlock变量赋值的过程
squareBlock = ^(int a){
int result = a*a;
return result;
};
我们也可以在Block声明的时候直接赋值,例如:
int (^squareBlock)(int) = ^(int a){
return a*a;
};
当Block的返回值和参数列表为空时,在赋值阶段可以省略为空的部分,如:
void (^blankBlock)(void);
上述blankBlock的赋值可以有以下几种等价形式:
blankBlock = ^void(void){};
blankBlock = ^ (void){};
blankBlock = ^void() {};
blankBlock = ^void {};
blankBlock = ^ {};
上述代码的空格在实际应用时应该去掉,之所以保留是为了便于学习时理解记忆。
Block调用:
我们以squareBlock为例,其调用格式为:
int result = squareBlock(4);
NSLog(@"result is : %d", result);
可以看到,squareBlock的调用方式和C++中函数的调用方式基本相同。
Block与typedef
前面我们说过,Block与变量相似,因此它也可以被传递。由于Block的写法相对比较繁琐,当它作为函数参数时,我们一般会用typedef为其定义有意义的名字,方便书写和阅读。其定义格式为:
typedef <#returnType#>(^<#name#>)(<#arguments#>);
仍然以squareBlock为例:
typedef int(^squareBlock)(int);
此时squareBlock被声明为了一个Block类型(类比int类型),其使用如下:
//类比 int num = 10;
//类型 变量名 值
squareBlock mySquareBlock = ^int (int a)
{
return a*a;
};
int result = mySquareBlock(4);
NSLog(@"result is : %d", result);
Block作为属性
在下面的例子中,我们定义了一个Aclass类,在这个类中添加属性
Block作为属性时,其声明如下:
@interface Aclass : NSObject
//block声明为属性,用copy关键字
@property (copy, nonatomic) void(^logBlock)();
@end
在类中的初始化和调用
@implementation Aclass
- (id)initWithBlock:(void (^)())logblock
{
self = [super init];
if (self)
{
_logBlock = logblock;
}
return self;
}
- (void)print
{
self.logBlock();
}
@end
在main函数中的赋值:
Aclass *objA = [[Aclass alloc]initWithBlock:^{
NSLog(@"this string is passed from main.");
}];
[objA print];
执行截图:

其用法和普通属性相似,只是其调用是由Block名+参数列表一起出现的(跟C++中的函数类似)。
当Block作为属性时,一般会先用typedef给Block一个名称,然后在类的属性中定义,如下:
typedef void(^printBlock)(NSString *);
@interface Aclass : NSObject
//block声明为属性,用copy关键字
@property (copy, nonatomic)printBlock myPrintBlock;
@end
Block作为函数参数
在Aclass中添加带有Block参数的方法,如下:
- (void)printWithBlock:(void(^)())printblock;
其函数内容如下:
- (void)printWithBlock:(void(^)())printblock
{
if (printblock)
{
printblock();
}
}
在main函数中创建Aclass的对象objA,并调用有Block参数的printWithBlock:方法,如下:
Aclass *objA = [[Aclass alloc]init];
[objA printWithBlock:^{
NSLog(@"this string is typed in main.");
}];
执行截图:

在这个例子中,main函数是调用方,它调用了Aclass的printWithBlock:方法并传入Block代码段供其执行。这里的Block已经是一种回调的用法了。
Block作为回调
为了说明Block的回调,我们创建Aclass和Bclass两个类,在Aclass中提供一个含有Block参数的方法,在Bclass中调用上述方法,代码如下:
1、Aclass.h
@interface Aclass : NSObject
- (void)funInAclass:(void(^)(NSString *))callback;
@end
2、Aclass.m
- (void)funInAclass:(void(^)(NSString *))callback
{
NSLog(@"funInAclass start.");
NSString *strA = @"strA";
if (callback)
{
callback(strA);
}
NSLog(@"funInAclass end.");
}
3、Bclass.h
@interface Bclass : NSObject
- (void)funInBclass;
@end
4、Bclass.m
@implementation Bclass
- (void)funInBclass
{
NSLog(@"funInBclass start");
Aclass *objA = [[Aclass alloc]init];
[objA funInAclass:^(NSString *str) {
NSLog(@"In funInBclass's block");
NSLog(@"str is: %@",str);
}];
NSLog(@"funInBclass end.");
}
@end
5、main函数
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Bclass *objB = [[Bclass alloc] init];
[objB funInBclass];
}
return 0;
}
6、执行结果

Block回调的理解
从上面的这个例子可以看出,funInAclass:方法接收一个Block类型的参数,在Bclass的funInBcalss:中调用Aclass的funInAclass:方法时,传入了Block的具体内容。
在funInAclass中,执行到if语句时,判断自身的callback参数是否有值,如果有就执行callback这个Block,因此会执行funInBclass中的那段Block代码。也就是说,funInBclass调用了funInAclass,而funInBclass又通过Block执行了funInBclass中的代码(funInAclass执行了funInBclass传递给他的代码),这就是Block回调。
Block的类型
Block有三种常见类型:
_NSConcreteGlobalBlock:全局静态Block,不会访问外部变量,不涉及任何拷贝
_NSConcreteStackBlock:保持在栈中,函数返回时被销毁;
_NSConcreteMallocBlock:保存在堆中,当引用计数为0时被销毁
总结
在调用Block时,需要对Block做非空判断,否则会崩溃;
用Block实现回调的好处是代码直观,在调用函数时就可以直接写后续的执行代码,而不需要向delegate那样换到另一个地方写回调函数。(例如上面的例子中,funInBclass在调用funInAclass时就直接把回调代码写在了调用的地方,方面阅读);
使用Block可能会造成循环引用;
iOS学习笔记之Block的更多相关文章
- ios学习笔记之block在ios开发中的应用
一.什么是Blocks Block是一个C级别的语法以及运行时的一个特性,和标准C中的函数(函数指针)类似,但是其运行需要编译器和运行时支持,从ios4.0开始就很好的支持Block. 二. ...
- iOS学习笔记之ARC内存管理
iOS学习笔记之ARC内存管理 写在前面 ARC(Automatic Reference Counting),自动引用计数,是iOS中采用的一种内存管理方式. 指针变量与对象所有权 指针变量暗含了对其 ...
- IOS学习笔记48--一些常见的IOS知识点+面试题
IOS学习笔记48--一些常见的IOS知识点+面试题 1.堆和栈什么区别? 答:管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制:对于堆来说,释放工作由程序员控制,容易产生memor ...
- iOS学习笔记-自己动手写RESideMenu
代码地址如下:http://www.demodashi.com/demo/11683.html 很多app都实现了类似RESideMenu的效果,RESideMenu是Github上面一个stars数 ...
- iOS学习笔记——AutoLayout的约束
iOS学习笔记——AutoLayout约束 之前在开发iOS app时一直以为苹果的布局是绝对布局,在IB中拖拉控件运行或者直接使用代码去调整控件都会发上一些不尽人意的结果,后来发现iOS在引入了Au ...
- IOS学习笔记25—HTTP操作之ASIHTTPRequest
IOS学习笔记25—HTTP操作之ASIHTTPRequest 分类: iOS2012-08-12 10:04 7734人阅读 评论(3) 收藏 举报 iosios5网络wrapper框架新浪微博 A ...
- IOS学习笔记之关键词@dynamic
IOS学习笔记之关键词@dynamic @dynamic这个关键词,通常是用不到的. 它与@synthesize的区别在于: 使用@synthesize编译器会确实的产生getter和setter方法 ...
- iOS学习笔记-精华整理
iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始 ...
- iOS学习笔记10-UIView动画
上次学习了iOS学习笔记09-核心动画CoreAnimation,这次继续学习动画,上次使用的CoreAnimation很多人感觉使用起来很繁琐,有没有更加方便的动画效果实现呢?答案是有的,那就是UI ...
随机推荐
- 虚拟化技术之KVM
虚拟化技术之KVM 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是虚拟化 其实虚拟化技术已经不是一个新技术了,上个世纪六十年代IBM公司已经在使用,只不过后来(上个世纪八 ...
- lombok 工具类的介绍
lombok 是一个非常非常好用的工具类.打个比方,一个bean,需要字段,get set方法 无参有参构造器,重写equals和hashcode,字段一多很麻烦.它,就是来解决这个问题的.一个注解全 ...
- reids的主从复制配置
本博来简单介绍一下redsi的主从配置.意思就是说有一台主redis,一个从redis,你向主的redis里面写一个数据,然后会同步到从redis里面.注意:从redis 不能写入数据.所以当我们用到 ...
- 运用Zabbix实现内网服务器状态及局域网状况监控(5) —— Zabbix监控路由器
1. 首先在zabbix服务器端安装snmp工具 [root@zabbix ~]# yum -y install net-snmp-utils net-snmp-libs net-snmp-devel ...
- Ubuntu(16.04.2)学习笔记(一)如何解决dpkg: error processing install-info
一.服务器安装软件是出现以下的错误信息: www@TinywanAliYun:~$ sudo apt-get install letsencrypt Reading package lists... ...
- 【★★★★★】提高PHP代码质量的36个技巧
http://www.cnblogs.com/52php/p/5658031.html 不要直接使用 $_SESSION 变量 某些简单例子: $_SESSION['username'] = $use ...
- IP地址分类以及子网划分
五类IP地址段 根据上表的说明,我们可以知道: 你只要知道 IP 的第一个十进制数,就能够约略了解到该 IP 属于哪一个等级, 以及同网域 IP 数量有多少. 这也是为啥我们上头选了 192.168. ...
- luogu P4160 [SCOI2009]生日快乐
传送门 考虑因为每个人的蛋糕体积要相等,如果切了一刀,那么要使得分当前蛋糕的人根据分成的两部分蛋糕的体积分成两部分人,所以假设当前有n人,切的这一刀要是在x或y的\(\frac{k}{n}(k\in ...
- Servlet.service() for servlet jsp threw exception
报错信息如下: org.apache.catalina.core.ApplicationDispatcher invoke 严重: Servlet.service() for servlet jsp ...
- 【SVN】svn使用方法
下载安装TortoiseSVN 下载地方 安装成功后 TortoiseSVN清除凭证 右击空白处-TortoiseSVN-Settings打开Settings窗口后做如下操作: svn在idea中的使 ...