NSNotification 接收通知时所在的线程取决于发送的线程还是注册的线程

发送的线程。

如果是注册的线程,则 NSNotificationCenter 需要维护监听者和其注册时所在线程的表,实现较为复杂;其次注册时所在的线程有可能被销毁,等到接收通知时不一定存在

如果是子线程发送通知,如何在主线程接受通知

1
2
3
4
5
6
7
[[NSNotificationCenter defaultCenter] addObserverForName:@"MyNoti" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"我在主线程接收到通知");
}];

dispatch_async(dispatch_get_global_queue(0, 0), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:@"MyNoti" object:nil];
});

多线程比较

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
2
3
4
5
6
7
8
9
10
11
12
int a = 10;  // 全局初始化区
char *p; // 全局未初始化区

int main()
{
int b; // 栈
char s[] = "abc" // 栈
char *p1; // 栈
char *p2 = "123456"; // 123456 在常量区,p2 在栈上。
static int c =0// 全局(静态)初始化区
int array = new int[10]; // 堆
}

卡顿检测

  1. 方案一:基于Runloop
    Matrix 卡顿监控在 Runloop 的起始最开始和结束最末尾位置添加 Observer,从而获得主线程的开始和结束状态。卡顿监控起一个子线程定时检查主线程的状态,当主线程的状态运行超过一定阈值则认为主线程卡顿,从而标记为一个卡顿。

  2. 子线程 Ping

  • 自旋锁
  • dispatch_semaphore
  • 递归锁
  • NSLock
  • NSConditionLock
  • pthread_mutex 互斥锁
  • @synchronized
  • dispatch_barrier_async
  • atomic

https://bestswifter.com/ios-lock/

内存检测

  1. hook 掉 UIViewController 和 UINavigationController 的 pop 跟 dismiss 方法
  2. 调用以下代码,有误判,需要建立白名单
1
2
3
4
5
6
7
- (BOOL)willDealloc {
__weak id weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf assertNotDealloc];
});
return YES;
}
  1. NSNotification 接收通知时所在的线程取决于发送的线程还是注册的线程
  2. 如果是子线程发送通知,如何在主线程接受通知
  3. 多线程比较
  4. Xcode 编译
  5. 内存分布
  6. 卡顿检测
  7. 内存检测