Block是iOS4.0+ 和Mac OS X 10.6+ 引进的对C语言的扩展,用来实现匿名函数的特性。他在是一个仿对象,也可以说是一种特殊的对象,他有三种类型:
1.NSGlobalBlock:类似函数,位于text段
2.NSStackBlock:位于栈内存,函数返回后Block将无效;
3.NSMallocBlock:位于堆内存。
第一种类型:说简单一点,就是不使用外部的变量的block块就是NSGlobalBlock,可以把他看待成一个静态block,他在内存中和初始化后的全局变量,静态变量处于同一区域,日常中很少用到、因为没有意义。
第二种类型:和第一种相反,block块如果使用了auto变量,也就是局部变量,那么他就会变成NSStackBlock,他在内存中处于栈内存。在其声明的函数执行完后会被释放掉。
第三种类型:和第二种是进阶的关系,但是要分两种情况下去说明它
1.在MRC下
NSMallocBlock只有在对NSStackBlock进行了copy操作之后,才会得到。别无他法
2.在ARC下
原理跟MRC下差不多,只是在ARC下编译器帮你做了很多 copy 操作 如下:
- 当block作为函数返回值返回的时候
- 将block赋值给__strong指针时
-
block作为Cocoa API中方法名含有usingBlock的方法参数时
-
block作为GCD API的方法参数时
用一张图
对于三类Block做copy、retain、release操作,结果如下
下图直观的展示了三种类型的block的内存管理:
直接上题目:
Example A
void exampleA() {
char a = 'A';
^{
printf("%c\n", a);
}();
}
A:always work
B:only work with ARC
C:always workout ARC
A:never work
Example B
void exampleB_addBlockToArray(NSMutableArray *array) {
char b = 'B';
[array addObject:^{
printf("%c\n", b);
}];
}
void exampleB() {
NSMutableArray *array = [NSMutableArray array];
exampleB_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}
A:always work
B:only work with ARC
C:always workout ARC
A:never work
各位看官慢慢思考!
附上福利图:
最后揭晓答案。
Example C
void exampleC_addBlockToArray(NSMutableArray *array) {
[array addObject:^{
printf("C\n");
}];
}
void exampleC() {
NSMutableArray *array = [NSMutableArray array];
exampleC_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}
A:always work
B:only work with ARC
C:always workout ARC
A:never work
Example D
typedef void (^dBlock)();
dBlock exampleD_getBlock() {
char d = 'D';
return ^{
printf("%c\n", d);
};
}
void exampleD() {
exampleD_getBlock()();
}
A:always work
B:only work with ARC
C:always workout ARC
A:never work
Example E
typedef void (^eBlock)();
eBlock exampleE_getBlock() {
char e = 'E';
void (^block)() = ^{
printf("%c\n", e);
};
return block;
}
void exampleE() {
eBlock block = exampleE_getBlock();
block();
}
A:always work
B:only work with ARC
C:always workout ARC
A:never work
Example F
int multiplier = 7 ;
void (^myBlock)( void ) = ^()
{
multiplier=7;
};
printf ( "%d" , myBlock( ));
问:上面代码有问题吗?有则指出。
福利图! 答案马上揭晓
Example A
在arc和mrc下都可以完美运行。
Example B
在arc下可以运行, 这个block使用了外部变量,他的类型NSStackBlock,位于栈内存,方法结束后本应该无效,然而在arc,block会自动转成mallocblock。所以在方法结束后不会有问题。
mrc: 在这个下面,block被加入到array中,方法结束,block无效,数组中的block成了野指针,在取出时就会报错,所在在mrc下需要这么写 :
void (^block)() = ^{
printf("%c\n", b);
};
[ array addObject:[[block copy ] autorelease]];
Example C:
在arc和mrc下都可以运行
Example D:
在arc下可以完美运行。
在mrc下,连编译都通过不了,因为编译器不允许返回一个localstack ,位于本地栈中的block
Exampe E:
这一题,在arc和mrc下都可以运行,但是与我的想法是不符合的,同理于Example D,应该也会报错
char e = 'E';
void (^block)() = ^{
printf("%c\n", e);
};
2. NSLog(@"%@",^{
printf("%c\n", e);
});
NSLog(@"%@",block);
return block;
}
void exampleE() {
eBlock block = exampleE_getBlock();
NSLog(@"%@",block);
block();
打印如下:
2014-08-02 12:41:19.356 Block_test[1054:303] <__NSStackBlock__: 0x7fff5fbff7c0>
2014-08-02 12:41:19.358 Block_test[1054:303] <__NSStackBlock__: 0x7fff5fbff7e8>
2014-08-02 12:41:19.359 Block_test[1054:303] <__NSStackBlock__: 0x7fff5fbff7e8>
这里如果换成return 2 这个block的话 就会报错, 这里我也不太懂。。。我想区别就在于这两种声明方法的不同,百度了也找了一些 , 没有看到很好的解释,如果有大神在看,希望能给出答案.
Example F:不管在arc 或者 mdc下都会报错.
错误1: block引用局部变量时,只能读不能写。否则会报错,对于static变量和全局变量都可以随意使用.
错误2: argument type ‘void’ is incomplete 会报这个错,意思是返回参赛是void,是不完整的。所以没有返回值的block不能直接打印。
大概就这些! 希望能队读者有帮助,另外如果笔者有不对的地方,请无情的指正吧!~~
后面还会找一些block循环引用的面试题和分析。 尽情期待!