2011-12-16 6 views
1

У меня есть приложение IPad, и я хочу, чтобы это сделатьДождитесь результата без остановки UI

-(IBAction) clicked { 
    image=download(@"http://....."); // this is on the main thread 
} 

Функция загрузки будет вызывать целую кучу, не являющихся функциями блокировки, чтобы загрузить файл из Интернета, но сама загрузка не должна возвращаться до загрузки изображения.

Хотя программа ожидает загрузки на рисунке = download (...) выше, я хочу, чтобы пользовательский интерфейс мог функционировать, например, иметь возможность прокручивать UITableView, нажимать другую кнопку и т. Д.

так что я сделал это в функции загрузки я использовал RunLoop

-(void) download:(NSString *)url 
{ 
    BOOL stillDownloading=TRUE; 
    while(stillDownloading) { 
     stillDownloading=downloadAFwBytes(...); 
     CFRunLoopRunInMode(kCFRunLoopCommonModes, 0, YES); 
    } 
} 

Я думал, что функция CFRunLoopRunInMode будет держать насосные сообщения UI, прикасается, прокручивают основной поток пользовательского интерфейса, так что пользовательский интерфейс будет держать работая и не замерзая, пока загрузка не закончится, но по какой-то причине она работает только на короткое время, и в конечном итоге замораживание пользовательского интерфейса s.

Знаете ли вы, почему, или как исправить?

Функция загрузки называется повсюду в программе, которая ожидает, что она ждет загрузки, поэтому я не могу ее изменить до момента блокировки.

ответ

5

Прямой ответ на ваш вопрос: нет, это не то, что CFRunLoopRunInMode. То, что вы фактически пытаетесь сделать, - это текущая строка цикла «yield», поэтому выполнение может продолжаться, пока операция загрузки продолжается. Это не так, как работают iOS и работают циклы. Функция загрузки блокирует поток, на котором он находится, до тех пор, пока загрузка не будет завершена, поэтому единственным решением вашей проблемы является изменение реализации, так что загрузка происходит в фоновом потоке, а объекты, которые ухаживают, уведомляются по завершении. Вот относительно небольшое изменение, которое может привести вас к правильному пути. Эта общая тема (параллелизм, управление фоновыми задачами) - это более широкое обсуждение, и есть разные соображения/компромиссы. Я перейду к погоне и надеюсь, что вы на правильном пути.

  1. Определить пару NSNotification о том, что ваш метод загрузки может отправлять сообщения для заинтересованных объектов для наблюдения:

    // in the .h file of the class performing the download 
    extern NSString * const MyClassLoadingDidStartNotification; 
    extern NSString * const MyClassLoadingDidFinishNotification; 
    
    // in the .m file of the class performing the download 
    NSString * const MyClassLoadingDidStartNotification = @"MyClassLoadingDidStart"; 
    NSString * const MyClassLoadingDidFinishNotification = @"MyClassLoadingDidFinish"; 
    
  2. В вашей загрузке рутине, сделать загрузку в фоновом режиме и отправлять соответствующие уведомления:

    -(void) download:(NSString *)url 
    { 
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
         [[NSNotificationCenter defaultCenter] postNotificationName:MyClassLoadingDidStartNotification object:self]; 
         BOOL stillDownloading=TRUE; 
         while(stillDownloading) { 
          stillDownloading=downloadAFwBytes(...); 
         } 
         [[NSNotificationCenter defaultCenter] postNotificationName:MyClassLoadingDidFinishNotification object:self]; 
        }); 
    } 
    
  3. В любом объекте, который инициирует загрузку, наблюдения и обработки уведомлений

    // in any class that initiates a download 
    - (void)init... 
    { 
        self = [super init...]; 
        if (self) { 
         // other initialization 
         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didStartLoading:) name:MyClassLoadingDidStartNotification object:nil]; 
         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFinishLoading:) name:MyClassLoadingDidFinishNotification object:nil]; 
        } 
        return self; 
    } 
    
    - (void)dealloc 
    { 
        [[NSNotificationCenter defaultCenter] removeObserver:self name:MyClassLoadingDidStartNotification object:nil]; 
        [[NSNotificationCenter defaultCenter] removeObserver:self name:MyClassLoadingDidFinishNotification object:nil]; 
    } 
    
    - (void)didStartLoading:(NSNotification *)notification 
    { 
        // update UI to show loading status (make sure you do UI changes on main thread) 
        // optionally check notification.object to ensure it's the loader class instance you care about 
    } 
    
    - (void)didFinishLoading:(NSNotification *)notification 
    { 
        // update UI to show loading status (make sure you do UI changes on main thread) 
        // optionally check notification.object to ensure it's the loader class instance you care about 
    } 
    

