Objective-C 各类 block
OC 里block 很常见,记录一下常见的用法。
原理 参考另外一篇blog
用法
常见形式
int (^firstBlock)(NSString *, int);
void (^showName)(NSString *);
NSDate *(^whatDayIsIt)(void);
void (^allVoid)(void);
NSString *(^composeName)(NSString *, NSString *);
即 返回值 (^名称)(参数)
示例
局部函数
int(^sum)(int ) = ^(int a){
NSLog(@"%d",a);
return a;
};
sum(1);
void(^logC)(void) = ^(){
NSLog(@"没得参数");
};
logC();
全局闭包
#import <UIKit/UIKit.h>
typedef void(^GlobleBlock)(void); // 声明
@interface ViewController : UIViewController
@end
// 实现
GlobleBlock block = ^(){
NSLog(@"全局的 block");
};
// 引用
block();
作为属性
#import <UIKit/UIKit.h>
typedef void(^GlobleBlock)(void);
@interface ViewController : UIViewController
@property (nonatomic, copy) GlobleBlock block;//属性
@end
self.block = ^{
NSLog(@"这是个全局的 block");
};
self.block();
作为函数参数
这个也是我们常说的尾随闭包。一般在函数执行完毕后调用 block,完成函数执行完毕之后想要的操作。
常见如 网络请求之后的操作、 页面刷新之后通知等
- (void)functionWithBlock:(NSString *)name complete:(void(^)(NSString *sss))handle {
NSLog(@"函数执行完毕");
handle(name);
}
- (void)functionWithBlockNo:(NSString *)name complete:(void(^)(void))handle {
handle();
}
[self functionWithBlockNo:@"name" complete:^{
NSLog(@"block 实现");
}];
[self functionWithBlock:@"str" complete:^(NSString *sss) {
NSLog(@"函数传递的参数 %@",sss);
}];
注意事项
该注意的就两个相互引用导致内存泄漏和闭包捕获数据
内存泄漏
强调一点,并不是所有的 block 都会造成循环引用
例1:
- (void)btnAction{
GlobleBlock block = ^(){
CCViewController *ssVC = [[CCViewController alloc]init];
[self.navigationController pushViewController:ssVC animated:YES];
};
block();
}
BlockKinds[50750:1209368] <CCViewController: 0x7fea9740a950> dealloc
BlockKinds[50750:1209368] <SSViewController: 0x7fea975087a0> dealloc
点击按钮通过 block 跳转 VC,并不会造成循环引用
例2:
- (void)btnAction{
[self functionWithBlock:@"block" complete:^(NSString *sss) {
CCViewController *ssVC = [[CCViewController alloc]init];
[self.navigationController pushViewController:ssVC animated:YES];
}];
}
- (void)functionWithBlock:(NSString *)name complete:(void(^)(NSString *sss))handle {
NSLog(@"函数执行完毕");
handle(name);
}
<CCViewController: 0x7fd9e0e12650> dealloc
<SSViewController: 0x7fd9e3103ef0> dealloc
可见也没有发生内存泄漏
原理需要开一篇新的博客说明一下了, 关于 block 是不是导致内存泄漏可以根据 block 和 block 里面的对象有没有相互持有
上面的例子中 block 都是作为局部变量,当函数执行完毕之后 block 被释放掉了。 block 都没了,自然不会循环引用
例3:
typedef void(^GlobleBlock)(void);
@interface SSViewController : UIViewController
@property (nonatomic, copy) GlobleBlock block;
@end
- (void)viewDidLoad {
//__weak typeof(self) weakself = self;
self.block = ^{
CCViewController *ssVC = [[CCViewController alloc]init];
[self.navigationController pushViewController:ssVC animated:YES];
//[weakself.navigationController pushViewController:ssVC animated:YES];
};
}
- (void)btnAction{
self.block();
}
<CCViewController: 0x7fd9e0e12650> dealloc
block作为 SSViewController 的属性被持有。然后 block 的实现里面又持有SSViewController。
典型的互相引用 造成内存泄漏,SSViewController
无法释放。此时编译器也会给出提示
Capturing 'self' strongly in this block is likely to lead to a retain cycle
**解决办法
__weak typeof(self) weakself = self;
使用 weakself 替代 self,将互相持有的一方打断。既可避免内存泄漏
同理打断 block 的持有一样能解决循环引用问题
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
self.block = nil;
}
<CCViewController: 0x7fbbef7177b0> dealloc
<SSViewController: 0x7fbbef701e80> dealloc
在 viewWillDisappear
中将 block 置为 nil,即可打断循环引用
捕获数据
block 使用外部局部变量是,Xcode 会给出提示 Variable is not assignable (missing __block type specifier)
__block int num = 0;
int(^sum)(int ) = ^(int a){
num ++;
return a +num;
};
局部变量需要__block 修饰
属性变量虽然不需要修饰,但是使用时要留心属性在其他地方的变化。