前世-内存管理

最近越来越发觉,自己做了这么久iOS开发,似乎都是在瞎玩,回望这一路写的App,写的代码,以及上线的App,又有几个自己真的非常满意的?

说道这里,不得不提的是:我相信不止我一个人有这种感触,代码写久了,自己都不知道自己在写撒,就连上线都是个意外。

这一路走来我一直在总结,也在回望,到底自己哪里出了问题,终于发现:终究是自己不敢面对而已,底层不了解,只知道敲。

因为换了电脑,换了工作,换了新的环境,也换了一个不一样的心情重新开始探索之路,所以之前的博客地址也停止更新了,如果你对iOS相关知识感兴趣或者有什么疑问和建议可以联系我,或者直接在下面评论,

本来打算这个博客中不会再写iOS的东西,但是最近一直上班,也找了一段时间公司,总之感触不少,现在这个行情,大家都懂的。

前段时间也整理了一些东西,我相信值得一看。

1
原则:没有强指针指向对象,对象就会被释放。

MRC-ARC

1
2
3
ARC:LLVM3.0(iOS5,Xcode4)
前段编译器:方法内创建对象,末尾自动插入release销毁。类拥有对象,在dealloc内释放。更底层的C语言实现。objc_release,objc_retain优化调用过程
ARC优化器:负责移除多余的插入,和一些引用的优化。包括运行期组件。

关于循环

1
2
3
4
5
weak:循环引用,自身强引用,IBOutlet。非拥有,不保留也不释放,置nil,weak必须用于OC对象,assign非OC对象

for循环:只有当自动释放器被release,池中的表示autorrelease对象才会被释放===内存耗尽,没有释放->内存泄露
1.i比较大:使用@autorreleasepool{},在for外面,循环结束,销毁创建对象,解决占据栈内存问题。
2.i玩命大:一次循环都会自动释放池满,@autorreleasepool{}放在for里面,每次循环前将上一次对象release。

关于内存

1
2
内存布局:没有多继承,所以布局简单
最前面isa,指向类。父类实例变量在子类实例变量之前。

关于线程

1
2
3
4
5
6
7
8
9
界面线程维护自己的线程池。自己创建的线程数据,需要创建线程的内存池。

autorreleasepool实现:objc_autorreleasepool=Push,Pop,objc_autorrelease

每次RunLoop完成一个循环的时候,都会检测对象的retainCount,为0则没有使用,释放。

内存管理的范围:集成自NSObject对象,基本数据类型无效。因为存储空间不同,基本数据存在栈区。对象在堆中,代码块结束,涉及局部变量弹栈清空,指向对象指针回收,对象没有指针指向,但是还在堆中,所以内存泄露了。

unowned(unsafe_unretained):对象销毁不会为空,但是更快,因为weak需要unwrap。

常见状态管理

1
2
3
4
5
6
7
野指针:指针变量没有初始化,指向的空间被释放。调用方法报异常,崩溃。release后,地址nil,OC中没有空指针异常
内存泄露:对象提前赋值nil,导致release不起作用。没有配对释放或者清空。栈区释放了,堆区没有释放。最终导致内存溢出
内存溢出:容量超出使用限制
僵尸对象:堆中已经被释放的对象count=0
空指针:指针赋值为nil

判断对象销毁:dealloc(需要super一下),已经释放的对象无法复活

对象关系

1
2
3
集成:
组合:(包含关系),确保成员连边不被提前释放,重写set方法,retain一下。成员变量在dealloc中配对释放。内存泄露:1.set没有retain对象,2.没有release旧对象,3.没有判断set方法传入是否是同一对象
依赖:(对象作为方法参数传递)

autorrelease(pool)/垃圾回收机制

1
2
3
4
autorrelease:把该对象放入自动释放池,自动释放池释放时,内部对象引用计数-1。
NSAutorreleasePool:通过接受对象向他发送的release消息,记录该对象的release消息,自动释放池销毁时,向池中记录release的对象发送release消息。

垃圾回收机制:autorrelease只是延迟释放,GC是每隔一段时间询问程序,是否有无指针指向的对象,没有就释放

自动释放池

1
2
3
4
5
6
自动释放池:
1.存储多想对象类型的指针变量(可以嵌套)
2.作用:将对象与自动释放池建议关系,池子内调用autorrelease,在自动释放池销毁时销毁对象,延迟release销毁时间
3.对池内对象作用:存入池中的对象,池销毁,全部对象release一次
4.调用autorrelease将对象加入自动释放池,多次调用导致野指针异常
5.释放时机:简单:autorrelease的}执行完后。实际:Autorrelease对象是在当前RunLoop迭代结束时释放,原因是:系统在每个RunLoop迭代中加入了自动释放池Push,Pop

关键字

1
2
3
4
5
block中多次使用weakSelf(延迟操作,导致取不到弱指针),可以block种先使用strongSelf,防止执行是weakSelf意外释放,对于非ARCweak改为block就可以

release和drain:ARC中一样,GC中release无效操作,所以无论是否为GC使用drain没有问题。

copy:OC对象类型如果有mutable,深拷贝,新对象为count=1,没有为浅拷贝,count+1.

其他总结

1
2
3
4
5
6
7
8
9
10
11
通过Observer监听RunLoop状态,一旦监听到RunLoop即将进入休眠等状态,就释放自动释放池。

FIFO:新访问的数据插入队列尾部,数据在队列中移动,淘汰头部数据。LRU(FIFO相反),LFU

循环引用:定时器(timer作为类的成员变量,self-target,不使用记得invalidate),Blcok(block在copy时对内部对象强引用(ARC)或者引用计数+1(MRC)),代理:(assign(MRC),weak(ARC))

通知:多对多,主要跨层传值。对象加入到通知中心后,对象被销毁前没有将对象从通知中心移除,当再次发送通知的时候,会崩溃。

默认关键字:基本数据类型(atomic,readwrite,assign),OC对象类型(atomic,readwrite,strong)

TableView代理用assign:控制器对内部的View进行了一次retain,TableView对代理控制器也retain一次就会循环引用。

其实知道了这个并不证明就能写出好的代码,还需要时间的沉淀,不断的尝试,不断的思考与总结。感谢你能看到最后,希望对你有用,我们下次再见!

坚持原创技术分享,您的支持将鼓励我继续创作!