一、内存管理
在iOS中,使用引用计数来管理OC对象的内存 一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间 调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1 当调用alloc、new、copy、mutableCopy方法返回了一个对象,在不需要这个对象时,要调用release或者autorelease来释放它
AutoreleasePoolPage
每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址 所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起。
为什么要减少内存:
让自己的App可以有更好的用户体验,更快的启动速度,不会因为内存过大而导致 Crash,可以让 App 存活更久等。
Clean Memory:可以用以 Page Out(当内存不足的时候,系统会按照一定策略来腾出更多空间供使用,比较常见的做法是将一部分低优先级的数据挪到磁盘上) 的内存。 Dirty Memory: 是指那些被 App 写入过数据的内存。Heap allocations (所有堆区的对象)、图像解码缓冲区。 Compressed Memory:当内存吃紧的时候,系统会将不使用的内存进行压缩,直到下一次访问的时候进行解压。当我们使用 Dictionary 去缓存数据的时候,假设现在已经使用了 3 页内存,当不访问的时候可能会被压缩为 1 页,再次使用到时候又会解压成 3 页。
像素对齐:
像素分为逻辑像素和物理像素。我们平时使用的是逻辑像素。GPU渲染的时候使用的是物理像素 他们的数值关系是,逻辑像素*[[UIScreen mainScreen] scale] = 物理像素 像素不对齐,指的是物理像素,像素不对齐,导致在GPU进行渲染的时候,会进行插值处理,导致耗时 场景&处理: 1、当加载的图片大小 != 逻辑像素scale的时候 本地图片,让UI切2倍3倍图,要和控件一样大。网络图片可以下载之后进行缩放绘制 2、当创建的UI控件的frame的X、Y和Width、Height分别scale不等于整数的时候 frame的各个元素最好用整数 3、我们在项目中可能会有通过文字计算Label大小的时候,如果label的size计算出来是小数,可能会造成像素不对齐。 计算完毕之后可以取下整ceil() 4、UITableView footer我们不想让他显示的时时候,会设置成0.01.这个时候也会产生像素不对齐。 可以使用CGFLOAT_MIN 5、我们项目中可能会写比例尺相关的操作,计算出来的frame为小数。 为了能够适配frame,建议加上ceil() Xcode 模拟器Debug -> Color Misaligned Images可以查看相关像素不对齐的现象
二、内存优化
1、视图层级很多的情况做一些处理 2、对一些大的数据或者资源的处理 3、对图片的处理,比如格式的选择,或者缩放等。 4、对很多对象的处理 5、避免内存抖动太大,比如可以用@autoreleasepool 处理for循环大量临时对象造成的问题。 6、内存泄露的处理 7、收到内存警告时候做一些处理,比如用 NSCache 代替 NSDictionary,使用 NSPurgableData 代替NSData。让系统在内存不足情况下自己清理内存。
三、weak
objc_initWeak 初始化weak表 objc_storeWeak 存储weak对象的方法
weak被释放为nil,需要对对象整个释放过程了解,如下是对象释放的整体流程: 1、调用objc_release 2、因为对象的引用计数为0,所以执行dealloc 3、在dealloc中,调用了_objc_rootDealloc函数 4、在_objc_rootDealloc中,调用了object_dispose函数 5、调用objc_destructInstance 6、最后调用objc_clear_deallocating。
weak是Runtime维护了一个hash表,用于存储指向某个对象的所有weak指针,Key是所指对象地址,Value是weak指针地址数组。
对象的内存一般被分配到堆上,基本数据类型和oc数据类型一般被分配在栈上。 如果用assign修饰对象,当对象释放后(因为不存在强引用,离开作用域对象内存可能被回收),指针的地址还是存在的,也就是说指针并没有被置为nil,下次再访问该对象就会造成野指针异常。对象是分配在堆上的,堆上的内存由程序员手动释放。
assign修饰基本数据类型或OC数据类型,因为基本数据类型是分配在栈上的,由系统分配和释放,所以不会造成野指针。