CADisplayLink、NSTimer使用注意
CADisplayLink 保证调用频率和刷帧频率一致,60 FPS, 不用设置时间间隔,每秒钟60次。
可以使用 proxy 代理解决循环引用
CADisplayLink、NSTimer 会对 target 产生强引用,如果 target 又对它们产生强引用,那么就会引发循环引用。
解决方案1.使用block
解决方案2.使用代理对象(NSProxy)
NSProxy 也属于基类
代理,用于解决循环引用,用于消息转发,不会在父类查找方法
NSObject 和 NSProxy 区别
GCD定时器
NSTimer 依赖于 RunLoop,如果 RunLoop 的任务过于繁重,可能会导致 NSTimer 不准时
而 GCD 的定时器会更加准时,GCD 定时器,不依赖 Runloop,会很准时,依赖内核。
iOS 程序的内存布局
低地址-> 高地址
保留->代码段->数据段(字符串常量,已初始化全局数据,未初始化数据)>堆->栈内存-> 内核区域
- 代码段编译之后的代码
- 字符串常量
- 通过 alloc malloc calloc 动态分配的内存
- 函数调用开销( )
Tagged Pointer
- 从 64bit 开始,iOS引入了 Tagged Pointer 技术,用于优化 NSNumber、NSDate、NSString 等小对象的存储
- 在没有使用 Tagged Pointer 之前, NSNumber 等对象需要动态分配内存、维护引用计数等,NSNumber 指针存储的是堆中 NSNumber 对象的地址值
- 使用 Tagged Pointer 之后,NSNumber 指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中
- 当指针不够存储数据时,才会使用动态分配内存的方式来存储数据
- objc_msgSend 能识别 Tagged Pointer,比如 NSNumber 的 intValue 方法,直接从指针提取数据,节省了以前的调用开销NSNumber *number = @10;
- NSLog(@“%d\”,[number intValue]);
- iOS平台:最高有效位是1(第64bit)
OC对象的内存管理
在 iOS中,使用引用计数来管理 OC 对象的内存
一个新创建的 OC 对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间
调用 retain 会让 OC 对象的引用计数+1,调用 release 会让OC对象的引用计数 -1
**内存管理的经验总结 **
当调用 alloc、new、copy、mutableCopy 方法返回了一个对象,在不需要这个对象时,要调用 release 或者 autorelease 来释放它
想拥有某个对象,就让它的引用计数 +1;
不想再拥有某个对象,就让它的引用计数 -1
可以通过以下私有函数来查看自动释放池的情况
extern void _objc_autoreleasePoolPrint(void);
copy 和 mutableCopy
引用计数器的存储 retaincount
dealloc
autoreleasePool 自动释放池
自动释放池的主要底层数据结构是:__AtAutoreleasePool、AutoreleasePoolPage
调用了 autorelease 的对象最终都是通过 AutoreleasePoolPage 对象来管理的
源码分析
- -clang 重写@autoreleasepool
- -objc4 源码:NSObject.mm
AutoreleasePoolPage 的结构
调用 push 方法会将一个 POOL_BOUNDARY 入栈,并且返回其存放的内存地址
调用 pop 方法时传入一个 POOL_BOUNDARY 的内存地址,会从最后一个入栈的对象开始发送 release消息,直到遇到这个 POOL_BOUNDARY
id *next 指向了下一个能存放 autorelease 对象地址的区域
runloop 和 autoreleasePool
iOS 在主线程的 Runloop 中注册了2个Observer
- -第1个 Observer监听了 kCFRunLoopEntry 事件,会调用 objc_autoreleasePoolPush()
- 监听了 kCFRunLoopBeforeWaiting事件,会调用 objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
- 点赞
- 收藏
- 分享
- 文章举报
GS-NICE发布了177 篇原创文章 · 获赞 0 · 访问量 2704私信关注