从NSUrlSession调用CoreData主线程UI警告

时间:2017-10-30 06:53:21

标签: objective-c core-data appdelegate

我正在使用NSUrlSession下载JSON数据并且它正在工作,但我收到一个UI警告,AppDelegate只能被称为主线程,我不知道如何在这种情况下摆脱它。我尝试将AppDelegate行本身包装到main_queue语句中,但这没有任何区别,我假设我在这里做了一些根本上基本的错误,但我无法弄清楚到底是什么。

代码如下所示。

+(void)fetchPricelistAll:(int)pricelistId :(int)startAtRow :(int)takeNoOfRows;
{
    if ([NWTillHelper isDebug] == 1) {
        NSLog(@"WebServices:fetchPriceList:priceListId = %d", pricelistId);
    }

    NSString *finalURL = [NSString stringWithFormat:@"https://xxx.yyy.com/zzz/zzz/zzz/%d?StartAtRow=%d&TakeNoOfRows=%d",pricelistId, startAtRow, takeNoOfRows];

    [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:finalURL]
                                 completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

                                     if (error != nil) {
                                         if ([NWTillHelper isDebug] == 1) {
                                             NSLog(@"WebServices:fetchPriceList:Transport error %@", error);
                                         }
                                     } else {
                                         NSHTTPURLResponse *responseHTTP;
                                         responseHTTP = (NSHTTPURLResponse *) response;

                                         if(responseHTTP.statusCode != 200) {
                                             if ([NWTillHelper isDebug] == 1) {
                                                 NSLog(@"WebServices:fetchPriceList:Server Error %d", (int) responseHTTP.statusCode);
                                             }
                                         } else {
                                             NSArray *priceListObjectArray = [NSJSONSerialization JSONObjectWithData:data
                                                                                                             options:0
                                                                                                               error:NULL];
                                             if ([NWTillHelper isDebug] == 1) {
                                                 NSLog(@"WebServices:fetchPriceList:count = %lu", (unsigned long)[priceListObjectArray count]);
                                             }

                                             AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];

                                             NSOperationQueue *prlQueue = [[NSOperationQueue alloc] init];
                                             prlQueue.maxConcurrentOperationCount = 1;

                                             NSPersistentContainer *container = appDelegate.persistentContainer;

                                             NSArray *arrayOfArrays = [NWTillHelper splitIntoArraysOfBatchSize:priceListObjectArray :1000];

                                             NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
                                             [dateFormat setDateFormat:@"YYYY-MM-dd'T'HH:mm:ss"];

                                             for(NSArray *batch in arrayOfArrays) {

                                                 [prlQueue addOperationWithBlock:^{

                                                     [container performBackgroundTask:^(NSManagedObjectContext *context ) {
                                                         context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;

                                                         NSDictionary *priceListObjectDict = nil;

                                                         //Loop through the array and for each dictionary insert into local DB
                                                         for (id element in batch) {
                                                             priceListObjectDict = element;

                                                             NSString *currencyName = [priceListObjectDict objectForKey:@"currencyName"];
                                                             NSString *price = [priceListObjectDict objectForKey:@"price"];
                                                             NSString *priceIncTax = [priceListObjectDict objectForKey:@"priceIncTAX"];
                                                             NSString *validFrom = [priceListObjectDict objectForKey:@"validFromDate"];
                                                             NSString *validTo = [priceListObjectDict objectForKey:@"validToDate"];
                                                             NSString *itemId = [priceListObjectDict objectForKey:@"itemID"];

                                                             NSDate *validToDate = [dateFormat dateFromString:validTo];
                                                             NSDate *validFromDate = [dateFormat dateFromString:validFrom];

                                                             NSManagedObject *newPrlItem = Nil;
                                                             newPrlItem = [NSEntityDescription
                                                                           insertNewObjectForEntityForName:@"PriceList"
                                                                           inManagedObjectContext:context];

                                                             [newPrlItem setValue:itemId forKey:@"itemId"];
                                                             [newPrlItem setValue:validToDate forKey:@"validTo"];
                                                             [newPrlItem setValue:validFromDate forKey:@"validFrom"];
                                                             [newPrlItem setValue:price forKey:@"price"];
                                                             [newPrlItem setValue:priceIncTax forKey:@"priceIncTax"];
                                                             [newPrlItem setValue:currencyName forKey:@"currencyName"];

                                                             if ([NWTillHelper isDebug] == 1) {
                                                                 NSLog(@"WebServices:fetchTillData:ItemId in loop = %@", itemId);
                                                                 NSLog(@"WebServices:fetchTillData:newPrlItem = %@", newPrlItem);
                                                                 NSLog(@"WebServices:fetchTillData:CoreData error = %@", error);
                                                             }
                                                         }
                                                         NSError *error = nil;
                                                         if (![context save:&error]) {
                                                             NSLog(@"Failure to save context: %@\n%@", [error localizedDescription], [error userInfo]);
                                                             abort();
                                                         } else {
                                                             NSUserDefaults *tillUserDefaults = [NSUserDefaults standardUserDefaults];
                                                             [tillUserDefaults setInteger:1 forKey:@"hasPriceList"];
                                                             [tillUserDefaults synchronize];
                                                         }
                                                         [context reset];
                                                     }];
                                                 }];
                                             }
                                         }
                                     }
                                 }] resume];
}

