前言:对于ios初学者,block通常用于逆向传值,遍历等,会使用,但是可能心虚,会感觉block很神秘,那么下面就一起来揭开它的面纱吧。

ps: 下面重点讲叙了闭包的概念,常用的语法,以及访问变量,循环引用问题,至于底层的运行,堆栈block的区别,还有其他用法这里就不介绍了,目前也处于迷糊中,等到真正理解了再来补充 - -。

一. 概念 

1. 什么是闭包?

闭包就是能够读取其他函数内部变量的函数,可以理解成“定义在一个函数内部的函数“。

在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

闭包在很多语言中都有应用,C,JAVA,OC等

2. OC中的Block

在OC中,Block是在iOS4开始引入,是对C语言的扩展,被用来实现匿名函数的特性

Block是一种特殊的数据类型,可以正常定义变量、作为参数、作为返回值

特殊地,Block还可以声明赋值去保存一段代码,在需要调用的地方去调用

目前Block已经广泛应用于各类回调传值、排序遍历、GCD、动画等

二. 基本语法

1. block做自由变量 - 声明、赋值以及调用 (个人感觉理解语法就好)

 // 声明一个名字为 TestBlockTest 的无返回值含有参数的变量
void (^TestBlockTest)(NSString *);
// 只声明变量,需要赋值
TestBlockTest = ^(NSString *parameter){
NSLog(@"测试");
};
// 调用
TestBlockTest(@""); // 声明TestTwoBlock变量同时赋值
int (^TestTwoBlock)(int) = ^(int num){
return num*;
};
// 已经声明了blcok并赋值了 ,可以直接调用
int num = TestTwoBlock();
NSLog(@"%d",num);

注意:

^ 这个叫做 脱字符,其中,返回值类型,参数列表可以省略简写,这个用多了就知道了,开始推荐写全,基础要扎实

第一个输出值 测试,没有用到参数parameter,强迫症大神请见谅哈!第二个输出值 64

Block的声明与赋值只是保存了一段代码段,必须 调用 才能执行内部代码

2. 使用typedef定义Block类型

可做属性,可做参数、返回值类型等 (这个用法务必掌握)

示例代码背景:A页面点击按钮 跳转 B页面,B页面返回A页面时候,传值@“测试”,用于修改A页面按钮名字

2.1 .h中定义

 #import <UIKit/UIKit.h>

 // typedef 定义无返回值,有一个参数,名字为TestBlock的block类型
typedef void(^TestBlock)(NSString *); @interface ViewController : UIViewController // 做属性
@property (nonatomic, copy) TestBlock testBolck; // 做方法参数
- (void)returnText:(TestBlock)block; @end

2.2 .m中用法,实现(下面整合了做属性,做参数的代码)

 // B页面

 // 做属性
TestBlock blockVar = ^(NSString *parameterTwo){
NSLog(@"hello world %@",parameterTwo);
};
/*
* 我们可能需要重复地声明多个相同返回值相同参数列表的Block变量
* 如果总是重复地编写一长串代码来声明变量会非常繁琐
* 所以我们可以使用typedef来定义Block类型
* 然后像OC中声明变量一样使用Block类型 TestBlock 来声明变量
**/
blockVar(@"UZI");
blockVar(@"");
// 实现定义的方法
- (void)returnText:(TestBlock)block{
self.testBolck = block;
}
// 这里选择返回传参数,用法很多,看个人喜好
- (void)viewWillDisappear:(BOOL)animated{
self.testBolck(@"测试");
}
// A页面 在页面跳转部分
// 做属性回调
__weak __typeof(self) weakSelf = self;
vc.testBolck = ^(NSString *parameter) {
[weakSelf setBtnTitle:parameter];
}; // 做参数回调
[vc returnText:^(NSString *parameter) {
[self setBtnTitle:parameter];
}];
// 做参数另一种写法
__weak __typeof(self) weakSelf = self;
[vc returnText:^(NSString *parameter) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
// 这里用strong保证self不被释放,详见下文 循环引用weak,strong修饰问题
[strongSelf setBtnTitle:parameter];
}];

注意:

上面选择在B页面返回A页面时候传递参数,传递给A,A页面分别用了block做属性,做参数是怎么完成逆向传值的,方式很多,凭自己喜好

三.   block访问变量问题

这里不一一代码举例,个人感觉看总结,主要有以下四点,记住就好

1.   Block拥有捕获外部变量的功能,在Block中访问一个外部的局部变量,Block会持用它的临时状态,自动捕获变量值,外部局部变量的变化不会影响它的的状态,可以理解为瞬间性捕获。

