NSNotification 接收通知时所在的线程取决于发送的线程还是注册的线程
发送的线程。
如果是注册的线程,则 NSNotificationCenter 需要维护监听者和其注册时所在线程的表,实现较为复杂;其次注册时所在的线程有可能被销毁,等到接收通知时不一定存在
如果是子线程发送通知,如何在主线程接受通知
1 | [[NSNotificationCenter defaultCenter] addObserverForName:@"MyNoti" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { |
多线程比较
Xcode 编译
基本的编译过程分为四个步骤:
- 预处理(Pre-process):把宏替换,删除注释,展开头文件,产生 .i 文件。
- 编译(Compliling):把之前的 .i 文件转换成汇编语言,产生 .s 文件。
- 汇编(Asembly):把汇编语言文件转换为机器码文件,产生 .o 文件。
- 链接(Link):对.o 文件中的对于其他的库的引用的地方进行引用,生成最后的可执行文件(同时也包括多个 .o 文件进行 link)
内存分布
栈区 0x7 开头
通常存放局部变量、函数参数等。栈在运行时可动态地扩展和收缩
栈是向低地址扩展的数据结构,是一块连续的内存的区域。栈顶的地址和栈的最大容量是系统预先规定好的,栈的大小是一个编译时就确定的常数,如果申请的空间超过栈的剩余空间时,将提示 overflow。因此,能从栈获得的空间较小。
堆区 0x6 开头
那些由 new/alloc 创建的对象所分配的内存块,内存由开发者申请和销毁;堆在运行时可动态地扩展和收缩
堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
静态区(未初始化数据) bss 段
存放未初始化的全局变量和静态变量,程序结束后由系统释放;如 int a;
常量区(已初始化数据) data 段
专门用于存放常量,程序结束后由系统释放;如 int a = 0;
代码区
用于存放程序运行时的代码,代码会被编译成二进制存进内存的程序代码区
全局区又可分为未初始化全局区:.bss 段和初始化全局区:data 段
栈区从上往下走,堆区会从下往上走,当两个相遇的时候,则会发生堆栈溢出
1 | int a = 10; // 全局初始化区 |
卡顿检测
方案一:基于Runloop
Matrix 卡顿监控在 Runloop 的起始最开始和结束最末尾位置添加 Observer,从而获得主线程的开始和结束状态。卡顿监控起一个子线程定时检查主线程的状态,当主线程的状态运行超过一定阈值则认为主线程卡顿,从而标记为一个卡顿。
锁
- 自旋锁
- dispatch_semaphore
- 递归锁
- NSLock
- NSConditionLock
- pthread_mutex 互斥锁
- @synchronized
- dispatch_barrier_async
- atomic
https://bestswifter.com/ios-lock/
内存检测
- hook 掉 UIViewController 和 UINavigationController 的 pop 跟 dismiss 方法
- 调用以下代码,有误判,需要建立白名单
1 | - (BOOL)willDealloc { |