iOS底层分析-锁(五)

921 阅读5分钟

这是我参与8月更文挑战的第28天,活动详情查看: 8月更文挑战

NSConditionLock分析

汇编分析NSConditionLock创建

接下来,我们来分析一下NSConditionLock锁的执行:

解析:

  • NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];中的2相当于一个匹配条件;设置为2,也就是说lockWhenCondition2的先执行;所以先打印线程2
  • 打印线程2之后,由于[conditionLock unlockWithCondition:1];将匹配条件改为了1,所以在执行1条件的任务,也就是打印线程1
  • 由于打印线程3的任务没有设置匹配条件,就是一个普通的加锁,缺少条件控制,所以不需要匹配,会先执行,打印线程3;
  • 所以最终打印结果线程3线程2线程1依次打印;

这个时候我们发现NSConditionLock的执行和NSCondition十分相似,都有条件控制;那么我们就不免有些疑问?

  • NSConditionLockNSCondition有什么关系呢
  • 初始化时传入的条件2又是什么东西呢?
  • lockWhenCondition又是如何进行条件控制的呢?
  • unlockWithCondition又做了什么呢?

接下来,我们分别在这四处有疑问的地方打上断点:

按照以往的经验,如果我们想要研究initWithCondition:2的操作,那么我们可以添加initWithCondition的符号断点:

但是我们继续执行代码之后发现,initWithCondition符号断点并没有被拦截到:

这是因为,此处的符号断点不能单纯的设置为initWithCondition,而是应该设置-[NSConditionLock initWithCondition:],我们修改符号断点,然后重新执行查看结果:

成功拦截到符号断点!接下来我们看一下寄存器中的情况:

  • x0为默认参数,消息的接收者:NSConditionLock;
  • x1为默认参数,SEL:initWithCondition:;
  • x2为方法的参数:2

接下来,我们通过汇编代码来分析执行的流程,细枝末节我们可以忽略,那么分析流程的话,我们就只需要关注blreturn指令的执行,因为bl是跳转指令,会有相关的处理,而return;指令会直接返回;我们把所有的bl指令都打上断点,然后继续向下执行,分析NSConditionLockinitWithCondition:方法的执行流程:

此时一个未知的对象,调用了init方法,并传递了参数2; [? init:2]

继续执行:

NSConditionLock对象调用了init方法,并传递了参数2-[NSConditionLock init:2](NSConditionLock初始化)

继续执行:

NSConditionLock对象调用了zone方法;-[NSConditionLock zone](内存开辟)

继续执行:

NSCondition调用了allocWithZone:方法;+[NSCondition allocWithZone:]

继续执行:

NSCondition对象调用了init方法;-[NSCondition init]

继续执行:

此时执行到了return方法,因为返回值放在了x0寄存器;打印x0发现返回了NSConditionLock对象,并且condition2;

-[NSConditionLock initWithCondition:]方法到此处已经return;至此,内存开辟成员变量的处理已经结束;我们可以通过控制台查看一下内存:

  • 0x01000001ffe44209isa;
  • <NSCondition: 0x28340c000>和我们前边汇编中查看到的创建的NSCondition地址一致;<NSCondition: 0x28340c000>成为了<NSConditionLock: 0x2808400f0>的成员变量;

没有看到2,可以继续扩大查看的内存:

我们看到了2的地址;

至此,我们可以大致分析出-[NSConditionLock initWithCondition:]方法的实现:

  • 1、[? init:2]
  • 2、-[NSConditionLock init:2]
  • 3、-[NSConditionLock zone]
  • 4、+[NSCondition allocWithZone:]
  • 5、-[NSCondition init]

-[NSConditionLock initWithCondition:]中,封装了一个NSCondition

但是lockWhenConditionunlockWithCondition方法是怎么进行控制的,我们依然不得而知;

lockWhenCondition与unlockWithCondition分析

接下来,我们分析lockWhenConditionunlockWithCondition,那么就需要添加符号断点-[NSConditionLock lockWhenCondition:]-[NSConditionLock unlockWithCondition:]

lockWhenCondition分析

接下来,我们依然研究bl指令的执行:

NSDate调用了distantFuture方法;

需要注意的是,因为我们是多线程操作,所以我们要注意左侧的线程,我们目前是Thread 7,我们尽量在当前的线程中分析,避免分析到其他线程的逻辑;

继续执行到指令b:

调用了-[NSConditionLock lockWhenCondition:beforeDate:]方法,这个方法有两个参数,意味着我们还需要看一下x3寄存器

从名字_NSConstantDateDistantFuture上我们推测,第二个参数极有可能是NSDate调用了distantFuture的返回值

我们此时,添加符号断点-[NSConditionLock lockWhenCondition:beforeDate:],进入此方法:

其第二个参数值为1;

继续下一个bl指令:

调用了-[NSCondition lock]方法

继续向下执行(步骤过多,分析类似,我们挑主要的分析):

调用了-[NSCondition waitUntilDate]方法,进入等待;

接下来就会跳转到其他线程做操作,此部分我们不分析;再次回来的时候:

调用了unlock方法,并且返回1,也就是true;表示此刻不再等待;

-[NSConditionLock lockWhenCondition]代码大致流程为:

  • 1、+[NSDate distantFuture]
  • 2、-[NSConditionLock lockWhenCondition:beforeDate:]
    • 2.1、-[NSCondition lock]
    • 2.2、-[NSCondition waitUntilDate]
    • 2.3、unlock

接下里分析unlockWithCondition;

unlockWithCondition分析

调用了-[NSCondition lock]方法(加锁)

继续执行:

调用了-[NSCondition broadcast]方法(广播)

调用了-[NSCondition unlock]方法(解锁)

-[NSConditionLock unlockWithCondition]代码大致流程为:

  • 1、-[NSCondition lock]
  • 2、-[NSCondition broadcast]
  • 3、-[NSCondition unlock]

源码对比

因为我们看的是swift版的,所以代码会有些差异,我会直接在代码中进行注释对应的代码;

  • NSConditionLock初始化

  • lockWhenCondition

  • unlockWithCondition

通过源码与我们通过汇编分析出的代码逻辑对比,基本上主要流程都能够分析出来,这也就是我们分析底层源码的一种方式:在未开源的情况下,我们依旧能够通过汇编大致推算出底层代码的实现逻辑;