iPhone SDK 多線程使用方法以及注意事項(xiàng)
iPhone SDK 多線程使用方法以及注意事項(xiàng)是本文要介紹的內(nèi)容,不多說,直接進(jìn)入話題。雖然現(xiàn)在大部分PC應(yīng)用程序都支持多線程/多任務(wù)的開發(fā)方式,但是在iPhone上,Apple并不推薦使用多線程的編程方式。但是多線程編程畢竟是發(fā)展的趨勢,而且據(jù)說即將推出的iPhone OS4將全
雖然現(xiàn)在大部分PC應(yīng)用程序都支持多線程/多任務(wù)的開發(fā)方式,但是在iPhone上,Apple并不推薦使用多線程的編程方式。但是多線程編程畢竟是發(fā)展的趨勢,而且據(jù)說即將推出的iPhone OS4將全面支持多線程的處理方式。所以說掌握多線程的編程方式,在某些場合一定能挖掘出iPhone的更大潛力
從例子入手
先從一個(gè)例程入手,具體的代碼參考了這里。還有例程可以下載。多線程程序的控制模型可以參考這里,一般情況下都是使用 管理者/工人模型, 這里,我們使用iPhone SDK中的 NSThread 來實(shí)現(xiàn)它。
首先創(chuàng)建一個(gè)新的 View-based application 工程,名字為 "TutorialProject" 。界面如下圖所示,使用UILabel實(shí)現(xiàn)兩部分的Part(Thread Part和Test Part),Thread Part中包含一個(gè)UIProgressView和一個(gè)UIButton;而Test Part包含一個(gè)值和一個(gè)UISlider。如圖:
接下來,在 TutorialProjectViewController.h 文件中創(chuàng)建各個(gè)UI控件的 IBOutlets.
- @interface TutorialProjectViewController : UIViewController {
- // ------ Tutorial code starts here ------
- // Thread part
- IBOutlet UILabel *threadValueLabel;
- IBOutlet UIProgressView *threadProgressView;
- IBOutlet UIButton *threadStartButton;
- // Test part
- IBOutlet UILabel *testValueLabel;
- // ------ Tutorial code ends here ------
- }
同時(shí),也需要創(chuàng)建outlets變量的property.
- @property (nonatomic, retain) IBOutlet UILabel *threadValueLabel;
- @property (nonatomic, retain) IBOutlet UIProgressView *threadProgressView;
- @property (nonatomic, retain) IBOutlet UIProgressView *threadStartButton;
- @property (nonatomic, retain) IBOutlet UILabel *testValueLabel;
接下來定義按鈕按下時(shí)的動作函數(shù),以及slider的變化函數(shù)。
- - (IBAction) startThreadButtonPressed:(UIButton *)sender;
- - (IBAction) testValueSliderChanged:(UISlider *)sender;
- 然后在 TutorialProjectViewController.m 文件中synthesize outlets,并在文件為實(shí)現(xiàn)dealloc釋放資源。
- @synthesize threadValueLabel, threadProgressView, testValueLabel, threadStartButton;
- ...
- - (void)dealloc {
- // ------ Tutorial code starts here ------
- [threadValueLabel release];
- [threadProgressView release];
- [threadStartButton release];
- [testValueLabel release];
- // ------ Tutorial code ends here ------
- [super dealloc];
- }
現(xiàn)在開始線程部分的代碼,首先當(dāng) thread button 被按下的時(shí)候,創(chuàng)建新的線程.
- - (IBAction) startThreadButtonPressed:(UIButton *)sender {
- threadStartButton.hidden = YES;
- threadValueLabel.text = @"0";
- threadProgressView.progress = 0.0;
- [NSThread detachNewThreadSelector:@selector(startTheBackgroundJob) toTarget:self withObject:nil];
- }
該按鈕被按下后,隱藏按鈕以禁止多次創(chuàng)建線程。然后初始化顯示值和進(jìn)度條,最后創(chuàng)建新的線程,線程的函數(shù)為 startTheBackgroundJob。具體的 startTheBackgroundJob 函數(shù)定義如下.
- - (void)startTheBackgroundJob {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- // 線程開始后先暫停3秒(這里只是演示暫停的方法,你不是必須這么做的)
- [NSThread sleepForTimeInterval:3];
- [self performSelectorOnMainThread:@selector(makeMyProgressBarMoving) withObject:nil waitUntilDone:NO];
- [pool release];
- }
在第1行,創(chuàng)建了一個(gè) NSAutoreleasePool 對象,用來管理線程中自動釋放的對象資源。這里 NSAutoreleasePool 在線程退出的時(shí)候釋放。這符合 Cocoa GUI 應(yīng)用程序的一般規(guī)則。最后一行,阻塞調(diào)用(waitUntilDone狀態(tài)是ON)函數(shù) makeMyProgressBarMoving。
- - (void)makeMyProgressBarMoving {
- float actual = [threadProgressView progress];
- threadValueLabel.text = [NSString stringWithFormat:@"%.2f", actual];
- if (actual < 1) {
- threadProgressView.progress = actual + 0.01;
- [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(makeMyProgressBarMoving) userInfo:nil repeats:NO];
- }
- else threadStartButton.hidden = NO;
- }
這里計(jì)算用于顯示的進(jìn)度條的值,利用 NSTimer ,每0.5秒自增0.01,當(dāng)值等于1的時(shí)候,進(jìn)度條為100%,退出函數(shù)并顯示剛才被隱藏的按鈕。
最后,添加 UISlider 的實(shí)現(xiàn)函數(shù),用來更改主線程中 Test Part 中的 label 值。
- - (IBAction) testValueSliderChanged:(UISlider *)sender {
- testValueLabel.text = [NSString stringWithFormat:@"%.2f", sender.value];
- }
編譯執(zhí)行,按下線程開始按鈕,你將看到進(jìn)度條的計(jì)算是在后臺運(yùn)行。如圖:
使用線程的注意事項(xiàng)
線程的堆棧大小
iPhone設(shè)備上的應(yīng)用程序開發(fā)也是屬于嵌入式設(shè)備的開發(fā),同樣需要注意嵌入式設(shè)備開發(fā)時(shí)的幾點(diǎn)問題,比如資源上限,處理器速度等。
iPhone 中的線程應(yīng)用并不是無節(jié)制的,官方給出的資料顯示iPhone OS下的主線程的堆棧大小是1M,第二個(gè)線程開始都是512KB。并且該值不能通過編譯器開關(guān)或線程API函數(shù)來更改。
你可以用下面的例子測試你的設(shè)備,這里使用POSIX Thread(pthread),設(shè)備環(huán)境是 iPhone 3GS(16GB)、SDK是3.1.3。
- #include "pthread.h"
- void *threadFunc(void *arg) {
- void* stack_base = pthread_get_stackaddr_np(pthread_self());
- size_t stack_size = pthread_get_stacksize_np(pthread_self());
- NSLog(@"Thread: base:%p / size:%u", stack_base, stack_size);
- return NULL;
- }
- - (void)applicationDidFinishLaunching:(UIApplication *)application {
- void* stack_base = pthread_get_stackaddr_np(pthread_self());
- size_t stack_size = pthread_get_stacksize_np(pthread_self());
- struct rlimit limit;
- getrlimit(RLIMIT_STACK, &limit);
- NSLog(@"Main thread: base:%p / size:%u", stack_base, stack_size);
- NSLog(@" rlimit-> soft:%llu / hard:%llu", limit.rlim_cur, limit.rlim_max);
- pthread_t thread;
- pthread_create(&thread, NULL, threadFunc, NULL);
- // Override point for customization after app launch
- [window addSubview:viewController.view];
- [window makeKeyAndVisible];
- }
結(jié)果如下:
模擬器
- Main thread: base:0xc0000000 / size:524288
- rlimit-> soft:8388608 / hard:67104768
- Thread: base:0xb014b000 / size:524288
設(shè)備
- Main thread: base:0x30000000 / size:524288
- rlimit-> soft:1044480 / hard:1044480
- Thread: base:0xf1000 / size:524288
由此可見,當(dāng)你測試多線程的程序時(shí),模擬器和實(shí)際設(shè)備的堆棧大小是不一樣的。如果有大量遞歸函數(shù)調(diào)用可要注意了。
Autorelease
如果你什么都不考慮,在線程函數(shù)內(nèi)調(diào)用 autorelease 、那么會出現(xiàn)下面的錯(cuò)誤:
- NSAutoReleaseNoPool(): Object 0x********* of class NSConreteData autoreleased with no pool in place ….
一般,在線程中使用內(nèi)存的模式是,線程最初
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
而在線程結(jié)束的時(shí)候 [pool drain] 或 [pool release]。
子線程中描畫窗口
多線程編程中普遍遵循一個(gè)原則,就是一切與UI相關(guān)的操作都有主線程做,子線程只負(fù)責(zé)事務(wù),數(shù)據(jù)方面的處理。那么如果想在子線程中更新UI時(shí)怎么做呢?如果是在windows下,你會 PostMessage 一個(gè)描畫更新的消息,在iPhone中,需要使用performSelectorOnMainThread 委托主線程處理。
比如,如果在子線程中想讓 UIImageView 的 image 更新,如果直接在線程中
- imageView.image = [UIImage imageNamed:@"Hoge.png"];
這么做,什么也不會出現(xiàn)的。需要將該處理委托給主線程來做,像下面:
- [delegate performSelectorOnMainThread:@selector(theProcess:) withObject:nil waitUntilDone:YES];
就OK了!
小結(jié):iPhone SDK中多線程 使用方法以及注意事項(xiàng)的內(nèi)容介紹完了,希望本文對你有所幫助。
轉(zhuǎn)自 http://www.yifeiyang.net/iphone-developer-advanced-11-multiple-threads-of-use-and-precautions/