前言
- Block 是一段预先准备好的代码,可以在需要的时候执行,可以当作参数传递。Block 可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。Block 是 C 语言的,类似于一个匿名函数,它和传统的函数指针很类似,但是 Block 是 inline(内联函数)的,并且默认情况下它对局部变量是只读的。
- 苹果官方建议尽量多用 Block。在多线程、异步任务、集合遍历、集合排序、动画转场用的很多。
- Block 语法
// Block as a local variable returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...}; // Block as a property @property (nonatomic, copy) returnType (^blockName)(parameterTypes); // Block as a method parameter - (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName; // Block as an argument to a method call [someObject someMethodThatTakesABlock: ^returnType (parameters) {...}]; // Block as typedef typedef returnType (^TypeName)(parameterTypes); TypeName blockName = ^returnType(parameters) {...};
1、Block 的使用
1.1 Block 的定义
- Block 的简单定义
// 定义 Block /* 定义了一个名叫 MySum 的 Block 对象,它带有两个 int 型参数,返回 int 型。等式右边就是 Block 的具体实现,大括号后需加分号 */ int (^MySum)(int, int) = ^(int a, int b){ return a + b; }; // 调用 Block int sum = MySum(10, 12);
- Block 数据类型的定义
// 定义 block 数据类型 MyBlock typedef int (^MyBlock)(int, int); // 定义 MyBlock 的变量 MyBlock myblock; // 实现 MyBlock 的变量 1 myblock = ^(int a, int b){ return a + b; }; // 调用 MyBlock 的变量 1 int sum = myblock(10, 12); // 实现 MyBlock 的变量 2 myblock = ^(int a, int b){ return a - b; }; // 调用 MyBlock 的变量 2 int minus = myblock(13, 2);
1.2 Block 访问局部变量
- Block 可以访问局部变量,但是不能修改,如果要修改需加关键字 __block(双下划线)。
// 这样定义时,局部变量 sum 只能读取值不能修改,编译时会报错 // int sum = 10; // 这样定义时,局部变量 sum 既可以读取值又能修改 __block int sum = 10; int (^MyBlock)(int) = ^(int a){ // 对局部变量值修改 sum ++; // 读取局部变量的值 return a * sum; }; int result = MyBlock(5);
2、Block 的回调
2.1 Block 回调使用
// Block1.h
// block 属性变量定义
/*
要使用 copy 类型,格式:@property (nonatomic, copy) 返回值类型 (^变量名) (参数类型列表);
*/
@property (nonatomic, copy) void (^completion) (NSString *);
// 调用 block 代码段声明
- (void)useBlock;
// Block1.m
// 调用 block 代码段
- (void)useBlock {
// 设置 block 的回调值
// 判断是否设置了 block
if (self.completion != nil) {
// 设置回调值
self.completion(@"hello world");
}
}
// Block.m
#import "Block1.h"
Block1 *block = [[Block1 alloc] init];
// 设置 block 代码段
block.completion = ^(NSString *str) {
// 结果:string = @"hello world"
NSString *string = str;
};
// 调用 block 代码段
[block useBlock];
2.2 Block 回调封装
// Block2.h
// block 方法参数定义
// 类方法定义
+ (Block2 *)blockWithCompletion:(void (^) (NSString *)) completion;
// 调用 block 代码段声明
- (void)useBlock;
// Block2.m
// block 属性变量定义
// 要使用 copy 类型,格式:@property (nonatomic, copy) 返回值类型 (^变量名) (参数类型列表);
@property (nonatomic, copy) void (^completion) (NSString *);
// 调用 block 代码段
- (void)useBlock {
// 设置 block 的回调值
// 判断是否设置了 block
if (self.completion != nil) {
// 设置回调值
self.completion(@"hello world");
}
}
// 类方法实现
+ (Block2 *)blockWithCompletion:(void (^)(NSString *))completion {
Block2 *bl = [[Block2 alloc] init];
// 设置属性的值
bl.completion = completion;
return bl;
}
// Block.m
#import "Block2.h”
// 设置 block 代码段
Block2 *block = [Block2 blockWithCompletion:^(NSString *str) {
// 结果:string = @"hello world"
NSString *string = str;
}];
// 调用 block 代码段
[block useBlock];
3、Block 属性定义中为什么使用 copy 修饰
- ARC 开发的时候,编译器底层对 block 做过一些优化,使用 copy 修饰可以防止出现内存泄漏。
- 从内存管理的角度而言,程序员需要管理的内存只有堆区的。如果用 strong 修饰,相当于强引用了一个栈区的变量。
- 而使用 copy 修饰,在设置数值的时候,可以把局部变量从栈区复制到堆区。
// 用 copy 修饰定义属性 @property (nonatomic, copy) void (^myTask)(); // 定义,myBlock 是保存在栈区的,出了作用域,就应该被销毁 void (^myBlock)() = ^ { NSLog(@"hello"); }; // 用属性记录 self.myTask = myBlock; // 执行 self.myTask();
4、循环引用
- 在 Block 中调用 self 容易产生循环引用,无法释放对象,在程序中可以使用析构方法判断是否产生了循环引用。
@implementation ViewController // 在 Block 中调用 self 容易产生循环引用 [[QWebImageManager sharedManager] downloadImage:self.urlStr completion:^(UIImage *image) { self.image = image; }]; @end
// 判断是否存在循环引用,无法释放时即存在循环引用 - (void)dealloc { NSLog(@"成功退出"); }
- 可以使用关键字 __weak 声明一个弱变量,或者为属性指定 weak 特性。如:
@implementation ViewController // 弱引用 self,typeof(self) 等价于 ViewController __weak typeof(self) weakSelf = self; [[QWebImageManager sharedManager] downloadImage:self.urlStr completion:^(UIImage *image) { weakSelf.image = image; }]; @end
时间: 2024-09-21 00:48:06