想知道是否有人知道或者指向讨论Cocoa的'performSelectorOnMainThread:'方法的低级实现细节的好文档。
我最好的猜测,我认为可能非常接近,就是它使用马赫端口或它们之上的抽象来提供线程内通信,将选择器信息作为马赫消息的一部分传递。
右?错误?谢谢!
更新09:39 AMPST
谢谢Evan DiBiase和Mecki的答案,但要澄清一下:我理解运行循环中会发生什么,但我正在寻找答案的是; “其中是排队的方法? 是如何将选择器信息传递到队列中的?”寻找超过Apple的文档信息:我读过'em
更新14:21PST
Chris Hanson在评论中提出了一个很好的观点:我的目标不是学习底层机制,以便在我自己的代码中利用它们。相反,我只是对更好地概念性地理解另一个线程执行代码的过程感兴趣。正如我所说的,我自己的研究让我相信它利用了IPC的消息传递来在线程之间传递选择器信息,但我特别在寻找关于正在发生的事情的具体信息,所以我可以肯定我正确理解 。谢谢!更新03/06/09
我已经开启了这个问题的赏金,因为我真的希望看到它的回答,但是如果你想收集请确保你阅读一切,包括所有当前提出的答案,评论这些答案和我原来的问题,以及我上面发布的更新文本。我正在寻找performSelectorOnMainThread:
等使用的机制的最低级细节,正如我之前提到的,我怀疑它与Mach端口有关,但我'我真的很想知道。除非我确认给出的答案是正确的,否则不会获得赏金。谢谢大家!
答案 0 :(得分:10)
是的,确实使用了Mach端口。会发生什么:
@synchronized
完成的,最终使用pthread_mutex_lock
。来自Apple文档:
版本1源由运行循环和内核管理。这些信号源使用马赫端口在信号源准备好发射时发出信号。当消息到达源的Mach端口时,内核会自动发信号通知源。当触发源时,消息的内容将被提供给要处理的源。 CFMachPort和CFMessagePort的运行循环源目前实现为版本1源。
我现在正在查看堆栈跟踪,这就是它显示的内容:
0 mach_msg
1 CFRunLoopWakeUp
2 -[NSThread _nq:]
3 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:]
4 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:]
在mach_msg上设置断点,您将能够确认它。
答案 1 :(得分:2)
documentation for NSObject's performSelectorOnMainThread:withObject:waitUntilDone:
method说:
此方法使用默认的运行循环模式(即与NSRunLoopCommonModes常量关联的模式)在主线程的运行循环上对消息进行排队。作为正常运行循环处理的一部分,主线程使消息出列(假设它在一个默认的运行循环模式下运行)并调用所需的方法。
答案 2 :(得分:2)
再一次编辑:
回答评论的问题:
正在使用什么IPC机制 在线程之间传递信息?共享 记忆?套接字?马赫消息?
NSThread在内部存储对主线程的引用,通过该引用,您可以获得对该线程的NSRunloop的引用。 NSRunloop内部是链接列表,通过向runloop添加NSTimer对象,将创建一个新的链接列表元素并将其添加到列表中。所以你可以说它的共享内存,实际上属于主线程的链表,只是在不同的线程中修改。有互斥/锁(甚至可能是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方法table(因此它需要方法的字符串名称)(因此它需要对它的引用)。
不完全是实现,但非常接近它:
Cocoa中的每个线程都有一个NSRunLoop(它总是在那里,你永远不需要为一个线程创建)。 PerformSelectorOnMainThread创建一个像this这样的NSTimer对象,一个只触发一次并且过去已经定位的时间(因此需要立即触发),然后获取主线程的NSRunLoop并添加定时器对象那里。一旦主线程空闲,它就会在其Runloop中搜索下一个要处理的事件(如果没有任何事情要处理,则会进入休眠状态,并在添加事件后再次唤醒并执行它。当您安排呼叫时,主线程正忙,在这种情况下,它将在完成当前任务后立即处理计时器事件,或者此时它正在休眠,在这种情况下,它将通过添加事件来唤醒并立即处理它。
一个很好的资源来查找Apple最有可能做到这一点(没有人可以肯定地说,因为它毕竟是关闭源代码的)是GNUStep。由于GCC可以处理Objective-C(它不仅仅是Apple扩展,只有标准GCC可以处理它),然而,如果没有Apple运送的所有基本类的Obj-C相当无用,那么GNU社区试图重新使用它 - 实现你在Mac上使用的最常见的Obj-C类,它们的实现是OpenSource。
Here您可以下载最近的源包。
解压缩并详细了解NSThread,NSObject和NSTimer的实现。我猜Apple没有做太多不同,我可能用gdb来证明它,但为什么他们会采用与那种方法有很大不同呢?这是一个非常有效的聪明方法:)
答案 3 :(得分:0)
正如Mecki所说,可用于实施-performSelectorOn…
的更一般机制是NSTimer
。
NSTimer
免费桥接到CFRunLoopTimer
。 CFRunLoopTimer
的实现 - 虽然不一定是OS X中正常进程实际使用的实现 - 可以在CFLite(CoreFoundation的开源子集; Darwin 9.4 source code中的包CF-476.14)中找到。(CF与OS X 10.5.5相对应的-476.15尚不可用。)