切图做网站,福建省建设厅网站职业资格,能源门户网站建设,WordPress 经典编辑器##ARC下的内存泄漏 ARC全称叫 ARC(Automatic Reference Counting)。在编译期间#xff0c;编译器会判断对象的使用情况#xff0c;并适当的加上retain和release#xff0c;使得对象的内存被合理的管理。所以#xff0c;从本质上说ARC和MRC在本质上是一样的#xff0c;都是…##ARC下的内存泄漏 ARC全称叫 ARC(Automatic Reference Counting)。在编译期间编译器会判断对象的使用情况并适当的加上retain和release使得对象的内存被合理的管理。所以从本质上说ARC和MRC在本质上是一样的都是通过引用计数的内存管理方式。ARC 的出现大大节省了程序员手动管理内存的时间成本But世上没有完美的事物我们也不要把任何事想的那么美好在 ARC 环境下如果不注意的话也会引起内存泄漏。 目前在项目中引入了MLeaksFinder,能比较清晰的找到内存泄漏的位置。 ##分析一下内存泄漏的主要原因 ####循环引用Retain Cycle 什么是引用循环retain cycle 假设我们有两个实例A和BB是A的一个strong型的property则B的引用计数是1当A的需要释放的时候A则会调用[B release]来释放BB的引用计数则减为0释放。 可如果这时候将B的一个strong型property指向A则A与B互相为强引用问题就来了。因为B强引用AA的引用计数永远不会减为0当A原本的强引用对象被释放以后A和B成为了一个相互引用的孤岛永远不会被释放了这就会引起内存泄漏。 在上面的例子中就是一种非常普遍的引用循环情况加入如上代码的VC在dismiss或者pop以后并不会执行dealloc方法证明内存泄漏了。而引起泄漏的原因就是在作为self的property的block中使用self指针导致self被block强引用形成引用循环。 1、Delegate 我们在使用代理设计模式的时候一定要注意将 delegate 变量声明为 weak 类型像这样 property (nonatomic, weak) idxxxx delegate; 如使用strong或别的类型修饰的话将会导致循环引用导致dealloc()不会被调用。从而触发一些意想不到的后果。 2、Block 目前在项目中出现的内存泄漏大部分是因为block的问题。 在 ARC 下当 block 获取到外部变量时由于编译器无法预测获取到的变量何时会被突然释放为了保证程序能够正确运行让 block 持有获取到的变量向系统声明我要用它你们千万别把它回收了然而也正因 block 持有了变量容易导致变量和 block 的循环引用造成内存泄露 [_sortButton setButtonSpreadPreAction:^BOOL{if (_resultItems.count 0) {[progressHUD showText:xxxx];return NO;}return YES;}];
复制代码这个例子的问题就在于在使用 block 的过程中形成了循环引用self 持有 sortButtonsortButton 持有 blockblock 持有 self。三者形成循环引用内存泄露。 GCD已经一些系统级的API并不会提示循环引用的警告但通过测试发现大部分系统提供block也是需要弱引用的__weak typeof(self) weakSelf self; 项目中除了AFN的第三方组件在调用block时都是需要弱引用的如MJRefresh。 3、NSTimer NSTimer在VC释放前一定要调用[timer invalidate]不调用的后果就是NSTimer无法释放其target如果target正好是self则会导致引用循环。 这里要补充一点引用循环不是只能有两个对象三个四个更多都是可以的甚至环数也不一定只有一个所以要养成良好的代码习惯在NSTimer停用前调用invalidate方法。 关于performSelector:afterDelay的问题 - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
复制代码我们还是看看官方文档怎么说的。 This method sets up a timer to perform the aSelector message on the current thread’s run loop. The timer is configured to run in the default mode (NSDefaultRunLoopMode). When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in the default mode; otherwise, the timer waits until the run loop is in the default mode. 大概意思是系统依靠一个timer来保证延时触发但是只有在runloop在default mode的时候才会执行成功否则selector会一直等待run loop切换到default mode。根据我们之前关于timer 的说法在这里其实调用performSelector:afterDelay:同样会造成系统对target强引用也即retain住。这样子如果selector一直无法执行的话比如runloop不是运行在default model下,这样子同样会造成target一直无法被释放掉发生内存泄露。怎么解决这个问题呢其实很简单我们在适当的时候取消掉该调用就行了系统提供了接口: (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget
复制代码