ios 添加block 类别_iOS 关于Block代码块的详解
概述
block
上图就是一个block简单使用,它包括了block的声明、赋值实现、调用 三个部分,其中,实现部分可以看作是一种匿名函数;跟函数一样,block也是需要调用才能执行内部代码的;赋值的行为又让block看起来跟数据类型类似
代码块Block是在iOS4开始引入的,是对C语言的扩展,用来实现匿名函数的特性
Block是一种特殊的数据类型,可以像基本数据类型一样定义成变量、作为参数、返回值来使用
Block还可以保存一段代码,在需要的时候调用
在iOS开发中,Block被系统应在很多地方,例如:GCD、UIView动画、排序等,我们开发者也可以应用在各类回调、传值、传消息等
Block的声明、赋值实现、调用
Block的声明样式。
返回类型 (^Block名称)(参数列表)
void(^myBlock)(NSString *, NSString *)
Block的返回类型分为有返回类型和无返回类型(void),参数列表也可有也可以没有,具体看需求
// 无返回类型无参数列表
void(^block)();
// 无返回类型有参数列表
void(^block)(int);
// 有返回类型无参数列表
int(^block)();
// 有返回列表有参数列表
int(^block)(int);
Block的简单使用:声明、赋值、调用
Block变量的赋值格式为:
Block变量 = ^(参数列表){函数体}; // 这里的参数列表一定要和声明时的参数列表一致
// 声明
void(^block)();
// 赋值
block = ^(){
NSLog(@"Hello World");
};
// 调用
block();
也可以在声明时完成赋值
// 声明、赋值
void(^block)() = ^(){
NSLog(@"Hello World");
};
// 调用
block();
定义Block类型
前面提到过,Block是一种特殊的数据类型,我们可以使用 typedef 来定义 Block 类型,这样我们就可以使用该类型来声明很多相同的Block变量了。
typedef 返回类型(^Block名称)(参数列表);
示例:
// 声明一个Block类型
typedef void(^Block)();
// 使用定义两个block变量
Block myBlock,myNewBlock;
// 赋值实现
myBlock = ^(){
NSLog(@"Hello World");
};
myNewBlock = ^(){
NSLog(@"Hello World, I am lolita0164.");
};
// 调用
myBlock();
myNewBlock();
ARC模式下简单应用
作为对象属性实现消息传递
前面说到,Block保存一段代码,在需要的时候调用。我们可以将使用Block的三个步骤拆开,实现消息传递、传值功能。
// 定义Block类型
typedef void(^Block)(NSString *);
@interface Person : NSObject
// 声明Block变量
@property (nonatomic, copy) Block myBlock;
-(void)sayHello;
@end
@implementation Person
-(void)sayHello{
// Block调用
self.myBlock(@"Hello, I am lolita0164");
}
@end
在定义声明、调用之后,还缺少实现的部分,这一步通常由外部实现。
Person *p = [Person new];
// Block赋值实现
p.myBlock = ^(NSString *string) {
NSLog(@"%@",string);
};
[p sayHello];
这样,我们就可以在Block的赋值实现部分里拿到 p类里的数据了。
作为函数参数实现数据回调
我们将之前的例子稍加改动
// 定义Block类型
typedef void(^Block)(NSString *);
@interface Person : NSObject
// 将Block作为参数
-(void)sayHelloUseBlock:(Block)myBlock;
@end
@implementation Person
-(void)sayHelloUseBlock:(Block)myBlock{
// Block调用
myBlock(@"Hello, I am lolita0164");
}
@end
在外部进行实现。
Person *p = [Person new];
// Block实现
[p sayHelloUseBlock:^(NSString *string) {
NSLog(@"%@",string);
}];
作为参数和作为属性传递消息,在应用场景稍稍有些不同。
作为参数时,通常和当前的方法有着紧密的联系,函数体内部需要与调用的外部进行交互。例如在请求方法中,经常会使用到block进行回调,而这个block和当前的方法关系紧密,通常是该方法的结果回调。又或者是方法执行期间需要外部提供一定的信息,从而通过block获取外部提供的数据。
作为属性时,通常是和当前类相关,作为类与类之间的交互代表。
作为返回值实现链式语法
将block作为返回值的经典例子就是约束库 masonry,这个库在做完每次约束设置之后通过 block 将实例再次回调,就形成了链式语法。
[view makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(view1.mas_right).offset(10);
make.top.equalTo(view1).offset(0);
make.right.equalTo(-10);
make.size.equalTo(viewWidth);
}];
下面通过创建颜色类来演示 block 作为参数的使用。
// block 作为返回值
+(UIColor* (^)(CGFloat, CGFloat, CGFloat))rgb{
// block 的声明和实现
UIColor* (^rgbBlock)(CGFloat, CGFloat, CGFloat) = ^id(CGFloat r, CGFloat g, CGFloat b) {
return [UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:1];
};
return rgbBlock;
}
解析
返回值是一个有返回值参数有三个的block:UIColor * (^)(CGFloat, CGFloat, CGFloat)。我们在该方法的内部进行了block的声明和具体实现,并且将其作为返回值返回了出去,那么外部在调用该方法之后接收到的是一个block,可以使用该值。
那么外部使用情况如下。
// 接收 block 类型
UIColor* (^colorBlock)(CGFloat, CGFloat, CGFloat) = [UIColor rgb];
// 使用 block 获取到颜色
UIColor* color = colorBlock(10,33,65);
self.view.backgroundColor = color;
在丢弃不需要的部分后,代码如下。
+(UIColor* (^)(CGFloat, CGFloat, CGFloat))rgb{
return ^id(CGFloat r, CGFloat g, CGFloat b) {
return [UIColor colorWithRed:(r)/255.0f green:(g)/255.0f blue:(b)/255.0f alpha:1];
};
}
// 使用block作为参数的方法
self.view.backgroundColor = UIColor.rgb(10, 33, 65);
Block 和 变量
Block访问局部变量问题
block 内部可以访问局部变量。
int global = 100;
void (^Block)() = ^(){
NSLog(@"global = %i", global);
};
Block(); // 输出 "global = 100"
但是 block 会把变量 复制 为自己私有的const变量,也就是说block会捕获栈上的变量(或指针),将其复制为自己私有的const变量,当变量被修改时,不会影响到block自己私有的const变量。
int global = 100;
void (^Block)() = ^(){
NSLog(@"global = %i", global);
};
global = 101;
Block(); // 输出 "global = 100"
在Block中不可以直接修改局部变量。
int global = 100;
void (^Block)() = ^(){
global ++; // 这句报错
NSLog(@"global = %i", global);
};
Block();
但是可以通过 __block 修饰符修改局部变量。
__block int global = 100;
void (^Block)() = ^(){
NSLog(@"global = %i", global);
};
global = 101;
Block(); //输出 "global = 101"
__block int global = 100;
void (^Block)() = ^(){
global ++; // 这句正确
NSLog(@"global = %i", global);
};
Block(); //输出 "global = 101"
原因:在局部变量前使用 __block修饰 ,在Block定义时便是将局部变量的指针传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改会影响Block内部的值,同时内部的值也是可以修改的。
Block访问全局变量、静态变量问题
可以访问和修改。
全局变量所占用的内存只有一份,供所有函数共同调用,在Block定义时并未将全局变量的值或者指针传给Block变量所指向的结构体,因此在调用Block之前对全局变量进行修改会影响Block内部的值,同时内部的值也是可以修改的。
在Block定义时便是将静态变量的指针传给Block变量所指向的结构体,因此在调用Block之前对静态变量进行修改会影响Block内部的值,同时内部的值也是可以修改的。
ARC下的内存管理
在ARC默认情况下,Block的内存存储在堆中,ARC会自动进行内存管理,我们只需要避免循环引用即可。
// 当Block变量出了作用域,Block的内存会被自动释放
void(^myBlock)() = ^{
NSLog(@"------");
};
myBlock();
如果在Block中引用了外面的对象,会对所引用的对象进行强引用,但是在Block被释放时会自动去掉对该对象的强引用,因此比并不会造成内存泄漏问题。
Person *p = [[Person alloc] init];
void(^myBlock)() = ^{
NSLog(@"------%@", p);
};
myBlock();
// Person对象在这里可以正常被释放
// 注:这里的Block只是单方面的强引用,所以不会产生循环引用,也不会内存泄漏
如果对象内部引用一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用,导致内存泄漏。
self.block = ^{
NSLog(@"------%@", self);
};
解决办法:使用一个弱引用的指针指向该对象,然后在Block内部使用该弱引用指针来进行操作,这样就避免了Block对对象进行强引用。
__weak typeof(self) weakSelf = self;
weakSelf.block = ^{
NSLog(@"------%@", weakSelf);
};
提示:如果只是Block单方面地对外部变量进行强引用,并不会造成内存泄漏。
补充
1、声明block属性的时候为什么用copy呢?
在说明为什么要用copy前,先思考下block是存储在栈区还是堆区呢?其实block有3种类型:
全局块(_NSConcreteGlobalBlock)
栈块(_NSConcreteStackBlock)
堆块(_NSConcreteMallocBlock)
全局块存储在静态区(也叫全局区),相当于OC中的单例;栈块存储在栈区,超出作用域则马上被销毁。堆块存储在堆区中,是一个带引用计数的对象,需要自行管理其内存。
关于内存分配,请看这篇:C语言内存分配。
怎么判断一个block所在的存储位置呢?
block不访问外界变量(包括栈中和堆中的变量)
block既不在栈中也不在堆中,此时就为全局块,ARC和MRC下都是如此。
block访问外面变量
MRC环境下:默认存储在栈区
ARC环境下:默认存储在堆中,实际上是先放在栈区,在ARC情况下自动又拷贝到堆区,自动释放
因此,使用 copy 修饰符的作用就是将block从栈区拷贝到堆区
为什么要这么做呢?官方给出的答案是:
复制到堆区的主要目的就是 保存 block 的状态,延长其声明周期。因为block如果在栈上的话,其所属的变量作用域结束,该block就被释放掉了,block中的 __block 变量也同时被释放掉了,为了解决超出作用域就被释放的问题,我们就需要把block复制到堆中。
总结
OC 中的 block 是对 C 语言的匿名函数的一种特性是实现。block 具有函数特性,同时也可以作为变量使用。block 可以作为属性、参数、返回值使用。想要在 Block 内部修改外部变量时,需要使用 __Block 将变量指针传递给 block。在使用 Block 时需要特别注意内存泄漏的问题。
ios 添加block 类别_iOS 关于Block代码块的详解相关推荐
- Java: static,final,代码块 的详解
Java: static,final,代码块 的详解 每博一文案 山本文绪说过这样一句话:哪些决定放弃了的事,就请放弃得干干净净.哪些决定再也不见面的人,就真 的不要再见面了,不要再做背叛自己的事,如 ...
- ios 添加block 类别_ios之Block的详细使用和具体说明
image.png iOS代码块Block 一:概述 闭包 = 一个函数「或指向函数的指针」+ 该函数执行的外部的上下文变量「也就是自由变量」:Block 是 Objective-C 对于闭包的实现. ...
- java 代码块_详解java中的四种代码块
在java中用{}括起来的称为代码块,代码块可分为以下四种: 一.简介 1.普通代码块: 类中方法的方法体 2.构造代码块: 构造块会在创建对象时被调用,每次创建时都会被调用,优先于类构造函数执行. ...
- java 注释 超链接_java_Java代码注释规范详解,代码附有注释对程序开发者来 - phpStudy...
Java代码注释规范详解 代码附有注释对程序开发者来说非常重要,随着技术的发展,在项目开发过程中,必须要求程序员写好代码注释,这样有利于代码后续的编写和使用. 基本的要求: 1.注释形式统一 在整个应 ...
- php调用C代码的方法详解和zend_parse_parameters函数详解
来源:http://my.oschina.net/Customs/blog/490873 http://blog.csdn.net/super_ufo/article/details/3863731 ...
- c调用python脚本如何获取结果_使用C++调用Python代码的方法详解
一.配置python环境问题 1.首先安装Python(版本无所谓),安装的时候选的添加python路径到环境变量中 安装之后的文件夹如下所示: 2.在VS中配置环境和库 右击项目->属性-&g ...
- 史上最详细的Pytorch版yolov3代码中文注释详解(四)
史上最详细的Pytorch版yolov3代码中文注释详解(一):https://blog.csdn.net/qq_34199326/article/details/84072505 史上最详细的Pyt ...
- Matlab中的FCM算法代码及中文详解
Matlab中的FCM算法代码及中文详解 转自:http://xiaozu.renren.com/xiaozu/106512/336681453 function [center, U, obj_fc ...
- Apollo6.0代码Lattice算法详解——Part4:计算障碍物ST/SL图
Apollo6.0代码Lattice算法详解--Part4:计算障碍物ST/SL图 0.前置知识 1.涉及主要函数 2.函数关系 3.部分函数代码详解 3.1 lattice_planner.cc中代 ...
最新文章
- proxy跨域不生效_vue前后端端口号不同,proxytable代理跨域无效
- 分区变为RAW的解决办法
- 【Elasticsearch】Elasticsearch查询参数batched_reduce_size的解释
- 零窗口探测怎么抓包_天问·探路火星|五大亮点!最快“胖五”把探测器送入苍穹...
- 后起之秀,《你好,李焕英》,票房已过十个亿和唐探三哪个好?
- 【赛尔原创】用对比集成式方法理解基于文档的对话
- 分区供水条件口诀_分区供水高频考点,收藏哦
- 英文单词打字练习与学习
- EAS的各种日志说明 (转载)
- pk 打包文件_【旅行】同样是旅行,为啥中国人喜欢拉行李箱,老外喜欢背大包?附干货:行李打包锦囊...
- 红米Note3刷机教程(升级安卓9)
- 《触动人心—设计优秀的iPhone应用》读书笔记(二)
- 大数据核心技术之分布式基础入门
- 【Bugku】Misc2
- POJ 3322 Bloxorz I
- Android蓝牙开发音频焦点
- 在职非全日制计算机考研上岸心得分享
- 河北欧格教育:主图构图法
- 使用UltraISO(软碟通)制作U盘制作启动盘完整教程
- 电脑开机自检过程都有什么?
热门文章
- 【实战】MATLAB+神经网络+MNIST
- GitHub 2W 星:一键生成前后端代码
- 【收藏】sonar-scanner扫描代码出错 SonarQube svn: E170001
- pyspark join代码示例
- win11安装chocolatey软件包管理工具(win版)
- JVM 调优实战--常用JVM命令:jps/jinfo/jstat/jmap/jstack/jhat
- Java工具类--雪花算法生成全局唯一ID
- 微型计算机技术习题,微型计算机技术课后习题一二三章答案
- Jenkins pipeline JENKINS_NODE_COOKIE踩坑记录
- C++ warning:’xxx‘ has no out-of-line virtual method definitions...