原文:http://lianxu.me/2012/11/10-cocoa-objc-newbie-problems/c++
http://blog.csdn.net/dongdongdongjl/article/details/7794050程序員
thread和runloop在之前,開發者根本不太當成一個問題。由於沒有靜態語言裏runloop就是固定的線程執行loop。而如今Cocoa新手搞不明白的太多了,由於沒有從動態角度看它,首先回想一下第2點介紹的runtime概念,接着出一個思考題。設計模式
如今有一個程序片斷以下:xcode
1
2
3
4
5
6
7
8
9
10
11
|
- (void)myThread:(id)sender
{
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
while (TRUE) {
//do some jobs
//break in some condition
usleep(10000);
[pool drain];
}
[pool release];
}
|
如今要求,作某些設計,使得當這個線程運行的同時,還能夠從其它線程裏往它裏面隨意增長或去掉不一樣的計算任務。 這,就是NSRunloop的最原始的開發初衷。讓一個線程的計算任務更加靈活。 這個功能在c, c++裏也許能夠作到可是很是難,最主要的是由於語言能力的限制,之前的程序員不多這麼去思考。安全
好,如今咱們對上面代碼作一個很是簡單的進化:函數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
NSMutableArray *targetQueue;
NSMutableArray *actionQueue;
- (void)myThread:(id)sender
{
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
while (TRUE) {
//do some jobs
//break in some condition
int n=[targetQueue count];
assert(n==[actionQueue count]);
for(int i=0;i<n;i++){
id target=[targetQueue objectAtIndex:i];
SEL action=NSSelectorFromString([actionQueue objectAtIndex:i]);
if ([target respondsToSelector:action]) {
[target performSelector:action withObject:nil];
}
}
usleep(10000);
[pool drain];
}
[pool release];
}
|
注意,這裏沒有作線程安全處理,記住Mutable container is not thread safe. 這個簡單的擴展,讓咱們看到了如何利用runtime能力讓線程靈活起來。當咱們從另外線程向targetQueue和actionQueue同時加入對象和方法時候,這個線程函數就有了執行一個額外代碼的能力。oop
有人會問,哪裏有runloop? 那個是nsrunloop? 看不出來啊。spa
1
2
3
|
while (TRUE) {
//break in some condition
}
|
這個結構就叫線程的runloop, 它和NSRunloop這個類雖然名字很像,但徹底不是一個東西。之前在使用靜態語言開始時候,程序員沒有什麼迷惑,由於沒有NSRunloop這個東西。 我接着來講,這個NSRunloop是如何來得。.net
第二段擴展代碼裏面確實沒有NSRunloop這個玩意兒,咱們接着作第3次改進。 此次咱們的目的是把其中動態部分抽象出來。線程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
@interface MyNSTimer : NSObject
{
id target;
SEL action;
float interval;
CFAbsoluteTime lasttime;
}
- (void)invoke;
@end
@implementation MyNSTimer
- (void)invoke;
{
if ([target respondsToSelector:action]) {
[target performSelector:action withObject:nil];
}
}
@end
#!objc
@interface MyNSRunloop : NSObject
{
NSMutableArray *timerQueue;
}
- (void)addTimer:(MyNSTimer*)t;
- (void)executeOnce;
@end
@implementation MyNSRunloop
- (void)addTimer:(MyNSTimer*)t;
{
@synchronized(timerQueue){
[timerQueue addObject:t];
}
}
- (void)executeOnce;
{
CFAbsoluteTime currentTime=CFAbsoluteTimeGetCurrent();
@synchronized(timerQueue){
for(MyNSTimer *t in timerQueue){
if(currentTime-t.lasttime>t.interval){
t.lasttime=currentTime;
[t invoke];
}
}
}
}
@end
#!objc
@interface MyNSThread : NSObject
{
MyNSRunloop *runloop;
}
- (void)main:(id)sender;
@end
@implementation MyNSThread
- (void)main:(id)sender
{
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
while (TRUE) {
//do some jobs
//break in some condition
[runloop executeOnce];
usleep(10000);
[pool drain];
}
[pool release];
}
@end
|
走到這裏,咱們就算是基本把Runloop結構抽象出來了。例如我有一個MyNSThread實例,myThread1。我能夠給這個實例的線程添加須要的任務,而myThread1內部的MyNSRunloop對象會管理好這些任務。
1
2
3
4
5
|
MyNSTimer *timer1=[MyNSTimer scheduledTimerWithTimeInterval:1 target:obj1 selector:@selector(download1:)];
[myThread1.runloop addTimer:timer1];
MyNSTimer *timer2=[MyNSTimer scheduledTimerWithTimeInterval:2 target:obj2 selector:@selector(download2:)];
[myThread1.runloop addTimer:timer2];
|
當你看懂了上面的代碼也許會感嘆,‘原來是這麼回事啊!爲何把這麼簡單的功能搞這麼複雜呢?’ 其實就是這麼回事,把Runloop抽象出來能夠使得線程任務管理更加loose coupling,給設計模式提供更大的空間。這樣第三方開發者不須要過深刻的涉及線程內部代碼而輕鬆管理線程任務。另外請注意,這裏MyNSRunloop, MyNSTimer等類是我寫得一個模擬狀況,真實的NSRunloop實現確定不是這麼簡單。這裏爲了說明一個思想。這種思想貫穿整個cocoa framework,從界面更新到event管理。
僅當在爲你的程序建立輔助線程的時候,你才須要顯式運行一個 run loop。Run loop 是程序主線程基礎設施的關鍵部分。因此,Cocoa 和 Carbon 程序提供了代碼運 行主程序的循環並自動啓動 run loop。IOS 程序中 UIApplication 的 run 方法(或 Mac OS X 中的 NSApplication)做爲程序啓動步驟的一部分,它在程序正常啓動的時 候就會啓動程序的主循環。相似的,RunApplicationEventLoop 函數爲 Carbon 程序 啓動主循環。若是你使用 xcode 提供的模板建立你的程序,那你永遠不須要本身去顯 式的調用這些例程。
對於輔助線程,你須要判斷一個 run loop 是不是必須的。若是是必須的,那麼 你要本身配置並啓動它。你不須要在任何狀況下都去啓動一個線程的 run loop。比 如,你使用線程來處理一個預先定義的長時間運行的任務時,你應該避免啓動 run loop。Run loop 在你要和線程有更多的交互時才須要,好比如下狀況:
使用端口或自定義輸入源來和其餘線程通訊 使用線程的定時器 Cocoa 中使用任何 performSelector...的方法 使線程週期性工做