关于block内存问题的分析
来源:互联网 发布:淘宝装修队模板免费 编辑:程序博客网 时间:2024/05/17 22:35
话不多说, 先根据代码结果看block到底在内存的哪个分区:
一:
MRC下, 声明的block实现部分,没有引入外界的任何局部变量
int main(int argc, const char * argv[]) {
@autoreleasepool {
//定义bolck
void(^myBlock)() = ^{
NSLog(@"呵呵");
};
//打印block地址
NSLog(@"%@", myBlock);
//回调
myBlock();
}
return 0;
}
//打印结果
19:32:50.813 fenxiang[2174:162871] <__NSGlobalBlock__: 0x100001050>
19:32:50.814 fenxiang[2174:162871]呵呵
此时block位于全局静态区,此时block的内存不需要程序管理,程序运行结束时, 内存被系统回收.
二:
MRC下, 声明的block实现部分, 引入全局变量
int a = 10;
int main(int argc, const char * argv[]) {
@autoreleasepool {
//定义bolck
void(^myBlock)() = ^{
NSLog(@"呵呵, %d", a);
};
//打印block地址
NSLog(@"%@", myBlock);
//回调
myBlock();
}
return 0;
}
//打印结果19:40:20.623 fenxiang[2236:166090] <__NSGlobalBlock__: 0x100001050>
19:40:20.624 fenxiang[2236:166090]呵呵, 10
此时block位于全局静态区, 此时block的内存不需要程序管理, 程序运行结束时, 内存被系统回收.
三: MRC下, 声明的block实现部分, 引入static变量
int main(int argc, const char * argv[]) {
@autoreleasepool {
static int a = 10;
//定义bolck
void(^myBlock)() = ^{
NSLog(@"呵呵, %d", a);
};
//打印block地址
NSLog(@"%@", myBlock);
//回调
myBlock();
}
return 0;
}
//打印结果
19:40:20.623 fenxiang[2236:166090] <__NSGlobalBlock__: 0x100001050>
19:40:20.624 fenxiang[2236:166090] 呵呵, 10
此时block位于全局静态区, 此时block的内存不需要程序管理, 程序运行结束时, 内存被系统回收.
四:这个注意看啦! MRC下, 声明的block实现部分, 引入局部变量(基础类型和对象类型)
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
//定义bolck
void(^myBlock)() = ^{
NSLog(@"呵呵, %d", a);
};
//打印block地址
NSLog(@"%@", myBlock);
//回调
myBlock();
}
return 0;
}
//打印结果
2016-01-22 19:45:55.477 fenxiang[2254:168302] <__NSStackBlock__: 0x7fff5fbff7c8>
2016-01-22 19:45:55.478 fenxiang[2254:168302]呵呵, 10
看结果,变化啦! 当block中引入局部变量(基础类型和对象类型),此时block位于栈区,出了函数作用域, 该内存就被释放掉了,在执行回调的时候, 使用栈区的block很危险,容易造成野指针问题! 怎么解决这个野指针问题呢, 请看第5个例子.
五: MRC下, 声明的block实现部分, 引入局部变量(基础类型和对象类型), 此时block位于栈区, 此时对栈区的block进行copy操作
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
//定义bolck
void(^myBlock)() = ^{
NSLog(@"呵呵, %d", a);
};
//对block进行copy操作
myBlock = [myBlock copy];
//打印block地址
NSLog(@"%@", myBlock);
//回调
myBlock();
}
return 0;
}
//打印结果20:07:44.521 fenxiang[2284:174041] <__NSMallocBlock__: 0x1002068a0>
20:07:44.522 fenxiang[2284:174041]呵呵, 10
看到结果没有, 此时block位于堆区, 我们对栈区的block进行copy操作, 此时block内存就会由栈区迁徙到堆区.
那么如果我们对位于全局静态区的block进行copy, 会出现什么结果呢, 请看例子6.
六: MRC下, 声明的block实现部分, 引入静态变量, 此时block位于全局静态区, 此时对全局静态区的block进行copy操作
int main(int argc, const char * argv[]) {
@autoreleasepool {
static int a = 10;
//定义bolck
void(^myBlock)() = ^{
NSLog(@"呵呵, %d", a);
};
//对block进行copy操作
myBlock = [myBlock copy];
//打印block地址
NSLog(@"%@", myBlock);
//回调
myBlock();
}
return 0;
}
//打印结果20:25:30.420 fenxiang[2306:179169] <__NSGlobalBlock__: 0x100001050>
20:25:30.421 fenxiang[2306:179169]呵呵, 10
结果显示, 此时block依然位于全局静态区. 结合第5个例子, 我们总结: 对栈区的block进行copy操作,此时block内存就会由栈区迁徙到堆区,我们对位于全局静态区的block进行copy,此时还在全局静态区.
下面我们来举一个block内部是对象类型的例子, 因为block内部是对象类型这种情况我们经常用到.结合上面的例子, 如果block内部是局部变量, 那么此时block位于栈区, 然后对栈区的block进行copy操作, 那么block内存就会由栈区迁徙到堆区, 那么block内部的对象会有怎样的改变呢?我们看例子7.
七: MRC下, 声明的block实现部分, 引入局部变量(此时是对象类型, 我们定义一个Person类, 这个类有个age属性), 此时block位于栈区, 此时对栈区的block进行copy操作, 然后打印person对象的引用计数
int main(int argc, const char * argv[]) {
@autoreleasepool {
//创建person类
Person *person1 = [[Person alloc] init];
person1.age = 18;
//定义bolck
void(^myBlock)() = ^{
NSLog(@"呵呵, %ld", (long)person1.age);
};
//对block进行copy操作
myBlock = [myBlock copy];
//打印block地址
NSLog(@"%@", myBlock);
//回调
myBlock();
//打印person的引用计数
NSLog(@"person.retainCount = %lu", (unsigned long)[person1 retainCount]);
[person1 release];
}
return 0;
}
//打印结果20:51:06.924 fenxiang[2372:186493] <__NSMallocBlock__: 0x100206a40>
20:51:06.925 fenxiang[2372:186493]呵呵, 18
20:51:06.925 fenxiang[2372:186493] person.retainCount = 2
看结果, person对象的引用计数为2, 我们在创建person时, alloc一次, 引用计数从0变成1, 那么为什么现在是2呢? 原因就是block的copy, 当栈区的block引用的是对象类型的局部变量,当进行copy, 内存迁徙到堆区的时候,会对所引用的对象类型进行引用计数+1. 根据内存管理原则, alloc一次, release一次, 但release后, 现在引用计数还有个1, 怎么办呢?请看例子8.
int main(int argc, const char * argv[]) {
@autoreleasepool {
//创建person类, 注意, 此时person类由__block修饰
__block Person *person1 = [[Person alloc] init];
person1.age = 18;
//定义bolck
void(^myBlock)() = ^{
NSLog(@"呵呵, %ld", (long)person1.age);
};
//对block进行copy操作, 内存从栈区迁徙到堆区, 这个时候person对象应该引用计数 +1, 但是由于__block的修饰, 它并没有+1
myBlock = [myBlock copy];
//打印block地址
NSLog(@"%@", myBlock);
//回调
myBlock();
//打印person的引用计数
NSLog(@"person.retainCount = %lu", (unsigned long)[person1 retainCount]);
[person1 release];
}
return 0;
}
//打印结果20:51:06.924 fenxiang[2372:186493] <__NSMallocBlock__: 0x100206a40>
20:51:06.925 fenxiang[2372:186493] 呵呵, 18
20:51:06.925 fenxiang[2372:186493] person.retainCount = 1
我们仔细观察代码, 只在例子7的基础上, 加个__block, 让__block修饰person类, 这样person的引用计数问题就解决啦.在这里__block的用处在于block迁徙的时候,告诉系统不要对所引入的对象类型进行引用计数+1.
其实还有个解决办法, Block_release, 请看例子9.
九: MRC下, 声明的block实现部分, 引入局部变量(此时是对象类型, 我们定义一个Person类, 这个类有个age属性), 此时block位于栈区, 此时对栈区的block进行copy操作, 然后打印person对象的引用计数
int main(int argc, const char * argv[]) {
@autoreleasepool {
//创建person类
Person *person1 = [[Person alloc] init];
person1.age = 18;
//定义bolck
void(^myBlock)() = ^{
NSLog(@"呵呵, %ld", (long)person1.age);
};
//对block进行copy操作
myBlock = [myBlock copy];
//打印block地址
NSLog(@"%@", myBlock);
//回调
myBlock();
//打印person的引用计数
NSLog(@"person.retainCount = %lu", (unsigned long)[person1 retainCount]);
[person1 release];
// (MRC)堆区的block内存需要进行释放
Block_release(myBlock);
}
return 0;
}
//打印结果20:51:06.924 fenxiang[2372:186493] <__NSMallocBlock__: 0x100206a40>
20:51:06.925 fenxiang[2372:186493] 呵呵, 18
20:51:06.925 fenxiang[2372:186493] person.retainCount = 2
观察代码, person对象引用计数为2, person对象release一下, block再release一下, 然后ok!说到__block, 就要再说一下block的实质, 请看例子10
十: MRC下, 声明的block实现部分, 引入局部变量, 此时block位于栈区
int main(int argc, const char * argv[]) {
@autoreleasepool {
//定义a
int a = 10;
//定义bolck
void(^myBlock)() = ^{
NSLog(@"呵呵, %d", a);
};
//对a值进行修改
a = 20;
//回调
myBlock();
}
return 0;
}
//打印结果22:08:59.587 fenxiang[2610:209577]呵呵, 10
为什么重新给a赋值, a的值还是10呢? 如果把a的定义改为:__block int a = 10; 打印结果就变成了20;
其实在底层上, block就是指向结构体的指针, 当我们局部变量前面加上static和__block或者是一个全局变量的时候,他们在传入结构体的时候,相当于传入了一个变量的指针,所以当我们在调用之前改变a的值的时候,结果会发生变化.
- 关于block内存问题的分析
- 关于Block的内存问题__上
- 关于block的回调使用-防止内存泄露问题
- 关于copy形式下block的内存泄漏问题
- iOS关于block的内存管理
- 关于深入研究block 出现循环引用的问题和内存泄漏
- iOS Block自身的内存问题
- iOS Block自身的内存问题
- 关于 Block 中捕获 self 的分析
- 关于union的内存对齐问题,从二进制数分析
- 关于一些异常内存溢出的问题分析
- 关于block的一个奇怪问题
- 关于DAMAGE: after Normal block的问题
- 关于DAMAGE: after Normal block的问题
- 关于DAMAGE: after Normal block的问题
- 关于Block循环引用的问题
- 关于block的强弱引用问题
- 关于block的循环引用问题
- [官方教程] Unity 5 BLACKSMITH深度分享 - 汇总帖
- cell自适应高度
- c#多线程与异步开发 (上)
- iOS-分组UITableView删除崩溃问题(当删除section中最后一条数据崩溃的情况)
- Is It A Tree?(并查集问题)
- 关于block内存问题的分析
- CODEVS 1201 最小数和最大数
- 最近找了些理论,可以丰富自己的表达喔
- Android增强的LinearLayout,带分隔线
- Android_YouthArea之ApeendTextView
- struts接收url文件使用upload.parseRequest 获取文件为空的问题
- QT中结合实际例子谈谈QTableWidget中布局控件的方法
- Android App catch Exception
- 复杂类继承体系结构下,实际的类定义