2013-07-31 5 views
3

Как преформировать определенную операцию в фоновом режиме на другой поток, если она выполняется в основном потоке, она блокирует пользовательский интерфейс моего приложения. кто-нибудь знает, как это сделать?Выполнение фоновой задачи в разных потоках в 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

Ваш вызов 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 
    } 
} 

Таким образом, ваш основной поток, и ваш пользовательский интерфейс не будет заблокирован.

+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 
    }); 
Смежные вопросы