對於多線程的開發,iOS系統提供了多種不同的接口,先談談iOS多線程最基礎方面的使用。產生線程的方式姑且分兩類,一類是顯式調用,另一類是隱式調用。
一、顯示調用的類為NSThread。一般構造NSThread的線程對象可通過兩種方式:
1. 初始化線程主方法:
[NSThread detachNewThreadSelector:@selector(run:) toTarget:target withObject:obj];//類方法
或
NSThread *newThread = [[NSThread alloc] initWithTarget:target selector:@selector(run:) object:obj]; //實例方法可以拿到線程對象,便於以后終止線程。
2. 定義NSThread的子類MyThread,然后實現main方法(即方法1中的run)。然后創建新對象:
MyThread *newThread = [[MyThread alloc] init];
啟動線程:[newThread start];
終止線程:實際上沒有真正提供終止線程的api,但有個cancel方法可以利用; 它改變線程運行的一個狀態標志,我們可以這樣來利用:
先在run:或main方法中這樣實現線程循環:
- (void)main
{
// thread init
while (![[NSThread currentThread] isCancelled])
{
// thread loop
[NSThread sleepForTimeInterval:1.0]; //等同於sleep(1);
}
// release resources of thread
}
這時如果調用[newThread cancel]; 就可以終止線程循環了。
NSThread有個類方法exit是用於立即結束當前線程的運行(不安全),因為無法保證當前線程對資源的釋放,所以不推薦使用。像java中Thread的stop方法也被棄用一樣,因為不安全。
二、隱式調用
通過NSObject的Category方法調用,羅列如下:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; //在主線程中運行方法,wait表示是否阻塞這個方法的調用,如果為YES則等待主線程中運行方法結束。一般可用於在子線程中調用UI方法。
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait; //在指定線程中執行,但該線程必須具備run loop。
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg; //隱含產生新線程。
三、NSThread的其它一些常用的方法
創建的線程是非關聯線程(detached thread),即父線程和子線程沒有執行依賴關系,父線程結束並不意味子線程結束。
1. + (NSThread *)currentThread; //獲得當前線程
2. + (void)sleepForTimeInterval:(NSTimeInterval)ti; //線程休眠
3. + (NSThread *)mainThread; //主線程,亦即UI線程了
4. - (BOOL)isMainThread; + (BOOL)isMainThread; //當前線程是否主線程
5. - (BOOL)isExecuting; //線程是否正在運行
6. - (BOOL)isFinished; //線程是否已結束
四、一些非線程調用(NSObject的Category方法)
即在當前線程執行,注意它們會阻塞當前線程(包括UI線程):
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
以下調用在當前線程延遲執行,如果當前線程沒有顯式使用NSRunLoop或已退出就無法執行了,需要注意這點:
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
而且它們可以被終止:
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
五、線程執行順序
通常UI需要顯示網絡數據時,可以簡單地利用線程的執行順序,避免顯式的線程同步:
1. UI線程調用
[threadObj performSelectorInBackground:@selector(loadData) withObject:nil];
2. 子線程中回調UI線程來更新UI
- (void)loadData
{
//query data from network
//update data model
//callback UI thread
[uiObj performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:YES];
}
也可以使用NSThread實現同樣的功能,loadData相當於NSThread的main方法。