2.  在block中,可以访问局部变量(自由变量),但是不能修改局部变量,因为:block捕获的是自动变量的const值,名字一样,不能修改

3.  可以访问静态变量,并修改变量的值,静态变量属于类的,不是某一个变量,因此block不用调用self指针,所以block可以修改值

4.  使用__block修饰符的局部变量,可以修改局部变量的值。包括可变类型的参数,也可以修改,这个可以用clang命令将OC转为C++代码来查看一下Block底层实现

四:  循环引用 __weak __strong 修饰问题

很多初学者对这块都是模糊的,只知道加上 __weak __typeof(self) weakSelf = self 这句,弱引用,可以防止循环引用。

那什么是循环引用?

简单理解为 相互持有强引用,造成block内所持有的对象无法释放,引起内存泄漏

当然,造成循环引用不唯一,好比对象内部有一个Block属性,而在Block内部又访问了该对象,那么也会造成循环引用

下面分三点来谈论:

1. 如果相互持有强引用,即对象内部有一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用。

解决办法是使用一个弱引用的指针指向该对象,然后在Block内部使用该弱引用指针来进行操作,这样引用计数不加1,避免了Block对对象进行强引用。

通常是这样: __weak __typeof(self) weakSelf = self

注意: 以上是在ARC情况下,如果MRC中,可以在 会引起相互持有的对象 前面,使用 __block 修饰,原理是可以禁止block对对象进行retain操作,引用计数不会加1,从而解决循环引用问题。

这种循环引用示例(部分代码,提供思路):

 // 定义一个block
typedef void(^HYBFeedbackBlock)(id model);
// 声明一个对象
@property (nonatomic, strong) HYBAView *aView;
// block做方法参数
- (instancetype)initWithBlock:(HYBFeedbackBlock)block;
// 构造方法
- (instancetype)initWithBlock:(HYBFeedbackBlock)block {
if (self = [super init]) {
self.block = block;
return self;
}
// 调用
self.aView = [[HYBAView alloc] initWithBlock:^(id model) {
// 假设要更新model
self.currentModel = model;
}];

上面代码很容易看出所形成的环:

vc->aView->block->vc(self)

vc->aView->block->vc.currentModel

2. 对于上面的那种情况,为消除循环引用,而用弱引用

虽说使用__weak,但是此处会有一个隐患,你不知道 block内的 self 什么时候会被释放,

为了保证在block内不会被释放,我们添加__strong,更多的时候需要配合strongSelf使用

    // 上面讲到做方法参数时候的一种写法
__weak __typeof(self) weakSelf = self;
[vc returnText:^(NSString *parameter) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
[strongSelf setBtnTitle:parameter];
}];

可能有人问,用strong,那什么时候才释放呢?

用修饰符strong时,当外部把变量/对象释放掉,但block如果没有执行结束,那么系统就会等待block执行完成后再释放,

对该变量/对象在block中的使用起到了保护作用,当block执行结束后会自动释放掉(ARC)。

不过若无强烈需求,不建议在Block里加strong,容易占用内存,造成内存消耗

3. 是不是所有的block都要用弱引用呢?

  不是,如果没有相互直接引用,可以放心大胆的不用__weak

  并不是block就一定会造成循环引用,如果不是相互持有,可以不用__weak 去弱引用

最经典的示例: Masonry代码布局

  [self.headView mas_makeConstraints:^(MASConstraintMaker *make) {

         make.centerY.equalTo(self.otherView.mas_centerY);
}];

  * block里用到了self,block会保持一个对self的引用,但是self并没有直接或者间接持有block,所以不会造成循环引用

  形成的持有链:

self ->self.headView ··· MASConstraintMaker构造block->self

五. 感兴趣的可以继续了解下

*  block 与内存管理,堆栈block等

*  block 底层实现

*  block 其他用法

总结: 认清一件事物需要长期不断的观察与思考,长路漫漫修远兮,时刻保持学习心,与君共勉

参考文献:

一篇文章看懂iOS代码块Block

block 中使用__weak 和__strong修饰符的问题

iOS中Block的用法,举例,解析与底层原理(这可能是最详细的Block解析)

iOS闭包循环引用精讲

深入解构iOS的block闭包实现原理 (想看block实现原理推荐看此文献)

浅谈iOS中的闭包

