面试题
1.block的原理是怎样的?本质是什么?
析:封装了函数的调用以及调用环境的OC对象,本质上也是一个oc对象,它内部也有个isa指针。
2.__block的作用是啥?有什么使用注意点?
解决block内部无法修改auto变量的问题。把__block修饰的变量封装成一个对象,放在block内部。
3.block的属性修饰词为什么是copy?使用block有哪些使用注意点?
析:block一旦没有进行copy操作,就不会在堆上,放在堆上是为了控制block的生命周期
使用注意:循环引用问题
4.block在修改NSMutableArray,需不需要添加__block?
为数组增删改的时候不需要
修改指针的对象的时候需要
-
简单的使用
#import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { int age = 10; void (^myBlock)(int,int) =^(int a,int b){ NSLog(@"age=>%d",age); NSLog(@"a=%d",a); NSLog(@"b=%d",b); }; age = 20; myBlock(20,30); NSLog(@"age=>%d",age); } return 0; } --------------------------------------- 01.block的基本使用[25162:7452015] age=>10 01.block的基本使用[25162:7452015] a=20 01.block的基本使用[25162:7452015] b=30 01.block的基本使用[25162:7452015] age=>20
-
block的本质
使用clang编辑器转化为cpp文件: xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
通过分析:main.cpp我们发现
-
Block的变量捕获(capture)
为了保证block内部能够正常访问外部的变量,block有个变量捕获机制
#import <Foundation/Foundation.h>
double height = 175;//全局变量(不会捕获的,而是传进去的是变量名,然后用到的时候是 直接访问的)
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 20;//auto变量的捕获 -->自动变量,离开作用域就销毁
static NSString *name = @"Jack"; //static 此时传递的是变量的地址值
void (^myBlock)(void) = ^(void){
NSLog(@"block内部age =%d",age);
NSLog(@"block内部name = %@",name);
NSLog(@"block内部height = %f",height);
};
age = 30;
NSLog(@"外age =%d",age);
name = @"Lucy";
height = 180;
NSLog(@"外height = %f",height);
myBlock();
}
return 0;
}
打印:
03.block变量捕获[89261:9561310] 外age =30
03.block变量捕获[89261:9561310] 外height = 180.000000
03.block变量捕获[89261:9561310] block内部age =20
03.block变量捕获[89261:9561310] block内部name = Lucy
03.block变量捕获[89261:9561310] block内部height = 180.000000
分析:
1.先将:main.m转化为cpp文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
2.发现:__main_block_func_0
/**************************************************/
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy
NSString **name = __cself->name; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_nn_5r5gqvmx1xz7008s3sn5kfb00000gn_T_main_b5d7dc_mi_1,age);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_nn_5r5gqvmx1xz7008s3sn5kfb00000gn_T_main_b5d7dc_mi_2,(*name));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_nn_5r5gqvmx1xz7008s3sn5kfb00000gn_T_main_b5d7dc_mi_3,height);
}
/**************************************************/
我们发现:
对于age(局部变量auto)、name(局部变量static):都是被捕获到了block里面了(使用了copy啦),并且age是值传递,对于name是指针传递
【而】对于height(全局变量),block内部是直接访问的,传递进去的是变量名
-
block的类型(3种类型)
_ NSGlobalBlock_ (_ NSConcreteGlobalBlock _)
_ NSStackBlock _ (_ NSContreteStackBlock_)
_ NSMallocBlock _ (_ NSContreteMalloclock _ )
我们来看看不同block的存放的内存区域
- 数据段中的
__NSGlobalBlock__
直到程序结束才会被回收 -
__NSStackBlock__
类型的block存放在栈中,在栈中的内存由系统自动分配和释放,作用域执行完毕之后就会被立即释放 -
__NSMallocBlock__
存放在堆中(需要我们自己进行内存管理)
体会
分析:我们从block的本质来看block,本质上也是一个oc对象(里面也有isa)
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^myBlock)(void) = ^(){
NSLog(@"blcok内部");
};
NSLog(@"%@",[myBlock class]);
NSLog(@"%@",[[myBlock class] superclass]);
NSLog(@"%@",[[[myBlock class] superclass] superclass]);
NSLog(@"%@",[[[[myBlock class] superclass] superclass] superclass]);
}
return 0;
}
-----------------------------
04.block的类型[89372:9603049] __NSGlobalBlock__
04.block的类型[89372:9603049] __NSGlobalBlock
04.block的类型[89372:9603049] NSBlock
04.block的类型[89372:9603049] NSObject
接下来:我们具体分析
【在MRC环境下:】
double height = 180;
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block1)(void) = ^{
NSLog(@"Hello");
};
int age = 10;
void (^block2)(void) = ^{
NSLog(@"Hello - %d", age);
};
NSLog(@"%@ %@ %@", [block1 class], [block2 class], [^{
NSLog(@"%d", age);
} class]);
NSLog(@"%@",[[^{
NSLog(@"%d", age);
} copy] class]);
NSLog(@"%@",[^{
NSLog(@"%f", height);
} class]);
NSLog(@"%@",[[^{
NSLog(@"%f", height);
}copy] class]);
}
return 0;
}
结果:
04.block的类型[9877:10384412] __NSGlobalBlock__ __NSStackBlock__ __NSStackBlock__
04.block的类型[9877:10384412] __NSMallocBlock__
04.block的类型[9877:10384412] __NSGlobalBlock__
04.block的类型[9877:10384412] __NSGlobalBlock__
分析:
1.没有访问auto变量的block是__NSGlobalBlock__类型的,存放在数据段中
2.访问了auto变量的block是__NSStackBlock__类型的,存放在栈中
3.__NSStackBlock__类型的block调用copy成为__NSMallocBlock__类型并被复制存放在堆中
对于:NSStackBlock类型的block存放在栈上的
下面我们在来看看一个有趣具体的例子
同样是在【MR】的环境下----
void (^block)(void);//定义一个block变量
void test(){
int a = 10;
block = ^{
NSLog(@"block---------%d", a);
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test();
for (int i=0; i<10; i++) {
NSLog(@"%d",i);
}
block();//发现程序在这里崩溃了 【坏内存访问】
}
return 0;
}
----------------------
void (^block)(void);//定义一个block变量
void test(){
int a = 10;
block = [^{
NSLog(@"block---------%d", a);
} copy];//使用了copy
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test();
for (int i=0; i<10; i++) {
NSLog(@"%d",i);
}
block();
NSLog(@"%@",[block class]);//__NSMallocBlock__
}
return 0;
}
05.blcok的类型2[24991:10706450] 0
05.blcok的类型2[24991:10706450] 1
05.blcok的类型2[24991:10706450] 2
05.blcok的类型2[24991:10706450] 3
05.blcok的类型2[24991:10706450] 4
05.blcok的类型2[24991:10706450] 5
05.blcok的类型2[24991:10706450] 6
05.blcok的类型2[24991:10706450] 7
05.blcok的类型2[24991:10706450] 8
05.blcok的类型2[24991:10706450] 9
05.blcok的类型2[24991:10706450] block---------10
05.blcok的类型2[25002:10708430] __NSMallocBlock__
发现:成功打印了
分析:
在未使用copy的时候,block变量的作用域是test函数的右边的 "}",所以就会出现坏内存访问
而使用了copy之后,有效的延长了block变量的作用域,从最后的打印结果来看,确实是变成了【堆】blcok
那么其他2个类型的block进行了copy操作之后呢?
【MRC环境下】
void (^globalBlock)(void);
void (^mallocBlock)(void);
void test(){
globalBlock = [^{
NSLog(@"没有访问auto变量");
} copy];// __NSStackBlock__ 调用copy 转化为__NSMallocBlock__
int a = 10;
mallocBlock =[[^{
NSLog(@"a=%d",a);
} copy] copy]; // __NSMallocBlock__ 调用copy
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test();
for (int i=0; i<10; i++) {
NSLog(@"i=%d",i);
}
globalBlock();
mallocBlock();
NSLog(@"%@",[globalBlock class]);
NSLog(@"%@",[mallocBlock class]);
}
return 0;
}
打印:
06.block类型copy的作用[25170:10716981] I=0
06.block类型copy的作用[25170:10716981] I=1
06.block类型copy的作用[25170:10716981] I=2
06.block类型copy的作用[25170:10716981] I=3
06.block类型copy的作用[25170:10716981] I=4
06.block类型copy的作用[25170:10716981] I=5
06.block类型copy的作用[25170:10716981] I=6
06.block类型copy的作用[25170:10716981] I=7
06.block类型copy的作用[25170:10716981] I=8
06.block类型copy的作用[25170:10716981] I=9
06.block类型copy的作用[25170:10716981] 没有访问auto变量
06.block类型copy的作用[25170:10716981] a=10
06.block类型copy的作用[25170:10716981] __NSGlobalBlock__
06.block类型copy的作用[25170:10716981] __NSMallocBlock__
至此我们可以得出:
block_06.jpg补充:数据存储位置
#import <Foundation/Foundation.h>
@interface myObject : NSObject
@end
@implementation myObject
@end
int age = 10;
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
NSLog(@"数据段:age %p", &age);
NSLog(@"栈:a %p", &a);
NSLog(@"堆:obj %p", [[NSObject alloc] init]);
NSLog(@"数据段:class %p", [myObject class]);
}
return 0;
}
友情链接:
网友评论