我們會經常看到這樣的代碼:
1 2 3 4 5 6 7 8 9 10 |
- (IBAction )start : ( id )sender { pageStillLoading = YES; [ NSThread detachNewThreadSelector : @selector (loadPageInBackground : )toTarget :self withObject : nil ]; [progress setHidden : NO ]; while (pageStillLoading ) { [ NSRunLoop currentRunLoop ] runMode :NSDefaultRunLoopMode beforeDate : [ NSDate distantFuture ] ]; } [progress setHidden : YES ]; } |
這段代碼很神奇的,因為他會“暫停”代碼運行,而且程式運行不會因為這裡有一個while迴圈而受到影響。在[progress setHidden:NO]執行之後,整個函數想暫停了一樣停在迴圈裡面,等loadPageInBackground裡面的操作都完成了以後才讓[progress setHidden:YES]運行。這樣做就顯得簡介,而且邏輯很清晰。如果你不這樣做,你就需要在loadPageInBackground裡面表示load完成的地方調用[progress setHidden:YES],顯得代碼不緊湊而且容易出錯。
1.NSRunLoop是訊息機制的處理模式
NSRunLoop的作用在於有事情做的時候使的當前NSRunLoop的線程工作,沒有事情做讓當前NSRunLoop的線程休眠
2.nstimer預設添加到當前NSRunLoop中,也可以手動制定添加到自己建立的NSRunLoop的中
[NSTimer schduledTimerWithTimeInterval: target:selector:userInfo:repeats];
此方法預設添加到當前NSRunLoop中
NSTimer *timer = [NSTimer timerWithTimeInterval: invocation:repeates:];
NSTimer *timer = [[NSTimer alloc] initWithFireDate:...];
建立timer [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
注意 timer的釋放
3.NSRunLoop就是一直在迴圈檢測,從線程start到線程end,檢測inputsource(如點擊,雙擊等操作)同步事件,檢測timesource同步事件,檢測到輸入源會執行處理函數,首先會產生通知,corefunction向線程添加runloop observers來監聽事件,意在監聽事件發生時來做處理。
4.runloopmode是一個集合,包括監聽:事件來源,定時器,以及需通知的runloop observers
模式包括:
default模式:幾乎包括所有輸入源(除NSConnection) NSDefaultRunLoopMode模式
mode模式:處理modal panels
connection模式:處理NSConnection事件,屬於系統內部,使用者基本不用
event tracking模式:如組件拖動輸入源 UITrackingRunLoopModes 不處理定時事件
common modes模式:NSRunLoopCommonModes 這是一組可配置的通用模式。將input sources與該模式關聯則同時也將input sources與該組中的其它模式進行了關聯。
每次運行一個run loop,你指定(顯式或隱式)run loop的運行模式。當相應的模式傳遞給run loop時,只有與該模式對應的input sources才被監控並允許run loop對事件進行處理(與此類似,也只有與該模式對應的observers才會被通知)
例:
1).在timer與table同時執行情況,當拖動table時,runloop進入UITrackingRunLoopModes模式下,不會處理定時事件,此時timer不能處理,所以此時將timer加入到NSRunLoopCommonModes模式(addTimer forMode)
2).在scroll一個頁面時來鬆開,此時connection不會收到訊息,由於scroll時runloop為UITrackingRunLoopModes模式,不接收輸入源,此時要修改connection的mode
[scheduleInRunLoop:[NSRunLoop currentRunLoop]forMode:NSRunLoopCommonModes];
5.關於-(BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)date;方法
指定runloop模式來處理輸入源,首個輸入源或date結束退出。
暫停當前處理的流程,轉而處理其他輸入源,當date設定為[NSDate distantFuture](將來,基本不會到達的時間),所以除非處理其他輸入源結束,否則永不退出處理暫停當前處理的流程。
6.while(A){
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
當前A為YES時,當前runloop會一直接收處理其他輸入源,當前流程不繼續處理,出為A為NO,當前流程繼續
7.perform selector在thread中被序列化執行,這樣就緩和了許多在同一個thread中運行多個方法所產生的同步問題。perform selector source在運行完selector後自動從run loop中移除。
當在非main thread中perform selector時,其thread中必須有一個啟用的run loop。對於你自己建立的thread而言,只有你的代碼顯式的運行一個run loop後該perform selector才能得到執行。Run loop在當loop運行時處理所有已排隊的perform selector,而不是在一個loop迴圈時只處理某一個perform selector。
8.performSelector關於記憶體管理的執行原理是這樣的執行 [self performSelector:@selector(method1:) withObject:self.tableLayer afterDelay:3]; 的時候,系統會將tableLayer的引用計數加1,執行完這個方法時,還會將tableLayer的引用計數減1,由於延遲這時tableLayer的引用計數沒有減少到0,也就導致了切換情境dealloc方法沒有被調用,出現了記憶體泄露。
利用如下函數:
[NSObject cancelPreviousPerformRequestsWithTarget:self]
當然你也可以一個一個得這樣用:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(method1:) object:nil]
加上了這個以後,順利地執行了dealloc方法
在touchBegan裡面
[self performSelector:@selector(longPressMethod:) withObject:nil afterDelay:longPressTime]
然後在end 或cancel裡做判斷,如果時間不夠長按的時間調用:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(longPressMethod:) object:nil]
取消began裡的方法
**********************************以下是我在cocoachina中看到的一份總結 轉載過來
線程實現的幾種方式:
1. Operation Objects // NSOperation及相關子類
2. ***** // dispatch_async等相關函數
3. Idle-time notifications // NSNotificationQueue,低優先順序
3. Asynchronous functions // 非同步函數
4. Timers // NSTimer
5. Separate processes // 沒用過
線程建立的成本:
kernel data structures 約1KB
Stack space 512KB(secondary threads)
1MB(iOS main thread)
Creation time 約90 microseconds
Run Loop和線程的關係:
1. 主線程的run loop預設是啟動的,用於接收各種輸入sources
2. 對第二線程來說,run loop預設是沒有啟動的,如果你需要更多的線程互動則可以手動設定和啟動,如果線程執行一個長時間已確定的任務則不需要。
Run Loop什麼情況下使用:
a. 使用ports 或 input sources 和其他線程通訊 // 不瞭解
b. 線上程中使用timers // 如果不啟動run loop,timer的事件是不會響應的
c. 在Cocoa 應用中使用performSelector...方法 // 應該是performSelector...這種方法會啟動一個線程並啟動run loop吧
d. 讓線程執行一個周期性的任務 // 如果不啟動run loop, 線程跑完就可能被系統釋放了
註:timer的建立和釋放必須在同一線程中。
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 此方法會retain timer對象的引用計數。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
如下是一個CFRunLoop的demo 協助理解其使用:
/!!!如果沒有如下的currentLoop 那麼不會執行initPlayer方法中的timer 即執行完playerThread便結束
- (void) playerThread:(id)unused
{
currentLoop = CFRunLoopGetCurrent();//子線程的runloop引用
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];//為子線程建立自動釋放池
[self initPlayer];
CFRunLoopRun();
[pool release];
}
-(void) initPlayer
{
// 在這裡你可以初始化一個工作類,比如聲音或者視頻播放
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self
selector:@selector(checkState:) userInfo:nil repeats:YES];
}
-(void) checkState:(NSTimer*) timer
{
//需要退出自訂的線程了
//if()
//{
//釋放子線程裡面的資源
//釋放資源的代碼....
/*結束子線程任務 CFRunLoopStop,This function forces rl to stop running and
return control to the function that called CFRunLoopRun or CFRunLoopRunInMode
for the current run loop activation. If the run loop is nested with a callout
from one activation starting another activation running, only the innermost
activation is exited.*/
//CFRunLoopStop(currentLoop);
//}
for(int i = 0 ; i < 100; i++)
{
NSLog(@"%d", i);
}
//CFRunLoopStop 會將當前timer結束掉 如果runloop不結束 timer會反覆執行
CFRunLoopStop(currentLoop);
}
-(void)click:(id)sender{
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(playerThread: ) object:nil];
//開啟線程,如果是利用NSOperation,只需要加入到NSOperationQueue裡面去就好了,queue自己會在合適的時機執行線程,而不需要程式員自己去控制。
[thread start];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor whiteColor]];
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10, 10, 100, 50)];
[button setBackgroundColor:[UIColor blueColor]];
[button setTitle:@"abc" forState:UIControlStateNormal];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[button setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
[button setBackgroundColor:[UIColor blueColor]];
[button addTarget:self action:@selector(click:) forControlEvents:UIControlEventTouchDown];
[self.view addSubview:button];
[button release];
}