-
Block为什么要用copy
- block在创建的时候默认分配的内存是在栈上,而不是在堆上。这样的话其本身的作用域是属于创建时候的作用域,一旦在创建的作用域之外调用就会导致程序的崩溃。所以使用了copy将其拷贝到堆内存上。
- block创建在栈上,而block的代码中可能会用到本地的一些变量,在栈上可能随时会被系统释放掉,只有将其拷贝到堆上,才能一直保持Block的存在,并用这些外部变量
-
Block为什么不用retain
- retain只是增加了一次引用计数,block的内存还是在栈上,并没有存在堆上,存在栈上的block可能随时被系统回收
-
为什么进入block中的对象引用计数需要自动加1?
- Block执行的是回调,因此block并不知道其中的对象obj创建后会在什么时候被释放,为了不在block使用obj之前,对象已经被释放,block就retain了obj一次
-
block和函数的关系
- Block的使用很像函数指针,不过与函数最大的不同是Block可以访问函数以外、词法作用域以内的外部变量的值。换句话说,Block不仅 实现函数的功能,还能携带函数的执行环境。
- 函数指针
void(*fun)(int)
- block
void(^fun)(int)
-
block本质(对于block的理解)请查阅Block实现原理
- block实际上是: 指向结构体的指针
- 编译器会将block的内部代码生成对应的函数
-
对于基本数据类型,进入到block中会被当做常量处理。对象 retain 会一次
//如果需要在block中对num进行修改,需要加上关键字__block
//(我们也可以用static关键字进行修饰) int num1 = 10; void(^block1)() = ^{ NSLog(@"num1 is %d",num1); }; num1 = 20; block1(); //输出10
//改进:使用block,使进入到block块中的变量不被当做常量来使用 __block int num2 = 10; void(^block2)() = ^{ NSLog(@"num2 is %d",num2); }; num2 = 20; block2(); //输出20 ```
-
Block中self的循环引用
- block默认创建在栈上,所以对要对其进行执行copy操作,将其拷贝到堆区,便于更好的操作对象。但是执行了copy操作之后,block中使用self,此对象会被retain一次(注意:block在堆区上时才会起到retain作用),会造成循环引用。
- 解决方法:
- 在MRC下,使用__block修饰
- 在ARC下,使用__unsafe_unretained\weak修饰
-
Block 在内存中的分类
- 全局block --> GlobalBlock <==> 相当于全局变量, 系统会自动释放
- 栈block --> StackBlock <==> 相当于局部变量, 系统会自动释放
- 堆block --> MallocBlock <==> (需要手动释放内存)
-
block类型区分方法
- 如果block实现中没有访问任何"外部"变量(包括局部和全局), 该block为GlobalBlock
- 如果block实现中访问了任何"外部"变量(包括局部和全局), 该block为StackBlock
- 对StackBlock进行拷贝(copy/Block_copy), 该block为MallocBlock
-
block内如何修改block外 的变量?
__block int a = 0; void (^foo)(void) = ^{ a = 1; }; foo(); //这里,a的值被修改为1
默认情况下,block内访问的变量是 copy 该变量到堆后的变量,而非变量本身。 所以:读写操作对原变量不生效
我们知道:Block不允许修改外部变量的值,这里所说的外部变量的值,指的是栈中指针的内存地址。__block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值
__block int a = 0; NSLog(@"定义前:%p", &a); //栈区 void (^foo)(void) = ^{ a = 1; NSLog(@"block内部:%p", &a); //堆区 }; NSLog(@"定义后:%p", &a); //堆区 foo();
打印输出:
2016-05-17 02:03:33.559 LeanCloudChatKit-iOS[1505:713679] 定义前:0x16fda86f8 2016-05-17 02:03:33.559 LeanCloudChatKit-iOS[1505:713679] 定义后:0x155b22fc8 2016-05-17 02:03:33.559 LeanCloudChatKit-iOS[1505:713679] block内部: 0x155b22fc8
-
block个人理解
NSMutableString *kName = [NSMutableString stringWithString:@"kael"]; __block NSMutableString *myname = kName; NSLog(@"block 前%p",&myname);
void (^foo)(void) = ^(){
NSLog(@"block 中1:%p",&myname);
myname = [NSMutableString stringWithString:@"kaelinda"];
NSLog(@"block 中2:%p",&myname);
};
foo();
NSLog(@"block 后%p",&myname);
/**
* __block 修饰之前
* 外部变量的内存地址是存到了栈
* block 是在堆内,不清楚栈内的变量是否已经被释放,所以连引用的准确性都是问题,更谈不上是修改了
* 所以,block 不允许修改栈中的变量(或者栈中指针的内存地址)
*/
/**
* __block 修饰之后
* 初始化的时候,内存地址在栈内(因为并不确定block内是否会用到),
* 当检测到block内引用到了该对象,不管block有没有被调用,都会 从栈内 copy 内存地址到 堆内,
* 再操作的时候 操作的是堆内的内存地址
*/
```