ObjectiveC - Block
1
2
3
| // block 的格式
返回值类型 (^block变量名)(形参列表) = ^(形参列表) {
};
|
函数指针与 block
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| void printA() {
printf("a");
}
int main(int argc, const chjar * argv[]) {
// 1. 调用函数
printA(); // "a"
// 2. 指向函数 printA 的指针
// - void 表指向的函数没有返回值
// - () 代表指向的函数没有形参
// - (*ap) 代表 ap 是一个指向函数的指针
void (*ap) ();
ap = printA;
ap(); // "a"
// ---------------------------------------------------
// 3. 定义 block
// - block 和函数一样,可以没有(有)返回值,也没有(有)
// - void 代表这个 block 将来保存的代码没有返回值
// - () 代表这个 block 将来保存的代码没有形参
// - (^printB) 代表 printB 是一个 block 变量,可以用于保存一段 block 代码
void (^printB) (); // 定义
ap2 = ^{ // 保存 block 类型代码段
printf("b");
};
ap2(); // "b" 执行 block
return 0;
}
|
1
2
3
4
5
| int (^sum) (int, int) = ^(int a, int b) {
return a + b;
};
NSLog(@"sum = %i", sum(10, 50)); // "sum = 50"
|
block 与 typedef
使用 typedef 可简化 block 的声明
函数指针使用 typedef
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // 函数
int sum(int a, int b) { return a + b; }
int minus(int a, int b) { return a - b; }
int main(int argc, const chjar * argv[]) {
int (*sumP)(int, int); // 函数指针
sumP = sum;
NSLog(@"%i", sumP(1, 2)); // "3"
int (*minusP)(int, int);
minusP = minus;
NSLog(@"%i", minusP(3, 2)); // "1"
return 0;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| int sum(int a, int b) { return a + b; }
int minus(int a, int b) { return a - b; }
// 使用 typedef
typedef int (*calculte)(int, int);
int main(int argc, const chjar * argv[]) {
calculte sumP = sum;
NSLog(@"%i", sumP(1, 2)); // "3"
calculte minusP = minus;
NSLog(@"%i", minusP(3, 2)); // "1"
return 0;
}
|
block 使用 typedef
不能在方法代码中使用 typedef,必须写在文件的顶部或头文件中
1
2
3
4
5
6
7
8
9
| int main(int argc, const chjar * argv[]) {
int (^sum) (int, int);
sum = ^(int a, int b) { return a + b; };
NSLog(@"%i", sum(1, 2)); // "3"
int (^minus) (int, int);
minus = ^(int a, int b) { return a - b };
NSLog(@"%i", minus(3, 2)); // "1"
}
|
1
2
3
4
5
6
7
8
9
10
| // 使用 typedef
typedef int (^calculte) (int, int);
int main(int argc, const chjar * argv[]) {
calculte sum = ^(int a, int b) { return a + b; };
NSLog(@"%i", sum(1, 2)); // "3"
calculte minus = ^(int a, int b) { return a - b };
NSLog(@"%i", minus(3, 2)); // "1"
}
|
应用场景
- 代码复用
- 高阶函数 (作为参数或返回值)
1
2
3
4
5
6
7
8
| - (void) run:(void (^)())blockFunc {
// ...
blockFunc();
}
run(^{
NSLog(@"block...");
});
|
- 类之间的通信
- 可替代代理委托
- B 类中为 A 类的某个类型为 block 的属性赋值,A 类中会在某个事件中调用这个 block,从而实现跨类通信
注意事项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| // 1. block 中可以访问外部的变量
int a = 10;
void (^myBlock)() = ^{ NSLog(@"%i", a); };
myBlock(); // "10"
// 2. block 中定义了和外部同名的变量,则 blick 内的优先
int a = 10;
void (^myBlock)() = ^{
int a = 20;
NSLog(@"%i", a);
};
myBlock(); // "20"
// 3. 默认情况下,不可以在 block 中修改外界变量的值
// - 因为 block 中的变量和外界的变量不是用一个变量
// - 如果 block 中访问了外界的变量,block 会将外界的这个变量拷贝一份到堆内存中
int a = 10;
void (^myBlock)() = ^{
a = 20; // 修改了外部变量(实际上不是外部的变量),报错
NSLog(@"%i", a);
};
myBlock(); // 报错
// 4. block 会在定义时拷贝外界使用到的变量
int a = 10;
void (^myBlock)() = ^{
NSLog(@"%i", a); // 在这里就将 a 的值拷贝了一份,此时 a 值为 10
};
a = 20; // 不会影响到 block 中拷贝的值
myBlock(); // "10"
// 5.如想在 block 中修改外界变量的值,必须在外界变量前加上 __block
// - 这样如在 block 中修改了外界变量的值,会影响到外界变量的值
// - 因为加了 __block 就是地址传递,所以可修改
__block int a = 10;
void (^myBlock)() = ^{
a = 20;
NSLog(@"%i", a);
};
myBlock(); // "20"
NSLog(@"%i", a); // "20"
|
- block 可存储于堆中也可存储在栈中,默认在栈中
- 如对 block 进行 copy 操作,block 会转移到堆中
- 如 block 在栈中,block 中访问了外界的对象,那么不会对对象进行 retain 操作
- 但是如果 block 在堆中,block 中访问了外界的对象,那么会对外界的对象进行一次 retain 操作
1
2
3
4
5
6
7
8
9
10
11
12
| Person *p = [[Person alloc] init]; // p 引用计数为 1
NSLog(@"%lu", [p retainCount]); // "1"
void (^myBlock)() = ^{
NSLog(@"%@", p); // 由于下面用了 Block_copy,所以这里是 retain,p 引用计数 +1,为 2
NSLog(@"%lu", [p retainCount]); // "2"
};
Block_copy(myBlock); // copy 操作使 block 转移到堆中,此时 block 中访问外部变量会造成 retain
myBlock();
[p release]; // p 引用计数 -1,此时为 1,释放不了
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // 如在 block 中访问了外界的对象,一定要给对象加上 __block,只要加上了
// 哪怕 block 在堆中,也不会对外界的对象进行 retain
__block Person *p = [[Person alloc] init]; // p 引用计数为 1
NSLog(@"%lu", [p retainCount]); // "1"
void (^myBlock)() = ^{
NSLog(@"%@", p); // 由于 p 定义时加上了 __block,不会有 retain,p 引用计数不变为 1
NSLog(@"%lu", [p retainCount]); // "1"
};
Block_copy(myBlock);
myBlock();
[p release]; // p 引用计数 -1,此时为 0,释放了
|
内存管理
block 也是一个对象
MRC
- 只要 block 没有引用外部局部变量,block 放在全局区
- 只要 block 引用外部局部变量,block 则放在栈里
- block 只能使用 copy,不能使用 retain,使用 retain,block 还是在栈中,使用 copy 才会到堆里
ARC
- 只要 block 引用外部局部变量,block 则放在堆里
- block 使用 strong,最好不要使用 copy
循环引用
block 造成循环引用: block 会默认对里面用到的所有外部对象变量全部强引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| _block = ^{
NSLog(@"%@", self); // 强引用了 self,造成该实例不会销毁
};
// 解决: 使用弱引用
__weak typeof(self) weakSelf = self;
_block = ^{
NSLog(@"%@", weakSelf);
// 由于 block 中可能要用到这个 weakSelf
// 而当用到时,可能这个弱指针被销毁了,所以可在定义一个强指针保存它
__strong typeof(weakSelf) strongSelf = weakSelf;
// 之后就可使用这个 strongSelf 啦
};
|
传递变量
1
2
3
4
5
| // 如果是局部变量,block 是值传递
int a = 3;
void (^block)() = ^{ NSLog(@"%d", a); };
a = 5;
block(); // 3
|
1
2
3
4
5
| // 如是静态变量,block 是指针传递
static int a = 3;
void (^block)() = ^{ NSLog(@"%d", a); };
a = 5;
block(); // 5
|
1
2
3
4
5
6
7
8
9
10
| // 如是全局变量,block 是指针传递
int a = 3;
- (void)viewDidLoad {
[super viewDidLoad];
void (^block)() = ^{ NSLog(@"%d", a); };
a = 5;
block(); // 5
}
|
1
2
3
4
5
| // 如是 __block 修饰的变量,block 是指针传递
__block int a = 3;
void (^block)() = ^{ NSLog(@"%d", a); };
a = 5;
block(); // 5
|