错误消息如下所示

=================================================================
Main Thread Checker: UI API called on a background thread: -[UIApplication delegate]
PID: 43836, TID: 4857183, Thread name: (none), Queue name: NSOperationQueue 0x60c00023aac0 (QOS: UNSPECIFIED), QoS: 0
Backtrace:
4   NWMPos                              0x00000001076ce5c2 __35+[WebServices fetchPricelistAll:::]_block_invoke + 610
5   CFNetwork                           0x000000010f295208 __75-[__NSURLSessionLocal taskForClass:request:uploadFile:bodyData:completion:]_block_invoke + 19
6   CFNetwork                           0x000000010f294a6d __49-[__NSCFLocalSessionTask _task_onqueue_didFinish]_block_invoke + 147
7   Foundation                          0x0000000109a189b7 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 7
8   Foundation                          0x0000000109a1881a -[NSBlockOperation main] + 68
9   Foundation                          0x0000000109a16cd6 -[__NSOperationInternal _start:] + 778
10  libdispatch.dylib                   0x000000010ec3d43c _dispatch_client_callout + 8
11  libdispatch.dylib                   0x000000010ec42af4 _dispatch_block_invoke_direct + 592
12  libdispatch.dylib                   0x000000010ec3d43c _dispatch_client_callout + 8
13  libdispatch.dylib                   0x000000010ec42af4 _dispatch_block_invoke_direct + 592
14  libdispatch.dylib                   0x000000010ec42884 dispatch_block_perform + 109
15  Foundation                          0x0000000109a12ce4 __NSOQSchedule_f + 342
16  libdispatch.dylib                   0x000000010ec3d43c _dispatch_client_callout + 8
17  libdispatch.dylib                   0x000000010ec43856 _dispatch_continuation_pop + 967
18  libdispatch.dylib                   0x000000010ec41c86 _dispatch_async_redirect_invoke + 780
19  libdispatch.dylib                   0x000000010ec491f9 _dispatch_root_queue_drain + 772
20  libdispatch.dylib                   0x000000010ec48e97 _dispatch_worker_thread3 + 132
21  libsystem_pthread.dylib             0x000000010f1005a2 _pthread_wqthread + 1299
22  libsystem_pthread.dylib             0x000000010f10007d start_wqthread + 13
2017-10-30 14:40:51.252817+0800 NWMPos[43836:4857183] [reports] Main Thread Checker: UI API called on a background thread: -[UIApplication delegate]
PID: 43836, TID: 4857183, Thread name: (none), Queue name: NSOperationQueue 0x60c00023aac0 (QOS: UNSPECIFIED), QoS: 0
Backtrace:
4   NWMPos                              0x00000001076ce5c2 __35+[WebServices fetchPricelistAll:::]_block_invoke + 610
5   CFNetwork                           0x000000010f295208 __75-[__NSURLSessionLocal taskForClass:request:uploadFile:bodyData:completion:]_block_invoke + 19
6   CFNetwork                           0x000000010f294a6d __49-[__NSCFLocalSessionTask _task_onqueue_didFinish]_block_invoke + 147
7   Foundation                          0x0000000109a189b7 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 7
8   Foundation                          0x0000000109a1881a -[NSBlockOperation main] + 68
9   Foundation                          0x0000000109a16cd6 -[__NSOperationInternal _start:] + 778
10  libdispatch.dylib                   0x000000010ec3d43c _dispatch_client_callout + 8
11  libdispatch.dylib                   0x000000010ec42af4 _dispatch_block_invoke_direct + 592
12  libdispatch.dylib                   0x000000010ec3d43c _dispatch_client_callout + 8
13  libdispatch.dylib                   0x000000010ec42af4 _dispatch_block_invoke_direct + 592
14  libdispatch.dylib                   0x000000010ec42884 dispatch_block_perform + 109
15  Foundation                          0x0000000109a12ce4 __NSOQSchedule_f + 342
16  libdispatch.dylib                   0x000000010ec3d43c _dispatch_client_callout + 8
17  libdispatch.dylib                   0x000000010ec43856 _dispatch_continuation_pop + 967
18  libdispatch.dylib                   0x000000010ec41c86 _dispatch_async_redirect_invoke + 780
19  libdispatch.dylib                   0x000000010ec491f9 _dispatch_root_queue_drain + 772
20  libdispatch.dylib                   0x000000010ec48e97 _dispatch_worker_thread3 + 132
21  libsystem_pthread.dylib             0x000000010f1005a2 _pthread_wqthread + 1299
22  libsystem_pthread.dylib             0x000000010f10007d start_wqthread + 13

2 个答案:

答案 0 :(得分:0)

尝试将AppDelegate行中的所有代码包装到main_queue

