美文网首页
iOS block和闭包

iOS block和闭包

作者: CrystalZhu | 来源:发表于2020-02-12 18:41 被阅读0次

OC中称Block swift中称闭包 其实是同一种东西 block是OC中对闭包的实现.

什么是block或者闭包呢?

其实就是匿名函数,对比函数和函数指针:
函数:具有特定功能的代码块
函数指针:指向函数的指针
闭包:除具备“函数”和“函数指针”的所有功能外,还包括声明它的上下文(如作用域内的自由变量等)

闭包的用途:

通常来说,block都是一些简短代码片段的封装,适用于工作单元,通常用来做并发任务,遍历,以及回调.

Block的声明:

Block的定义和函数的声明差不多,就是把函数名改为(^blockName)即可.以下为Block的声明代码:
有返回值的:

int (^sumBlock)(int, int);

无返回值的:

void (^myBlock)(int, int);

block的声明需要使用copy声明

@property (nonatomic, copy) ...

闭包在swift中的声明

有返回值的:

var successCallBack:(([String: AnyObject?]) -> (int))?

无返回值的:

var successCallBack:(([String: AnyObject?]) -> (void))?

Block的使用:

  1. 声明 void (^blockName)();
  2. 定义 blockName = ^(){NSLog(@"执行block");};
  3. 执行 blockName();

iOS中有三种类型的block

1.NSGlobalBlock 全局静态

void (^blkGlobal)(void);
blkGlobal = ^(
  printf("NSGlobalBlock1");
);
NSLog(@"NSGlobalBlock1: %@", blkGlobal);
NSLog(@"NSGlobalBlock2:%@", ^{
  printf("NSGlobalBlock2");
});

2.NSConcreteStackBlock 栈block

int i = 0;
NSLog(@"NSConcreteStackBlock1:%@", ^{
  printf("NSConcreteStackBlock2: %d", i);
});

3.NSConcreteMallocBlock 堆(Heap)block

void (^blkStack)(void);
//等号是赋值运算,相当于block,进行copy,即相当于调用block的copy方法
blkStack = ^{
  printf("NSConcreteMallocBlock1: %d", i);
};
NSLog(@"NSConcreteMallocBlock1: %@", [ ^{
  printf("NSConcreteMallocBlock2: %d", i);
}  copy]);

block的使用注意事项

1.在block内直接调用类的实例变量会使self(类的实例)引用计数加1,这样可能会引起循环引用问题(可以用__weak或者local_var处理)
2.使用null的block程序会crash,使用前判断一下:

if (blockVar){
  // doing something
};

3.在多线程环境下(block中的weakSelf有可能被析构的情况下),需要先将self转为strong指针,避免在运行到某个关键步骤时self对象被析构.

__block变量和其他变量在block内的读写情况

类型 基本数据类型在block内的读写 block对OBJC对象类型的深浅拷贝
局部自动变量 只读 mutable copy
全局变量/extern 读写 deep copy
static变量 读写 deep copy
__block变量 读写 deep copy

Block的循环引用

原因:

一个对象A有Block类型的属性,从而持有这个Block.如果Block的代码块中使用到这个对象A或者仅仅是用到了A对象的属性,会使Block也持有A对象,导致两者互相持有,不能在作用域结束后正常释放.

解决原理:

对象A照常持有Block,但是Block不能强引用持有对象A,以打破循环

解决方法:
方法一:

对Block内要使用的对象A使用__weak进行修饰,Block对对象A弱引用打破循环
1.__weak className

__block ViewController *weakSelf = self;

2.__weak typeof(self)

__weak typeof(self) weakSelf = self;

3.Reaction Cocoa 中的@weakify 和 @strongify:

@weakify(self);
self.blk = ^{
  @strongify(self);
  NSLog(@"In Block: %@", self);
};
方法二:

对Block内要使用的对象A使用__block进行修饰,并在代码块内使用完__block变量后将其设为nil,并且该block必须至少执行一次

__block XXController *blkSelf = self;
self.blk = ^{
  NSLog(@"In Block: %@", self);
  blkSelf = nil;
};
self.blk();  //该block必须执行一次,否则还是内存泄露

此时: XXController对象持有Block对象blk,blk对象持有__block变量 blkSelf(类型为编译器创建的结构体),__block变量blkSelf在执行不blk()之后被置为nil(__block变量结构体的__forwarding指针指向了nil),不再持有XXController对象,打破循环.
方法二使用__block打破循环的方法优点是:
1.可通过__block变量动态控制有XXController对象的时间,运行时决定是否将nil或者其他变量赋值给__block变量.
2.不能是有__weak的系统中,使用__unsafe __unretained来替代__weak打破循环,可能有野指针问题,使用__block可避免该问题.
缺点:
1.必须手动保证__block变量最后设置为nil
2.block必须执行一次,否则__block不为nil循环引用仍存在

