iOS 内存泄漏

我们都知道只要发生循环引用,就在造成内存泄漏,但导致内存泄漏还有很多种情况,且听我述来

文章目录

    • 1.block的循环引用
      • 1.1 手写的block
      • 1.2第三方库的block
        • 1.2.1 AFN的AFHTTPSessionManager
        • 1.2.2 MJRefresh
    • 2.页面push后没有dismiss回来
    • 3.delegate的循环引用
    • 4.非OC对象的内存泄漏
    • 5.NSTimer
    • 6.autoreleasepool

1.block的循环引用

1.1 手写的block

self持有block时,不能在block里使用self或者通过_property访问属性
例如

(ARC环境下)
self.block = ^{
        [self doSomething];	//会造成循环引用
        self.property;		//会造成循环引用
        _property;		//会造成循环引用
    };

因为都造成block持有self,因此需声明weakself,具体怎么做呢,就是__weak typeof(self) weakself = self;这样声明后,在block中用weakself代替self,就不会让block持有self,并且能通过weakself访问到self的方法和属性,双赢。但要注意,引用属性时不能用下划线+属性名,必须weakself.property,因此上面的代码可以换做

__weak typeof(self) weakself = self;
self.block = ^{
        [weakself doSomething];	//不会造成循环引用
        weakself.property;	//不会造成循环引用
    };

1.2第三方库的block

1.2.1 AFN的AFHTTPSessionManager

需要写成单例,否则会循环引用

但有个问题,同一时刻只能有一个网络请求。异步会有问题。当两个线程同时申请manager对象时,肯定有一个manager申请不到,无法网络请求

1.2.2 MJRefresh

好了直接上图

iOS 内存泄漏_第1张图片

解决的方法还是声明weakself

关于更多block的内容,请戳BLOCK 探索

2.页面push后没有dismiss回来

新手往往会犯这种错误,push 到一个节面,返回之前的节面再继续push,这时候之前的界面不会调用delloc,不会被销毁掉,因为只有dismiss才会调用Controller的delloc。

3.delegate的循环引用

iOS 内存泄漏_第2张图片
如图,如果在view里声明代理时用了strong关键词,就会造成循环引用,因此我们一般使用weak关键字修饰delegate,就不会发生循环引用了。
具体就是

@property (nonatomic,weak) id delegate;

4.非OC对象的内存泄漏

iOS内存管理中的ARC只会对OC对象在编译后加上retain、release和autoreleasepool,因此其他的对象就需要开发者手动管理内存了
譬如CoreFoundation对象(C对象) :
只要函数中包含了create\new\copy\retain等关键字, 那么这些方法产生的对象, 就必须在不再使用的时候调用1次CFRelease或者其他release函数、C语言代码中的malloc等需要对应free等都需要注意,否则就不会被释放,造成内存泄漏

5.NSTimer

当我们用到下面这个方法

 (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti  target:(id)aTarget  selector:(SEL)aSelector  userInfo:(id)userInfo  repeats:(BOOL)

aTarget一般传入self,然后便会持有self,当self需要释放时,因为被持有,因此不能释放,出现泄漏,需声明weakSelf;

6.autoreleasepool

@autoreleasepool {}中创建的临时对象在pool销毁时才会销毁生成的对象,因此在一个autoreleasepool里大量添加任务就会内存会飙升,可能会导致内存泄漏,如果有类似需求,则在每一个任务外面加上autoreleasepool,每一个pool结束之后都会销毁对象,内存趋于平稳

@autoreleasepool {
           for(int i=1 ; i<100000; i++){
		//task
	   }
 }

替换成

@autoreleasepool {
           for(int i=1 ; i<100000; i++){
           	@autoreleasepool {
  	   	   //task
   	        }	
	   }
}

什么时候这么做呢
根据苹果官方文档中对 Using Autorelease Pool Blocks 的描述,我们知道在下面三种情况下是需要我们手动添加 autoreleasepool 的:
如果你编写的程序不是基于 UI 框架的,比如说命令行工具;
如果你编写的循环中创建了大量的临时对象;
如果你创建了一个辅助线程。

参考:https://www.jianshu.com/p/f5bc47607613
参考:https://blog.csdn.net/Mr_XiaoJie/article/details/52953807

你可能感兴趣的:(iOS)