+(void)fetchPricelistAll:(int)pricelistId :(int)startAtRow :(int)takeNoOfRows;
{
  if ([NWTillHelper isDebug] == 1) {
    NSLog(@"WebServices:fetchPriceList:priceListId = %d", pricelistId);
  }

  NSString *finalURL = [NSString stringWithFormat:@"https://xxx.yyy.com/zzz/zzz/zzz/%d?StartAtRow=%d&TakeNoOfRows=%d",pricelistId, startAtRow, takeNoOfRows];

  [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:finalURL]
                             completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

                               if (error != nil) {
                                 if ([NWTillHelper isDebug] == 1) {
                                   NSLog(@"WebServices:fetchPriceList:Transport error %@", error);
                                 }
                               } else {
                                 NSHTTPURLResponse *responseHTTP;
                                 responseHTTP = (NSHTTPURLResponse *) response;

                                 if(responseHTTP.statusCode != 200) {
                                   if ([NWTillHelper isDebug] == 1) {
                                     NSLog(@"WebServices:fetchPriceList:Server Error %d", (int) responseHTTP.statusCode);
                                   }
                                 } else {
                                   NSArray *priceListObjectArray = [NSJSONSerialization JSONObjectWithData:data
                                                                                                   options:0
                                                                                                     error:NULL];
                                   if ([NWTillHelper isDebug] == 1) {
                                     NSLog(@"WebServices:fetchPriceList:count = %lu", (unsigned long)[priceListObjectArray count]);
                                   }

                                   dispatch_async(dispatch_get_main_queue(), ^{
                                     AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];

                                     NSOperationQueue *prlQueue = [[NSOperationQueue alloc] init];
                                     prlQueue.maxConcurrentOperationCount = 1;

                                     NSPersistentContainer *container = appDelegate.persistentContainer;

                                     NSArray *arrayOfArrays = [NWTillHelper splitIntoArraysOfBatchSize:priceListObjectArray :1000];

                                     NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
                                     [dateFormat setDateFormat:@"YYYY-MM-dd'T'HH:mm:ss"];

                                     for(NSArray *batch in arrayOfArrays) {

                                       [prlQueue addOperationWithBlock:^{

                                         [container performBackgroundTask:^(NSManagedObjectContext *context ) {
                                           context.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;

                                           NSDictionary *priceListObjectDict = nil;

                                           //Loop through the array and for each dictionary insert into local DB
                                           for (id element in batch) {
                                             priceListObjectDict = element;

                                             NSString *currencyName = [priceListObjectDict objectForKey:@"currencyName"];
                                             NSString *price = [priceListObjectDict objectForKey:@"price"];
                                             NSString *priceIncTax = [priceListObjectDict objectForKey:@"priceIncTAX"];
                                             NSString *validFrom = [priceListObjectDict objectForKey:@"validFromDate"];
                                             NSString *validTo = [priceListObjectDict objectForKey:@"validToDate"];
                                             NSString *itemId = [priceListObjectDict objectForKey:@"itemID"];

                                             NSDate *validToDate = [dateFormat dateFromString:validTo];
                                             NSDate *validFromDate = [dateFormat dateFromString:validFrom];

                                             NSManagedObject *newPrlItem = Nil;
                                             newPrlItem = [NSEntityDescription
                                                           insertNewObjectForEntityForName:@"PriceList"
                                                           inManagedObjectContext:context];

                                             [newPrlItem setValue:itemId forKey:@"itemId"];
                                             [newPrlItem setValue:validToDate forKey:@"validTo"];
                                             [newPrlItem setValue:validFromDate forKey:@"validFrom"];
                                             [newPrlItem setValue:price forKey:@"price"];
                                             [newPrlItem setValue:priceIncTax forKey:@"priceIncTax"];
                                             [newPrlItem setValue:currencyName forKey:@"currencyName"];

                                             if ([NWTillHelper isDebug] == 1) {
                                               NSLog(@"WebServices:fetchTillData:ItemId in loop = %@", itemId);
                                               NSLog(@"WebServices:fetchTillData:newPrlItem = %@", newPrlItem);
                                               NSLog(@"WebServices:fetchTillData:CoreData error = %@", error);
                                             }
                                           }
                                           NSError *error = nil;
                                           if (![context save:&error]) {
                                             NSLog(@"Failure to save context: %@\n%@", [error localizedDescription], [error userInfo]);
                                             abort();
                                           } else {
                                             NSUserDefaults *tillUserDefaults = [NSUserDefaults standardUserDefaults];
                                             [tillUserDefaults setInteger:1 forKey:@"hasPriceList"];
                                             [tillUserDefaults synchronize];
                                           }
                                           [context reset];
                                         }];
                                       }];
                                     }
                                   });
                                 }
                               }
                             }] resume];
}

答案 1 :(得分:0)

正如您所说,对UIApplication.delegate的调用被视为用户界面的一部分,因此无法从后台线程访问。

由于您只需要对持久性容器的引用,我建议:

  • 在调用[[UIApplication delegate] persistentContainer]之前创建一个指向dataTaskWithURL:的局部变量,然后在块内使用该局部变量
  • 或者,您可以重构代码以将持久性容器保存在不与UI关联的其他全局对象上。