概述

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代码块的详解相关推荐

  1. Java: static,final,代码块 的详解

    Java: static,final,代码块 的详解 每博一文案 山本文绪说过这样一句话:哪些决定放弃了的事,就请放弃得干干净净.哪些决定再也不见面的人,就真 的不要再见面了,不要再做背叛自己的事,如 ...

  2. ios 添加block 类别_ios之Block的详细使用和具体说明

    image.png iOS代码块Block 一:概述 闭包 = 一个函数「或指向函数的指针」+ 该函数执行的外部的上下文变量「也就是自由变量」:Block 是 Objective-C 对于闭包的实现. ...

  3. java 代码块_详解java中的四种代码块

    在java中用{}括起来的称为代码块,代码块可分为以下四种: 一.简介 1.普通代码块: 类中方法的方法体 2.构造代码块: 构造块会在创建对象时被调用,每次创建时都会被调用,优先于类构造函数执行. ...

  4. java 注释 超链接_java_Java代码注释规范详解,代码附有注释对程序开发者来 - phpStudy...

    Java代码注释规范详解 代码附有注释对程序开发者来说非常重要,随着技术的发展,在项目开发过程中,必须要求程序员写好代码注释,这样有利于代码后续的编写和使用. 基本的要求: 1.注释形式统一 在整个应 ...

  5. php调用C代码的方法详解和zend_parse_parameters函数详解

    来源:http://my.oschina.net/Customs/blog/490873 http://blog.csdn.net/super_ufo/article/details/3863731 ...

  6. c调用python脚本如何获取结果_使用C++调用Python代码的方法详解

    一.配置python环境问题 1.首先安装Python(版本无所谓),安装的时候选的添加python路径到环境变量中 安装之后的文件夹如下所示: 2.在VS中配置环境和库 右击项目->属性-&g ...

  7. 史上最详细的Pytorch版yolov3代码中文注释详解(四)

    史上最详细的Pytorch版yolov3代码中文注释详解(一):https://blog.csdn.net/qq_34199326/article/details/84072505 史上最详细的Pyt ...

  8. Matlab中的FCM算法代码及中文详解

    Matlab中的FCM算法代码及中文详解 转自:http://xiaozu.renren.com/xiaozu/106512/336681453 function [center, U, obj_fc ...

  9. Apollo6.0代码Lattice算法详解——Part4:计算障碍物ST/SL图

    Apollo6.0代码Lattice算法详解--Part4:计算障碍物ST/SL图 0.前置知识 1.涉及主要函数 2.函数关系 3.部分函数代码详解 3.1 lattice_planner.cc中代 ...

最新文章

  1. proxy跨域不生效_vue前后端端口号不同,proxytable代理跨域无效
  2. 分区变为RAW的解决办法
  3. 【Elasticsearch】Elasticsearch查询参数batched_reduce_size的解释
  4. 零窗口探测怎么抓包_天问·探路火星|五大亮点!最快“胖五”把探测器送入苍穹...
  5. 后起之秀,《你好,李焕英》,票房已过十个亿和唐探三哪个好?
  6. 【赛尔原创】用对比集成式方法理解基于文档的对话
  7. 分区供水条件口诀_分区供水高频考点,收藏哦
  8. 英文单词打字练习与学习
  9. EAS的各种日志说明 (转载)
  10. pk 打包文件_【旅行】同样是旅行,为啥中国人喜欢拉行李箱,老外喜欢背大包?附干货:行李打包锦囊...
  11. 红米Note3刷机教程(升级安卓9)
  12. 《触动人心—设计优秀的iPhone应用》读书笔记(二)
  13. 大数据核心技术之分布式基础入门
  14. 【Bugku】Misc2
  15. POJ 3322 Bloxorz I
  16. Android蓝牙开发音频焦点
  17. 在职非全日制计算机考研上岸心得分享
  18. 河北欧格教育:主图构图法
  19. 使用UltraISO(软碟通)制作U盘制作启动盘完整教程
  20. 电脑开机自检过程都有什么?

热门文章

  1. 【实战】MATLAB+神经网络+MNIST
  2. GitHub 2W 星:一键生成前后端代码
  3. 【收藏】sonar-scanner扫描代码出错 SonarQube svn: E170001
  4. pyspark join代码示例
  5. win11安装chocolatey软件包管理工具(win版)
  6. JVM 调优实战--常用JVM命令:jps/jinfo/jstat/jmap/jstack/jhat
  7. Java工具类--雪花算法生成全局唯一ID
  8. 微型计算机技术习题,微型计算机技术课后习题一二三章答案
  9. Jenkins pipeline JENKINS_NODE_COOKIE踩坑记录
  10. C++ warning:’xxx‘ has no out-of-line virtual method definitions...