因此,建议避免使用第二种方法,直接使用__weak打破Block循环引用会更好.

方法三:

将在Block内要使用到的对象(一般为self对象),以Block参数的形式传入Block就不会捕获该对象,而将该其作为参数使用,其生命周期系统的栈自动管理,不造成内存泄露.
即原来使用__weak的写法:

__weak typeof (self) = self;
self.blk = ^{
  __strong typeof(self) strongSelf = weakSelf;
  NSLog(@"use property: %@", strongSelf);
};
self.blk();

改为Block传参写法后:

self.blk = ^ (UIViewController *vc){
  NSLog(@"use property: %@", vc.name);
};
self.blk(self);

优点:
1.简化了两行代码,更优雅
2.更明确的API设计,告诉API使用者,该方法的Block直接使用传进来的参数对象,不会造成循环引用,不用调用者再使用weak避免循环

截取自动变量值

Block表达式可截获所使用的自动变量的值.
截获:保存自动变量的瞬间值.
因为是“瞬间值”,所有声明Block之后,即使在Block外修改自动变量的值,也不会对Block内截获的自动变量值产生影响.
例如:

int i = 10;
void (^blk)(void) = ^{
  NSLog(@"In Block, i = %d", i);
};
i = 20 //block外修改变量i,不影响block内的变量
blk(); //执行打印 i = 10
NSLog(@"i = %d", i);  //i = 20

__block 说明符号

自动变量截获的值为Block声明时刻的瞬间值,保存后就不能改写该值.如需对自动变量进行重新赋值,需要在变量声明前附加__block说明符,这时该变量称为__block变量.
例如:

__blcok int i = 10; //i 为__block变量,可在block中重新赋值
void (^blk)(void) = ^{
  NSLog(@"In Block, i = %d", i);
};
i = 20;
blk(); // 打印 i= 20
NSLog(@"i = %d", i);  //i = 20

自动变量值为一个对象的情况

当自动变量为一个类的对象,且没有使用__block修饰时,虽然不可以在Block内对该变量进行重新赋值,但可以修改该对象的属性.
如果该对象是一个Mutable的对象,例如NSMutableArray,则还可以在Block内对NSMutableArray进行元素的增删:

NSMutableArray *array =[ [NSMutableArray alloc] initWithObjects: @"1", @"2", nil];
NSLog(@"Array count : %d", array.count); //array count: 2
void (^blk)(void) = ^{
  [array removeObjectAtIndex: 0];
  // array = [NSMutableArray new]; 没有__block 修饰,编译失败
};
blk();
NSLog(@"Array count : %d", array.count); // array count : 1

相关文章

  • iOS Block 原理解析

    一 : block要点分析 [ block是个闭包 ] block他的本质就是闭包功能在iOS上的实现。而闭包功能...

  • Swift笔记<二十>闭包

    1.闭包的介绍 闭包和OC中的block非常相似 2.闭包的使用 block的定义属性和方法中带block 闭包=...

  • iOS block和闭包

    OC中称Block swift中称闭包 其实是同一种东西 block是OC中对闭包的实现. 什么是block或者闭...

  • Swift 闭包

    闭包 闭包是保存一段代码块,可以在代码中传递和是使用(类似Block) 闭包和block类似 闭包的表达式如下闭包...

  • JavaScript学习之路-闭包

    一、闭包? 闭包一词想必iOS开发的童鞋指定很熟悉,Objective-C上的闭包叫Block,Swift上就叫闭...

  • 闭包 iOS block

    1 这个作者深入浅出,一下就看懂了闭包的本质 http://wanghuanming.com/2015/01/cl...

  • iOS block & 闭包

  • Swift-闭包

    Swift 闭包 函数 ()->() Swift 中的闭包和 Objective-C 中的 block 类似,闭包...

  • Swift学习-闭包&& 懒加载&&am

    闭包 闭包的介绍 闭包和OC中的block非常相似OC中的block是匿名的函数Swift中的闭包是一个特殊的函数...

  • Swift 学习之写一个属性 闭包函数(block) 和协议(代

    闭包函数(block) //闭包函数(block) 声明var timeBlockSelect : (NSStri...

网友评论

      本文标题:iOS block和闭包

      本文链接:https://www.haomeiwen.com/subject/xeycfhtx.html