Как реорганизовать код, чтобы он не находился в основном потоке

Я пытаюсь реорганизовать некоторый код NSJSONSerialization, чтобы он не находился в основном потоке. На данный момент приложение немного тормозит.

Я хотел бы преобразовать этот код в то, что у меня есть ниже, и у меня проблемы с синтаксисом, особенно с обработкой ошибок. Например, если я возьму свой существующий код (метод requestData:) и помещу его в

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
});

таблица больше не загружает никаких данных.

спасибо за любую помощь.

-(void)requestData {

    [HUD showUIBlockingIndicatorWithText:@"Fetching JSON"];

    NSError *requestError = nil;

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL
                                                          URLWithString:kURL]];

    NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&requestError];

    NSError *jsonParsingError = nil;

    if (requestError)
    {
        NSLog(@"sync. request failed with error: %@", requestError);
    }
    else
    {
        // handle data
       publicData =  [NSJSONSerialization JSONObjectWithData:response
                                                                    options:0
                                                                      error:&jsonParsingError];
        publicDataArray = [publicData objectForKey:@"data"];

    }

    /*
     for(publicDataDict in publicDataArray) {
     NSLog(@"data output is %@",[publicDataDict objectForKey:@"title"]);

     }
     */
    [self.mainTableView reloadData];

    [HUD hideUIBlockingIndicator];
}

Вот код, который я хотел бы использовать.

-(void)viewDidAppear:(BOOL)animated
{

    [HUD showUIBlockingIndicatorWithText:@"Fetching Data"];

    //1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //code executed in the background
        //2
        NSData* ghData = [NSData dataWithContentsOfURL:
                            [NSURL URLWithString:kURL]
                            ];
        //3
        NSDictionary* json = nil;
        if (ghData) {
            json = [NSJSONSerialization
                    JSONObjectWithData:ghData
                    options:kNilOptions
                    error:nil];
        }

        //4
        dispatch_async(dispatch_get_main_queue(), ^{
            //code executed on the main queue
            //5


            [self.tableView reloadData];
            [HUD hideUIBlockingIndicator];
        });

    });
}

person hanumanDev    schedule 03.07.2013    source источник
comment
Пожалуйста, укажите конкретные проблемы, включите журналы компиляции/исключений и трассировки стека. Не заставляйте нас гадать, с чем у вас проблемы.   -  person Wain    schedule 03.07.2013
comment
@Wain Я внес правку. В основном, когда я помещаю код в dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{}); данные таблицы больше не загружаются   -  person hanumanDev    schedule 03.07.2013
comment
Действительно ли json анализируется правильно, или вы получаете сообщение об ошибке. Отладьте, чтобы проверить.   -  person Wain    schedule 03.07.2013
comment
В первом сегменте кода вы используете self.mainTableView, а во втором — self.tableView.   -  person colincameron    schedule 03.07.2013
comment
@Wain да, данные выводятся на консоль. таблица, загружающая данные, является проблемой.   -  person hanumanDev    schedule 03.07.2013
comment
@ c.cam108 да, я использую первый код с self.mainTableView - он правильный. второй метод я не использую и хочу, чтобы мой код выглядел так с оптимизированной диспетчеризацией   -  person hanumanDev    schedule 03.07.2013
comment
Значит, второй код на самом деле не твой? Пожалуйста, опубликуйте код, который вы используете, который, по вашему мнению, не работает.   -  person colincameron    schedule 03.07.2013


Ответы (4)


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

-(void)viewDidAppear:(BOOL)animated
{

    [HUD showUIBlockingIndicatorWithText:@"Fetching Data"];

    //1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //code executed in the background
        //2
        NSData* ghData = [NSData dataWithContentsOfURL:
                          [NSURL URLWithString:kURL]
                          ];
        //3
        NSDictionary* json = nil;
        if (ghData) {
            json = [NSJSONSerialization
                    JSONObjectWithData:ghData
                    options:kNilOptions
                    error:nil];
        }

        //4
        [self performSelectorOnMainThread:@selector(reloadTable) withObject:nil waitUntilDone:NO];

    });
}

и после этого делай вот так.

-(void)reloadTable {

    [self.tableView reloadData];
    [HUD hideUIBlockingIndicator];
}

Также проверьте, вызываются ли методы делегата источника данных tableview, если он не вызывается, а затем установите делегат UITableView.

person Gyanendra    schedule 03.07.2013

Многие люди предполагают, что отправка процесса в фоновый режим автоматически избавит их приложение от какой-либо медлительности, но это НЕПРАВИЛЬНОЕ предположение. Если вы отправите задачу с интенсивным использованием ЦП в фоновый режим, она также заблокирует ЦП. Чтобы многопоточность работала в вашу пользу, вы должны действовать методично.

Теперь о вашей проблеме, самое простое решение для вас - использовать то, что Apple уже предоставляет, NSURLConnection - ваш лучший выбор, НИКОГДА не используйте [NSData dataWithContentsOfURL:], это определенно нет. Проблема не в NSJSONSerialization, а в сетевом запросе.

У вас есть два варианта на самом деле.

1) Используйте метод делегата NSRULConnection и поместите свои методы сериализации JSON в — connectionDidFinishLoading: метод делегата

2) Используйте блочные методы для NSURLConnection [NSURLConnection sendAsynchronousRequest: queue: completeHandler:] (мой предпочтительный выбор)

-(void)viewDidAppear:(BOOL)animated
{
    [HUD showUIBlockingIndicatorWithText:@"Fetching Data"];

    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

    if (!error) {
        NSError *jsonError = nil;
        NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError];

        if (jsonError) {
            NSLog(@"Error parsing JSON");
            //Optionally display error message here
        }else{

            self.globalDictionary = jsonDict;

            [self.tableView reloadData];
            [HUD hideUIBlockingIndicator];
        }

    }else
    {
        NSLog(@"Error with request");

        [HUD hideUIBlockingIndicator];
        //Optionally display error message here
    }


}];

}

Примечания: globalDictionary — это экземпляр NSDictionary, который заполняет таблицу.

person Edwin    schedule 03.07.2013

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

Говоря о вашей проблеме, похоже, вы все сделали правильно, но я не вижу, чтобы вы определяли источник tableView, проверьте, преобразовали ли вы JSON в какой-либо объект-контейнер, а именно в словарь или массив.

    -(void)viewDidAppear:(BOOL)animated
{
    [HUD showUIBlockingIndicatorWithText:@"Fetching Data"];

    [self fetchData];
}

-(void)fetchData
{
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  // switch to a background thread and perform your expensive operation

  NSData* ghData = [NSData dataWithContentsOfURL:
                            [NSURL URLWithString:kURL]
                            ];

  NSDictionary* json = nil;

  if (ghData) 
  {
    json = [NSJSONSerialization
                    JSONObjectWithData:ghData
                    options:kNilOptions
                    error:nil];
   }

   dispatch_async(dispatch_get_main_queue(), ^{
   // switch back to the main thread to update your UI
  [self.tableView reloadData];
  [HUD hideUIBlockingIndicator];
    });

 });
}
person Say2Manuj    schedule 03.07.2013

Попробуйте сначала провести рефакторинг, чтобы преобразовать проект для новой архитектуры ARC, я публикую это в старом ответе, посмотрите здесь:

Моя запись

Надеюсь, это поможет вам или даст идею для рефакторинга вашего кода;)

person BlackSheep    schedule 03.07.2013