IOS 浅谈闭包block的使用的更多相关文章

  1. [iOS、Unity、Android] 浅谈闭包的使用方法

    前言 我们经常所编程语言的的进步速度是落后于硬件的发展速度的. 但是最近几年,闭包语法在各个语言中都有自己的体现形式,例如 • C语言中使用函数指针作为回调函数的入口: • Java和C#语言中的La ...

  2. iOS 浅谈MVC设计模式及Controllers之间的传值方式

    1.简述你对MVC的理解? MVC是一种架构设计.它考虑了三种对象:Model(模型对象).View(试图对象).Controller(试图控制器) (1)模型:负责存储.定义.操作数据 (2)视图: ...

  3. [iOS]浅谈NSRunloop工作原理和相关应用

    一. 认识NSRunloop  1.1 NSRunloop与程序运行 那么具体什么是NSRunLoop呢?其实NSRunLoop的本质是一个消息机制的处理模式.让我们首先来看一下程序的入口——main ...

  4. iOS——浅谈iOS中三种生成随机数方法

    ios 有如下三种随机数方法:

  5. 【转】iOS 浅谈:深.浅拷贝与copy.strong

    深.浅拷贝 copy mutableCopy NSString 1 2 3 4 5 6 NSString *string = @"汉斯哈哈哈"; // 没有产生新对象 NSStri ...

  6. Python核心编程 | 浅谈闭包的使用

    1.函数的引用   >>> def test(): print('test:') >>> test <function test at 0x10ffad488 ...

  7. 浅谈闭包(Closure)

    一.闭包 好长时间对于闭包都知道与函数和其环境变量有关系,但是一直没有一个清晰的认识.今天查看了一下维基百科,顺便写下来. 二.闭包的定义 在编程语言中,闭包(语义闭包或函数闭包)是指哪些可以将语义范 ...

  8. iOS 浅谈:深.浅拷贝与copy.strong

    深.浅拷贝 copy mutableCopy NSString NSString *string = @"汉斯哈哈哈"; // 没有产生新对象 NSString *copyStri ...

  9. ios浅谈关于nil和 NIL区别及相关问题(转)

    转自:http://blog.csdn.net/guozh/article/details/8469131 个就是将引用技术减1,所谓的引用计数就是看看有多个指针指向一块内存实体,当release一次 ...

随机推荐

  1. Win2D 官方文章系列翻译 - 预乘 Alpha

    本文为个人博客备份文章,原文地址: http://validvoid.net/win2d-premultiplied-alpha/ 在计算机绘图中有两种表示颜色值不透明度的方法.Win2D 中两种方法 ...

  2. Cocos2d-js 开发记录:Loading载入界面自定义

    Loading界面是一个cc.Scene具体请看: http://blog.csdn.net/jonahzheng/article/details/38348255 如果仅仅是想把图片(cocos l ...

  3. scss-@each指令

    一.@each指令实例 在@each变量的定义,其中包含的每个项目的列表中的值. 语法: @each $var in <list or map> 语法简要说明如下. $var: 它代表了变 ...

  4. TopcoderSRM679 Div1 250 FiringEmployees(树形dp)

    题意 [题目链接]这怎么发链接啊..... 有一个 \(n\) 个点的树,每个点有点权(点权可能为负) ,求包含点\(1\)的最 大权连通子图(的权值和) . \(n \leqslant 2500\) ...

  5. 封装微信jssdk自定义分享代码

    var protocol = window.location.protocol; //获取协议 var host = window.location.host; //获取域名 var posuDoma ...

  6. linux账号权限管理

    作为一名管理服务器的程序,最近,明显感到各种linux的账号和权限问题弄得很混乱.所以,接下来要学习一下这块内容. /etc/passwd 这个文件每一行代表一个账号,有几行代表系统中有几个账号.很多 ...

  7. dpkg 打包root权限app

    dpkg 安装Macports 下载对应系统的Macports安装时间会比较久,安装完毕后放在了/opt/local/bin 目录下 安装dpkg 打开终端,输入 sudo port -f insta ...

  8. Jmeter各部件的作用

    JMeter主要组件介绍 1.测试计划(Test Plan)是使用 JMeter 进行测试的起点,它是其它 JMeter 测试元件的容器. 2.线程组(Thread Group)代表一定数量的并发用户 ...

  9. WebLogic配置与部署

    一.创建域: 第一步,选择“开始菜单”-> “Oracle WebLogic”-> “WebLogic Server 10gR3” -> “Tools”-> “Configur ...

  10. Azure进阶攻略丨共享访问签名是个什么东东?

    Azure 进阶攻略]一经推出便受到了广大粉丝的热情追捧,大家纷纷表示涨了姿势,并希望能了解更多的攻略~根据大家的投票结果,本期,小编将为大家讲解“如何生成 Shared Access Signatu ...