2013-07-31 127 views
3

如何在不同線程的後臺執行某些操作,如果它在主線程上執行,它將阻止我的應用程序的UI。任何人有任何想法如何做到這一點?在iOS中的不同線程上執行後臺任務

即使它在後臺打印NSLog它會沒事的。 即使用戶按HOME按鈕,我也想運行下面的東西。 在我的viewController我這樣做:

- (IBAction)btnStartClicked:(UIButton *)sender { 
    [NSThread detachNewThreadSelector:@selector(StartBGTask) toTarget:self withObject:nil]; 
    } 

-(void)StartBGTask{ 
    [[[UIApplication sharedApplication] delegate] performSelector:@selector(startThread)]; 
    } 

和appDelegate.m我有這個方法

-(void) startThread { 
@autoreleasepool { 
    for (int i = 0; i < 100; i++) { 
     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
      NSLog(@"current progress %d", i); 
     }); 
     [NSThread sleepForTimeInterval:1]; 
    } 
    } 
} 

它輸出從1到100的整數上1秒的時間間隔。

+0

Your for(){}在主隊列中執行。你使用[NSThread sleepForTimeInterval:1],是不是會阻止你的用戶界面?你阻止你主隊列。 –

+0

編輯幷包含更多細節,我試圖執行第二種方法在不同的線程上執行,但當主頁按鈕被按下時,它停止後臺任務。 –

+0

爲什麼不使用計時器。你甚至不需要把它放在後臺線程上。 – Abizern

回答

8

這些屬性添加到您的.h文件中

@property (nonatomic, strong) NSTimer *updateTimer; 
@property (nonatomic) UIBackgroundTaskIdentifier backgroundTask; 

現在更換btnStartClicked方法與此,

-(IBAction)btnStartClicked:(UIButton *)sender { 
    self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 
                                                        target:self 
                                                      selector:@selector(calculateNextNumber) 
                                                      userInfo:nil 
                                                       repeats:YES]; 
    self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ 
        NSLog(@"Background handler called. Not running background tasks anymore."); 
        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask]; 
        self.backgroundTask = UIBackgroundTaskInvalid; 
    }]; 
     
} 

-(void)calculateNextNumber{ 
    @autoreleasepool { 
     // this will be executed no matter app is in foreground or background 
    } 
} 

,如果你需要停止使用這種方法,

- (IBAction)btnStopClicked:(UIButton *)sender { 

    [self.updateTimer invalidate]; 
    self.updateTimer = nil; 
    if (self.backgroundTask != UIBackgroundTaskInvalid) 
    { 
     [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask]; 
     self.backgroundTask = UIBackgroundTaskInvalid; 
    } 
    i = 0; 
} 
0

一個非常簡單的方式來完成你想要什麼:

-(IBAction)btnStartClicked:(UIButton *)sender { 
    [self performSelectorInBackground:@selector(codeInBakground) withObject:nil]; 
} 

-(void)codeInBakground 
{ 
    for (int i = 0; i < 100; i++) { 
     NSLog(@"current progress %d", i); 
     [NSThread sleepForTimeInterval:1]; //the code will print one number in each second, until 100 
    } 
} 

這樣,你的主線程,和您的UI不會被阻止。

+0

performSelector與ARC不能很好地協作,編譯器不知道返回類型是什麼。 – Abizern

+0

你可以更具體地瞭解一個可能的問題,或者給出一些來源?我曾經使用過類似的代碼,沒有發現問題/錯誤/泄漏。 –

+0

例如http://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown我看到這一點,因爲我使用performSelector的唯一時間是當我使用一個字符串表示。對於其他所有我使用GCD的人。作爲第二點,在你的情況下,你將一個無對象傳遞給一個不帶參數的選擇器。在這種情況下,你可以使用'performSelector:'而不是'performSelector:withObject:'。實際上,你甚至不需要執行這裏的performSelector,你可以只使用dispatch_async()並將你在方法中的內容作爲一個塊運行。 – Abizern

6

查看GCD瞭解更多信息。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
     //code in background 
    });