Имейте в виду, что это очень основной отправной точкой. Когда вы узнаете больше и решите, что вам нужно, вы обязательно настроите его. Например, вы можете захотеть добавить обработку ошибок, ограничить параллельные операции загрузки, предоставить другой статус загрузки и т. Д.

0

Есть некоторые проблемы с архитектурой в вашем вопросе, но чтобы правильно загрузить изображение в фоновом режиме и загрузить его в UIImageView или использовать его любым другим способом, я бы предложил взглянуть на AFNetworking и прочитать их образец код для загрузки ресурсов.

+0

Я знаю, что это не отличный способ, но есть ли способ заставить его работать так, я поменяю его позже, но сейчас нет времени! –

+0

@NS Конечно, есть время, вы должны изменить его сейчас, его не так уж сложно ... – Daniel

+0

выше это упрощение реальной вещи, которая намного сложнее. Чтобы изменить это, потребуется массивная типизация, я думал, что могу добавить что-то в цикл выше, чтобы обрабатывать сообщения пользовательского интерфейса, пока все еще находится в цикле while? Это возможно? –

0

Можете ли вы запросить загрузку изображения на задний план, а затем вернуть статический образ в свой метод?

Таким образом, основной поток возвращается быстро и в зависимости от вашей архитектуры, ваш фоновый поток может обновлять изображение после его получения?

Я думаю, нам нужна подробная информация о вашем коде (ваш метод загрузки недействителен, так как используются загруженные данные?), Чтобы иметь возможность реально помочь.

+0

проблема в том, что это всего лишь одна функция, этот метод используется по всей программе для запроса разных вещей, а не только изображений. Вызывающий абонент ожидает, что элемент будет возвращен, когда функция вернется, но это остановит пользовательский интерфейс в этом процессе. поэтому я поставил RunLoop, чтобы попытаться решить, но он работает только некоторое время, пока он не зависает. Есть ли что-нибудь, что я могу положить в цикл while, чтобы исправить проблему? –

+0

Не знаю. Но я прочитал это в документе: «Вы не должны указывать константу kCFRunLoopCommonModes для параметра режима. Циклы запуска всегда запускаются в определенном режиме. Вы указываете общие режимы только при настройке наблюдателя с циклом и только в ситуациях, где вы хотите чтобы наблюдатель работал в более чем одном режиме ». –

+0

Спасибо, знаете ли вы, какой режим я могу использовать, чтобы runloop обрабатывал сообщения пользовательского интерфейса, чтобы пользовательский интерфейс не зависал? разве это не главная цель RunLoop? –

0

Наряду с тремя другими парнями, сообщающими вам то же самое, я скажу вам, чтобы вы забыли о своем собственном запуске и просто используйте возможности загрузки async (при условии, что вы загружаете через сеть). Вам даже не нужно создавать поток, просто запустите асинхронные загрузки, и они расскажут вам, когда они будут сделаны. Если у вас нет времени, чтобы правильно его закодировать, когда у вас будет время исправить свой код?

0

Не уверен, что это все еще разрешено, но вы не можете использовать?

[self.operationQueue addOperationWithBlock:^{ 
    [self MethodName]; 
}]; 

и когда вы хотите что-то произойдет в UI, как обновления таблиц затем поместить это в код метода:

[[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
////perform something here //// 
}]; 

надеюсь, что это помогает.