performSelectorOnMainThread的实现的低级详细信息:
想知道是否有人知道Cocoa的" performSelectorOnMainThread:"方法的低级实现细节,或者是否有指向讨论该问题的良好文档的指针。
我的最佳猜测(我认为可能很接近)是,它使用mach端口或者它们顶部的抽象来提供线程内通信,将选择器信息作为mach消息的一部分传递。
正确的?错误的?谢谢!
更新09:39 AMPST
谢谢Evan DiBiase和Mecki的回答,但请我们澄清一下:我了解运行循环中发生的事情,但是我正在寻找答案的是: "方法在哪里排队?选择器信息如何传递到队列中?"除了查找苹果的文档信息外,我还读了很多书
更新14:21PST
克里斯·汉森(Chris Hanson)在评论中提出了一个很好的观点:我的目的不是要学习底层机制以便在我自己的代码中利用它们。而是,我只是对更好地概念性地了解发信号通知另一个线程执行代码的过程感兴趣。就像我说的那样,我自己的研究使我相信IPC利用了Mach消息传递功能在线程之间传递选择器信息,但是我正在专门寻找正在发生的事情的具体信息,因此我可以确定自己正在理解正确的事情。谢谢!
更新03/06/09
我已经开始对此问题悬赏,因为我真的很想看到它的答案,但是如果我们要收集,请确保我们阅读了所有内容,包括所有当前提出的答案,对这些答案和我的原始问题的评论,以及我在上面发布的更新文本。我正在寻找performSelectorOnMainThread:
之类的机制的最底层细节,正如我之前提到的,我怀疑它与Mach端口有关,但我真的很想知道。除非我能确认给出的答案是正确的,否则不会授予赏金。谢谢大家!
解决方案
NSObject的performSelectorOnMainThread:withObject:waitUntilDone:
方法的文档说:
This method queues the message on the run loop of the main thread using the default run loop modes—that is, the modes associated with the NSRunLoopCommonModes constant. As part of its normal run loop processing, the main thread dequeues the message (assuming it is running in one of the default run loop modes) and invokes the desired method.
再编辑一次:
要回答评论问题:
what IPC mechanism is being used to pass info between threads? Shared memory? Sockets? Mach messaging?
NSThread在内部存储对主线程的引用,通过该引用,我们可以获取对该线程的NSRunloop的引用。 NSRunloop在内部是一个链表,并且通过将NSTimer对象添加到runloop中,可以创建一个新的链表元素并将其添加到列表中。因此,我们可以说它是共享内存,实际上属于主线程的链表只是在另一个线程中修改的。有互斥体/锁(甚至可能是NSLock对象)可以确保编辑链接列表是线程安全的。
伪代码:
// Main Thread for (;;) { lock(runloop->runloopLock); task = NULL; do { task = getNextTask(runloop); if (!task) { // function below unlocks the lock and // atomically sends thread to sleep. // If thread is woken up again, it will // get the lock again before continuing // running. See "man pthread_cond_wait" // as an example function that works // this way wait_for_notification(runloop->newTasks, runloop->runloopLock); } } while (!task); unlock(runloop->runloopLock); processTask(task); } // Other thread, perform selector on main thread // selector is char *, containing the selector // object is void *, reference to object timer = createTimerInPast(selector, object); runloop = getRunloopOfMainThread(); lock(runloop->runloopLock); addTask(runloop, timer); wake_all_sleeping(runloop->newTasks); unlock(runloop->runloopLock);
当然,这过于简单了,大多数细节都在这里隐藏在函数之间。例如。如果计时器应该已经触发,则getNextTask将仅返回一个计时器。如果每个计时器的触发日期仍在将来并且没有其他事件要处理(例如,键盘,UI中的鼠标事件或者已发送的通知),则它将返回NULL。
我仍然不确定问题是什么。选择器不过是一个包含被调用方法名称的C字符串。每个方法都是普通的C函数,并且存在一个字符串表,其中包含方法名称(如字符串)和函数指针。这就是Objective-C实际工作的基本原理。
如下所述,创建了一个NSTimer对象,该对象获取指向目标对象的指针和指向包含方法名称的C字符串的指针,并且当计时器启动时,它将使用字符串表找到要调用的正确C方法(因此它需要目标对象的方法的字符串名称)(因此需要引用它)。
不完全是实现,但是很接近它:
Cocoa中的每个线程都有一个NSRunLoop(它总是存在的,我们无需为线程创建on)。 PerformSelectorOnMainThread创建一个这样的NSTimer对象,该对象仅触发一次并且触发时间已经位于过去(因此它需要立即触发),然后获取主线程的NSRunLoop并将计时器对象添加到那里。一旦主线程空闲,它将在其Runloop中搜索下一个要处理的事件(如果没有要处理的事件就进入睡眠状态,并在添加事件后立即将其唤醒)并执行该事件。主线程在计划呼叫时处于繁忙状态,在这种情况下,它将在完成当前任务后立即处理计时器事件,或者在此刻处于睡眠状态,在这种情况下,通过添加事件将其唤醒并立即进行处理。
GNUStep是一个很好的资料来源,可以查看Apple最有可能这样做的方式(没人能肯定地说,毕竟它是封闭的来源)。由于GCC可以处理Objective-C(不仅仅是Apple附带的扩展,甚至标准的GCC都可以处理),但是,没有所有Apple附带的基本类的Obj-C毫无用处,GNU社区试图重新-实现我们在Mac上使用的最常见的Obj-C类,其实现是OpenSource。
在这里,我们可以下载最新的源程序包。
解压缩并查看NSThread,NSObject和NSTimer的实现以了解详细信息。我想苹果公司所做的并没有太大的不同,我也许可以使用gdb来证明这一点,但是为什么他们会比这种方法有很大的不同呢?这是一个很好的方法:)
正如Mecki所说,可以用于实现-performSelectorOn
的更通用的机制是NSTimer
。
" NSTimer"是免费电话桥接到" CFRunLoopTimer"。可以在CFLite(CoreFoundation的开放源代码子集; Darwin 9.4源代码中的CF-476.14程序包。)中找到" CFRunLoopTimer"的实现,尽管不一定是OS X中实际用于正常处理的实现。尚未安装到OS X?10.5.5. )
是的,它确实使用了马赫端口。这是怎么回事:
- 封装执行信息的数据块(目标对象,选择器,选择器的可选对象参数等)被放入线程的运行循环信息中。这是通过使用@synchronized完成的,最终使用了pthread_mutex_lock。
- 调用CFRunLoopSourceSignal表示信号源已准备好触发。
- 调用CFRunLoopWakeUp以使主线程的运行循环知道该唤醒的时间了。这是使用mach_msg完成的。
从Apple文档中:
Version 1 sources are managed by the run loop and kernel. These sources use Mach ports to signal when the sources are ready to fire. A source is automatically signaled by the kernel when a message arrives on the source’s Mach port. The contents of the message are given to the source to process when the source is fired. The run loop sources for CFMachPort and CFMessagePort are currently implemented as version 1 sources.
我现在正在查看堆栈跟踪,这是显示的内容:
0 mach_msg 1 CFRunLoopWakeUp 2 -[NSThread _nq:] 3 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] 4 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:]
在mach_msg上设置一个断点,我们将可以确认它。