You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于NSOperation、RunLoop调度及执行顺序的技术问询

针对你在研究苹果ListAdder示例和TN2109时遇到的这三个关于NSOperation、RunLoop和调用序列的问题,我来逐一拆解解答:

问题1:M1、M2、M3是否总会在单次RunLoop循环中被处理?

答案是不一定,这完全取决于旧Operation完成的时机:

  • 如果旧Operation在M1(UITableView删除代理方法)触发后的当前RunLoop循环结束前完成,那么M2(adderOperationDone回调)的消息会被加入主线程RunLoop的common modes队列。RunLoop在处理完当前事件(比如M1和M3)后,会检查队列中的待处理消息,这种情况下M2可能会在同一个RunLoop循环中被处理;
  • 如果旧Operation在当前RunLoop循环进入休眠后才完成,M2的消息会被暂存到主线程队列中,要等RunLoop下次被唤醒(比如下一个UI事件、定时器触发)时才会处理,这时候M1、M3和M2就分属不同的RunLoop循环了。

简单来说,RunLoop的循环是“唤醒→处理事件→休眠”的周期,只有当所有相关事件都在同一个周期内被触发并加入处理队列,才会在单次循环中完成。

问题2:函数adderOperationDone通过performSelectorOnMainThread发送至主线程执行,该队列何时会被执行?

当你调用performSelectorOnMainThread:withObject:waitUntilDone:时,这个方法会把目标选择器的消息加入主线程RunLoop的common modes队列(如果用了带mode参数的重载,可以指定特定mode)。RunLoop会在每次循环的特定阶段处理这些队列里的消息,具体时机是:

  1. RunLoop被唤醒后,先处理定时器事件(NSTimer)和非基于端口的输入源;
  2. 接着处理UI事件(比如触摸、单元格删除这类交互);
  3. 然后就会轮到处理common modes队列里的performSelector消息,包括你的adderOperationDone;
  4. 最后RunLoop进入休眠,等待下一次事件唤醒。

另外要注意:如果调用performSelectorOnMainThread时设置了waitUntilDone:YES,调用线程会阻塞,直到主线程执行完这个选择器;如果是NO,则立即返回,主线程会在上述时机自动处理。只要主线程没有被长时间同步任务阻塞,这个消息会很快被执行。

问题3:是否存在M4插入到M2与M3之间的情况?

完全存在这种可能。主线程RunLoop需要处理多种类型的事件源(UI事件、定时器、performSelector队列、系统回调等),不同事件源的处理优先级和触发时机决定了执行顺序。举个典型场景:

  1. 旧Operation完成,adderOperationDone(M2)被加入主线程common modes队列;
  2. 此时主线程RunLoop正在处理一个定时器事件,或者刚进入休眠就被一个定时器事件(M4)唤醒;
  3. RunLoop会先处理定时器事件M4;
  4. 紧接着用户触发单元格删除,UI事件(M1)被处理,在M1中启动新Operation(M3);
  5. 最后才处理common modes队列里的M2。

这个场景下,执行顺序就是M2→M4→M1→M3,也就是M4插入到了M2和M3之间。只要在M2被处理后、M3被执行前,有任何其他事件源触发了任务,都可能出现这种插入情况。

内容的提问来源于stack exchange,提问作者blueGhost

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:12:32