2008-09-29 49 views
8

不知道是否有人知道,或有指向好的文档,讨论,可可的低层次的实现细节“performSelectorOnMainThread:”方法。performSelectorOnMainThread执行的低层细节:

我最好的猜测是一个我认为可能非常接近的地方,就是它使用mach端口或其上的抽象来提供内部线程通信,并将选择器信息作为mach消息的一部分传递。

对不对?错误?谢谢!

更新09:39 AMPST

谢谢埃文DiBiase和Mecki的答案,但澄清:我理解在运行循环会发生什么,但我正在寻找一个答案是; “其中在方法得到排队?如何被选择器信息获取传递到队列?”寻找比苹果的doc info更多:我读过“时间

更新14:21PST

克里斯·汉森带来了一个好点评论:我在这里的目的并不是学习的基本机制为了在我自己的代码中利用它们。相反,我只是对更好的概念性理解对另一个线程执行代码的信号过程感兴趣。正如我所说的,我自己的研究使我相信,它的花费马赫通讯的优势,为IPC线程之间传递选择的信息,但我专门找上正在发生的事情具体信息,所以我可以肯定我理解事物正确。谢谢!

更新03/06/09

我已经打开了这个问题,一个赏金,因为我真的很想看到它回答,但如果你试图收集请务必阅读一切,包括所有当前提出的答案,这些答案和我原来的问题的评论,以及我上面发布的更新文本。我期待通过performSelectorOnMainThread:等中所使用的机制最低层次的细节,正如我前面提到的,我怀疑它是与马赫端口,但我真的很想知道。赏金将不能获得除非我能确认给出的答案是正确的。感谢大家!

+0

如果你解释了为什么你认为你需要知道使用的确切机制,这可能是有用的。如果你想或者觉得你需要在应用程序代码中依赖它,那么很可能你错了...... – 2008-09-29 20:57:55

+0

好点!在一个编辑中解释,希望能够让我更清楚地了解我所追求的。 – rpj 2008-09-29 21:31:21

回答

10

是的,它使用马赫港口什么情况是这样的:

  1. 数据封装的执行信息(目标对象,选择器,可选的块选择器的对象参数等)被加入到线程的运行循环信息中,这通过0​​来完成,最终使用pthread_mutex_lock
  2. 调用CFRunLoopSourceSignal来指示源已准备好触发。
  3. 调用CFRunLoopWakeUp让主线程的运行循环知道是醒来的时候了。这是使用mach_msg完成的。

从Apple文档:

版本1来源是由运行循环和内核管理。这些信号源使用Mach端口发出信号,表明信号源准备好发射。当消息到达源的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一个断点,你就可以确认。

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。


我还不确定问题是什么。 A 选择器不过是一个包含被调用方法名称的C字符串。每个方法都是一个普通的C函数,并且存在一个字符串表,其中包含方法名称作为字符串和函数指针。这是Objective-C实际工作的基础。

正如我在下面写的,创建了一个NSTimer对象,它获取一个指向目标对象的指针和一个指向包含方法名称的C字符串的指针,并且当定时器触发时,它找到了使用字符串表(因此它需要方法的字符串名称)的目标对象(因此它需要对它的引用)。

不完全执行,但非常接近它:

可可每个线程都有一NSRunLoop(它总是在那里,你永远需要创建一个线程)。 PerformSelectorOnMainThread创建了一个NSTimer对象,如this,一个只触发一次,并且触发时间已经位于过去的位置(因此它需要立即触发),然后获取主线程的NSRunLoop并在那里添加定时器对象。一旦主线程空闲,它将搜索其Runloop中的下一个事件来处理(或者如果没有任何要处理的事情进入休眠状态,并且一旦添加事件就会再次被唤醒)并执行它。主线程在计划呼叫时很忙,在这种情况下,一旦它完成当前任务或当前处于睡眠状态,它将处理计时器事件,在这种情况下,它将通过添加事件而被唤醒并立即处理它。

一个很好的资料来看看苹果是如何最有可能做(没有人可以肯定,因为毕竟它的封闭源码)是GNUStep。由于GCC可以处理Objective-C(它不仅仅是Apple推出的扩展,即使是标准的GCC也可以处理它),但是,如果Obj-C没有苹果发布的所有基本类,那么GNU社区就会尝试重新使用它 - 实现你在Mac上使用的最常见的Obj-C类,它们的实现是OpenSource。

Here您可以下载最近的源码包。

解压缩并查看NSThread,NSObject和NSTimer的实现细节。我想苹果公司并没有这么做,我可以使用gdb来证明它,但是为什么他们会这样做呢?这是一个聪明的方法,效果非常好:)

0

正如Mecki所说,可以用来实现-performSelectorOn…的更一般的机制是NSTimer

NSTimer免费桥接到CFRunLoopTimer。可以在CFLite(CoreFoundation的开源子集; CF-476.14中的Darwin 9.4 source code(CF-476.15,对应于OS X 10.5)中找到实现CFRunLoopTimer - 尽管不一定是实际用于OS X中的正常进程的实现。。5,尚未公布)