由于前段时间我负责的app 严重发热,由于上线时间短,所以出现了一些问题 结合网上一些教程我这边也仔细检查,检查的结果原因如下。可能有些地方写得不对,大神勿喷,请订正.传送门
1.Block循环引用问题
在苹果使用ARC管理之前,Block的内存管理(block如何进行内存管理的)需要区分是Global(全局)、Stack(栈)还是Heap(堆)。而在使用了ARC之后,苹果自动会将所有原本应该放在栈中的Block全部放到堆中,所以这使得我们现在的讨论可以省去很大一部分的麻烦。下面我们就只讨论ARC环境下全局Block和堆Block的内存管理。
Block的循环引用是比较容易被忽视,原本也是相对比较难检查出来的问题。当然现在苹果在XCode编译的层级就已经做了循环引用的检查,所以这个问题的检查就突然变的没有难度了。简单说一下循环引用出现的原理:Block的拥有者在Block作用域内部又引用了自己,因此导致了Block的拥有者永远无法释放内存,就出现了循环引用的内存泄漏
@interface ObjTest () {
NSInteger testValue;
}
@property (copy, nonatomic) void (^block)();
@end
@implement ObjTest
- (void)function {
self.block = ^() {
self.testValue = 100; 这里就会发生内存泄漏问题
};
}
@end
ObjTest拥有了一个名字叫block的Block对象;然后在这个Block中,又对ObjTest的一个成员变量testValue进行了赋值。于是就产生了循环引用:ObjTest->block->ObjTest。
要避免循环引用的关键就在于破坏这个闭合的环。在目前只考虑ARC环境的情况下,笔者所知的只有一种方法可以破坏这个环:在Block内部对拥有者使用弱引用。
@interfaceObjTest () {
NSInteger testValue;
}
@property (copy, nonatomic)void(^block)();
@end
@implement ObjTest
- (void)function {
__weak ObjTest* weakSelf =self;
self.block= ^() {weakSelf.testValue=100;
};
}@end
在Block外创建一个对于self的弱引用,然后在Block内引用self的地方全部使用这个弱引用。这样就使得Block内部不会对self本身做引用计数+1的操作。那样就可以打破循环引用的环了。
2.AFN框架和MJRefresh上下拉刷新(其实都是block引发的命案)
在封装网络请求类时需注意的是需要将请求队列管理者AFHTTPSessionManager声明为单例创建形式。对于该问题,AFNetWorking的作者在gitHub上也指出建议使用者在相同配置下保证AFHTTPSessionManager只有一个,进行全局管理,因此我们可以通过单例形式进行解决。下方展示部分核心代码:
然后在说MJRefresh(参考:传送门)
一般的我们是怎么使用MJRefresh 这个玩意的 看代码
self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
self.page =1;
[self.dataArr removeAllObjects];
[self loadData];
}];
若在MJRefresh的执行Block中调用当前self或其所属属性,一定要注意循环引用问题。我们简单分析下MJRefresh为什么会造成循环引用问题:点击进入headerWithRefreshingBlock对应方法即可
#pragmamark- 构造方法
+ (instancetype)headerWithRefreshingBlock:(MJRefreshComponentRefreshingBlock)refreshingBlock{
MJRefreshHeader *cmp= [[selfalloc] init];
cmp.refreshingBlock= refreshingBlock;
return cmp;
}
这里仅有三行代码,无非就是创建了下拉刷新部分View然后返回,这里比较重要的是cmp.refreshingBlock = refreshingBlock;这一句,这里的refreshingBlock是属于MJRefreshHeader的强引用属性,最后header会成为我们自己tableView的强引用属性mj_header,也就是说self.tableView强引用header, header强引用refreshingBlock,如果refreshingBlock里面强引用self,就成了循环引用,所以必须使用weakSelf,破掉这个循环.如何解决直接看传送们.
3.大次数循环内存暴涨问题(你永远都不知道会存在这里的内存泄漏)
for(inti =0; i <100000; i++) {
NSString *string= @"Abc";
string= [string lowercaseString];
string= [stringstringByAppendingString:@"xyz"]; //引起内存飙涨
NSLog(@"%@",string);
}
该循环内产生大量的临时对象,直至循环结束才释放,可能导致内存泄漏,解决方法为在循环中创建自己的autoReleasePool(autoReleasePool都干了啥,这么🐂),及时释放占用内存大的临时变量,减少内存占用峰值。
for(inti =0; i < 100000; i++) {
@autoreleasepool{
NSString *string= @"Abc";
string= [stringlowercaseString];string= [stringstringByAppendingString:@"xyz"];
NSLog(@"%@",string);}
}
附、如何检测App的内存泄漏问题
1、借助Xcode自带的Instruments工具(选取真机测试)
2、简单暴力的重写dealloc方法,加入断点或打印判断某类是否正常释放。
3、通过Facebook出品的FBMemoryProfiler工具类进行检测,感兴趣的童鞋可进行了解。
4、更多关于如何进行app性能优化请看:微信读书 iOS 性能优化总结 MLeaksFinder简介