#import
@interface A : NSObject
/*
定义一个Block类型的属性,
格式: 返回值类型 (^Block名称) (参数类型1, 参数类型2);
*/
@property (nonatomic, strong) NSString *(^thisBlock)(int, NSDate *);
/*
当Block作为返回类型时的定义方式,
格式: 返回类型 (^) (参数类型1, 参数类型2)
*/
- (NSString *(^)(int, NSDate *))getBlock;
/*
当Block作为参数时的定义方式,
格式: 返回类型 (^) (参数类型1, 参数类型2)
*/
- (void)setBloc:(NSString *(^)(int, NSDate *))aBlock;
@end
#import "A.h"
@implementation A
- (void)test
{
/*
定义一个Block时,
格式: 返回类型 (^Block名) (参数类型1, 参数类型2)
*/
NSString *(^myBlock) (int, NSDate *); // 定义一个Block, Block名叫 myBlock
/*
编写Block的实现代码,
格式: ^ 返回值 (参数类型1 形参1, 参数类型2 形参2)
*/
myBlock = ^NSString *(int a, NSDate *date) {
// ... 逻辑代码 ..
return [[NSString alloc] init];
};
// 练习
__unused id(^aBlock)() = ^id() {
return nil;
};
}
@end
即: Block 对象作为参数传入以后, 依然可以使用在 "原来环境" (声明Block的地方) 中的 变量/方法.
// .h文件
#import
@interface A : NSObject
- (void(^)(NSString *))returnBlock; // 返回一个Block
@end
// .m文件
#import "A.h"
@implementation A
- (void(^)(NSString *))returnBlock
{
int age = 11; // 局部变量age, Block 的 外部变量age
return ^void(NSString *name) { // 返回一个 匿名Block
NSLog(@"%@", [NSString stringWithFormat:@"我叫 \"%@\", 我今年 \"%d\" 岁", name, age]);
};
}
@end
// .h文件
#import
@interface B : NSObject
- (void)invokeBlock:(void(^)(NSString *))aBlock; // 执行Block
@end
// .m文件
#import "B.h"
@implementation B
- (void)invokeBlock:(void (^)(NSString *))aBlock
{
NSString *name = @"CN"; // 定义一个 B类 里的 局部变量name
aBlock(name);
}
@end
#import
#import "A.h"
#import "B.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
A *a = [[A alloc] init];
B *b = [[B alloc] init];
[b invokeBlock:[a returnBlock]];
// 打印结果: 我叫 "CN", 我今年 "11" 岁
}
return 0;
}
Block 的 闭包性 原理实现是因为它在”底层”做了以下行为 :
将 Block 引用到的变量, 在 Block 里面同样复制一份, 放入 Block 的代码块中, 一并传递.
这样一来, 虽然表面上你使用到了”原来环境”中的 变量, 实际上, 你只是引用的 Block 里的某一个由编译器自动给你生成的一个 局部变量. 这也是为什么在 Block 能够使用 外部变量, 但是不能修改 外部变量. 因为你修改的 变量, 实际上 Block 里的, “隐式”的声明出来的 局部变量, 这个 局部变量 是 外部变量 的 “替身”, 所以 外部变量 不会改变.
- (void)invokeBlock
{
// 添加 __block 修饰
__block int count = 10; // 外部变量count
NSLog(@"%d", count); // 打印: 10
void(^aBlock)() = ^void() { // 定义个 Block
count--;
};
aBlock(); // 执行 Block
NSLog(@"%d", count); // 打印: 9
}
__block 关键字的作用, 实际上是改变了编译器的”底层”操作, 由之前制作 外部变量 的 “替身”, 变成了制作 外部变量的指针 的 “替身”, 这样一来, Block 是带着 外部变量的指针 一起传递, 通过 指针 对 变量的值 进行修改, 自然 外部变量 的值也就变了. 就跟C语言中的scanf()输入函数原理一样.
int a;
scanf("%d", &a);
就跟别人问你什么是函数, 什么是方法, 什么是对象一样, 就是一个新的概念, 独立存在的, 它就是它自己.
"引用"这个功能不是只有对象才有的.
A "强引用" B, B 也"强引用" A, 这就强引用循环了.
_aBlock = ^void() { // 此时的_aBlock为成员变量
NSLog(@"Block强引用self: %@", self); // 强引用循环了
};
__weak typeof(self) weakSelf = self; // 创建变量weakSelf"弱引用"self;
_aBlock = ^void() { // 此时的_aBlock为成员变量
NSLog(@"Block弱引用self: %@", weakSelf); // 弱引用循环了
};
_aBlock = ^void() { // 此时的_aBlock为成员变量
NSLog(@"Block使用成员变量_num: %d", _num); // 成员变量 _num
/*
编译器在"底层"做了一个 "self.num" 替换 "_num" 的动作, 所以又变成了 block 强引用 "self",造成强引用循环
*/
};
__weak typeof(self) weakSelf = self; // 创建变量weakSelf"弱引用"self;
_aBlock = ^void() { // 此时的_aBlock为成员变量
NSLog(@"Block使用弱引用对象的属性num: %d", weakSelf.num);
/*
用 self 的 "弱引用对象" weakSelf 的属性 weakSelf.num 去替换 成员变量 _num
*/
};
- (void)test
{
void(^cnBlock)() = ^void() { // 此时的cnBlock为局部变量
NSLog(@"cnBlock中使用self :%@", self);
};
cnBlock();
/*
因为cnBlock是局部变量, 没有对象"强引用"cnBlock, cnBlock在超出它的生命周期之后,
就会被系统自动释放, 而被cnBlock"强引用"的 "self", 随着 cnBlock 被系统释放,
"self" 的 "强引用" 也就消失了, "self" 的 "引用计数" -1. 不会造成 "强引用循环"
*/
}