iOS 知识整理--Block

目录

Block--概念

Block--语法

Block--定义

Block--截取变量

Block--类型

Block--处理循环引用

Block--属性用什么修饰

Block--使用__weak 和__strong修饰符的问题



Block--概念

Block 可以认为是一种匿名函数,使用如下语法声明一个 Block 类型:

returnType (^block_name)(param)

Block--语法

作为变量:

returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};

作为属性:

@property (nonatomic, copy, nullability) returnType (^blockName)(parameterTypes);

作为参数:

- (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName;

作为函数调用的参数:

[someObject someMethodThatTakesABlock:^returnType (parameters) {...}];

作为typedef:

typedef returnType (^TypeName)(parameterTypes);
TypeName blockName = ^returnType(parameters) {...};

Block--定义

  1. void(^block1)()=^(){}
  2. void(^block2)(int)=^(int a){}//如果没有参数,参数可以隐藏,如果有参数,定义的时候必须要写参数,而且必须要有参数变量名
  3. int(^block3)()=^int{} block返回可以省略,不管有没有返回值,都可以省略

Block--截取变量

如果是局部变量,block是值传递

如果是静态变量,全局变量,_block修饰的变量,block都是指针传递

Block--类型

Objective-C 中 Block 有三种类型:

  • NSStackBlock          存储于栈区
  • NSGlobalBlock         存储于程序数据区
  • NSMallocBlock         存储于堆区

MRC 下

@property (nonatomic,copy) void(^block)();
int value = 10;
void(^blockA)()=^{
    NSLog(@"value:%d",value);
};
NSLog(@"MRC 引用计数:%ld, block is: %@",[blackA retainCount],blockA);

void(^blockB)()=^{
    NSLog(@"blockB");
};
NSLog(@"MRC 引用计数:%ld, block is: %@",[blackB retainCount],blockB);

_block = [blockA copy];
NSLog(@"MRC 引用计数:%ld, block is: %@",[self.block retainCount],self.block);

[_block retain];
NSLog(@"MRC 引用计数:%ld, block is: %@",[self.block retainCount],self.block);

[_block release];
NSLog(@"MRC 引用计数:%ld, block is: %@",[self.block retainCount],self.block);

打印结果:

MRC 引用计数: 1, block is: <__NSStackBlock__: 0x7fff59038bc8>
MRC 引用计数: 1, block is: <__NSGlobalBlock__: 0x106bc70e0>
MRC 引用计数: 1, block is: <__NSMallocBlock__: 0x610000058330>
MRC 引用计数: 1, block is: <__NSMallocBlock__: 0x610000058330>
MRC 引用计数: 1, block is: <__NSMallocBlock__: 0x610000058330>

可以看到,blockA 和 blockB的差异只在于有没有调用外部变量,这点差异导致他们的类型不同,存储位置不同。

  • NSGlobalBlock

block内部没有引用外部局部变量的 Block 类型都是 NSGlobalBlock 类型,存储于全局数据区,由系统管理其内存,retain、copy、release操作都无效。

  • NSStackBlock

block 内部引用外部局部变量,retain、release 操作无效,存储于栈区,变量作用域结束时,其被系统自动释放销毁。MRC环境下,[mutableArray addObject:blockA],(在 ARC 中不用担心此问题,因为 ARC 中会默认将实例化的 block 拷贝到堆上)在其作用域结束后就是函数出栈后,从 mutableArray 中取到的 blockA 已经被回收,变成野指针。正确的做法是先将 blockA copy到堆上,然后加入数组。支持copy,copy 之后生成新的NSMallocBlock类型对象。

  • NSMallocBlock

如上例中的 _block ,[blockA copy]操作后变量类型变为 NSMallocBlock,支持 retain、release,虽然 retainCount 始终是1, 但内存管理器中仍会增加、减少计数,当引用计数为零的时候释放(可多次retain、release 操作验证)。copy 之后不会生成新的对象,只是增加了一次引用,类似 retain,尽量不要对 Block 使用 retain 操作。

ARC 下:

@property (nonatomic, copy ) void(^block)();

int value = 10;
void(^blockA)() = ^{
      NSLog(@"value: %d",value);
};
NSLog(@"ARC 引用计数: %ld, block is: %@",CFGetRetainCount(((__bridge CFTypeRef)blockA)), blockA);
    
void(^blockB)() = ^{
      NSLog(@"blockB");
};
NSLog(@"ARC 引用计数: %ld, block is: %@",CFGetRetainCount(((__bridge CFTypeRef)blockB)), blockB);
    
_block = blockA;
NSLog(@"ARC 引用计数: %ld, block is: %@",CFGetRetainCount(((__bridge CFTypeRef)_block)), _block);

打印结果:

ARC 引用计数: 1, block is: <__NSMallocBlock__: 0x6080000536b0>
ARC 引用计数: 1, block is: <__NSGlobalBlock__: 0x106bc7140>
ARC 引用计数: 1, block is: <__NSMallocBlock__: 0x6080000536b0>

所以 Block 属性的特性限定符一般都是 copy,因为其 setter 方法中会拷贝一份新的副本到堆区。

 

Block--处理循环引用

默认情况下,Block中捕获的变量是不能修改的,如果想修改,需要使用__block来声明。

__block int a = 21;

对于 id 类型的变量,在MRC情况下,使用 __block id x 不会 retain 变量,而在 ARC情况下则会对变量进行 retain (即和其他捕获的变量相同)。如果不想在 block 中进行 retain 可以使用 __unsafe_unretained __block id x ,不过这样可能会导致野指针出现。更好的办法是使用 __weak 的临时变量。

typeof(self) __weak weakSelf = self;

或者把使用__block修饰的变量设置为nil,以打破引用循环:

__block AViewController * vc = [AViewController new];

vc.completionHandler = ^{

     vc = nil;

}

Block--属性用什么修饰

在非 ARC 的情况下,对于 block 类型的属性应该使用 copy,因为 block需要维持其作用域中捕获的变量。在 ARC 中编译器会自动对 block 进行 copy 操作,因此使用 strong 或者 copy 都可以,没有什么区别,但是苹果仍然建议使用 copy 来指明编译器的行为

Block--使用__weak 和__strong修饰符的问题

在block中调用self会引起循环引用,但是在block中需要对weakSelf进行strong,保证代码在执行到block中,self不会被释放,当block执行完后,会自动释放该strongSelf

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值