2013-08-21 18 views
1

我正在使用MagicalRecord,並且在我的一個視圖控制器中,數據源使用來自數據庫的數據提供。由於餵養過程包括多個步驟,我嘗試使用GCD來加速整個過程。 一般過程看起來像這樣。MagicalRecord和dispatch_group

在globalSummary中獲取訪問屬性後要完成的工作。 問題是應用程序掛在dispatch_group_wait。我嘗試將工作添加到該步驟,並在開始時工作,但是如果我一步一步地完成代碼,但添加更多工作,但如果讓它運行,則無法工作。

該方法有問題嗎?或者它與MagicalRecord在某些方面發生衝突?

- (NSArray *)dataSource 
{ 
    if (_dataSource == nil) { 

    _dataSource = [NSMutableArray array]; 
    NSManagedObjectContext *privateContext = [NSManagedObjectContext contextWithStoreCoordinator:[NSPersistentStoreCoordinator defaultStoreCoordinator]]; 

    GlobalSummary *globalSummary = [GlobalSummary insertInManagedObjectContext:privateContext]; // holds a bunch of fetched properties 

    dispatch_queue_t queue = dispatch_queue_create("de.berndrabe.dataSource", DISPATCH_QUEUE_CONCURRENT); 
    dispatch_group_t outerGroup = dispatch_group_create(); 

    __block SectionInfo *siPilotAircraft = nil; 
    if ([PilotAircraft countOfEntities]) { 
     dispatch_group_async(outerGroup, queue, ^{ 
      NSArray *frPilotAircraft = [PilotAircraft findAll]; 
      // do some processing an set SectionInfo variable 
     }); 
    } 

    __block SectionInfo *siMedicals = nil; 

    if ([PilotMedical countOfEntities]) { 
     dispatch_group_async(outerGroup, queue, ^{ 
      NSArray *frPilotMedical = [PilotMedical findAll]; 
     }); 
    } 

    // more working packets following the same patter 

    dispatch_group_wait(outerGroup, DISPATCH_TIME_FOREVER); 

    if (siPilotAircraft.countOfRows) { 
     [_dataSource addObject:siPilotAircraft]; 
    } 
    if (siMedicals.countOfRows) { 
     [_dataSource addObject:siMedicals]; 
    } 
} 

return _dataSource; 
} 

編輯: 使用[Entity findAllInContext:privateContext]讓我獲取所需要的記錄,但它仍然在現在一個實體:(

回答

0

訪問故障時的關係我找到了解決我的問題,我被困。 這裏再次(在短)什麼我試圖完成。

任務 構建一個NSMutableArray這將充當用於UITableView 每個條目DataSource對象包括核心數據獲取和處理操作 parallelise工作和完成添加效果,確保訪問NSMutableArray調節 開始與數據源對象完成工作

問題 項的數據源中的對象的數量可以改變

這裏對後處理我。 'NSMutableArray'上的類別是確保訪問序列化的一種方便方法。 有了這個實現,你已經儘可能平行工作,你可以保存 寫

[self prepareDataSource]; [self.tableView reloadData];

無需擔心某些工作仍在進行中。

dispatch_queue_t queue = dispatch_queue_create("com.yourdomain.serializedAccess", DISPATCH_QUEUE_CONCURRENT); 

- (void)prepareDataSource 
{ 
    [self.dataSource removeAllObjects]; 

    dispatch_group_t group = dispatch_group_create(); 

    [self prepareWorkEntry1FromManagedObjectContext:self.privateContext forDataSource:self.dataSource group:group]; 
    [self prepareWorkEntry2FromManagedObjectContext:self.privateContext forDataSource:self.dataSource group:group]; 
    [self prepareWorkEntry3FromManagedObjectContext:self.privateContext forDataSource:self.dataSource group:group]; 

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 
} 

- (void)prepareWorkEntry1FromManagedObjectContext:(NSManagedObjectContext *)context forDataSource:(NSMutableArray *)array group:(dispatch_group_t)group 
{ 
    __weak typeof(self) weakSelf = self; 

    dispatch_group_enter(group); 
    [context performBlock:^{ 
    Object *object = nil; 
     createObject(object); 

    if (object) { 
      [array serializedAddObject:object withQueue:weakSelf.queue group:group]; 
     } 
     else { 
      dispatch_group_leave(group); 
     } 
    }]; 
} 


@implementation NSMutableArray (ResourceProtection) 

- (void)serializedAddObject:(id)object withQueue:(dispatch_queue_t)queue group:(dispatch_group_t)group 
{ 
    if (group == NULL) { 
     dispatch_barrier_async(queue, ^{ 
      [self addObject:object]; 
     }); 
    } else { 
     dispatch_barrier_async(queue, ^{ 
      [self addObject:object]; 
      dispatch_group_leave(group); 
     }); 
    } 
} 

@end 

編輯: 至於ResourceProtection類別而言,你必須確保

  1. dispatch_group_enter之前dispatch_group_leave
  2. dispatch_group_enter和dispatch_group_leave平衡

這叫做是一個可觀察的同步寫入器方案,用於可變對象。

+0

你確實可以確認'performBlock:'將相對於其他塊並行執行給定塊嗎?我擔心,他們運行序列化。此外,還有一點小小的改進就是使'prepareDataSource:'異步,有一個完成塊,當所有的異步任務(performBlock :)完成時它會被調用。 dispatch_group_wait不必要地導致調用線程阻塞,直到所有任務完成。 – CouchDeveloper

+0

正如Apple在其文檔中聲明的那樣,它是一個異步調用,因此它立即返回。儘管如此,它還是將這個區塊排入私人隊列,並且(我想)會盡可能快地完成工作。但是,因爲這可能包括某種數據庫訪問,所以可能會被序列化。對於你提到的最後一點(group_wait ...),這是我想達到的。在開始使用它之前,我需要完全配置dataSource對象。 –

0

我想提出另一種使用助手庫的方法,它使異步任務更容易實現:它使用「Promises」作爲表示異步任務完成(或失敗)的手段。

基本上,代替完成塊,異步任務(方法在這種情況下)如下所示:

- (RXPromise*) doSomethingAsync; 

最終結果分別故障可經由將在「註冊處理程序獲得「下方所示一樣:

- (void) foo 
{ 
    RXPromise* promise = [self doSomethingAsync]; 
    promise.then(<completion handler block>, <error handler block>); 
} 

或短,與填充有實際的代碼處理程序佔位符:

- (void) foo 
{ 
    [self doSomethingAsync] 
    .then(^id(id result) 
    { 
     // result is the eventual result of the asynchronous task 
     return nil; // return the result of the handler 

    }, id(NSError*error) 
    { 
     NSLog(@"ERROR: %@, error"); // the task failed with error 
     return nil; 
    }); 
} 

注意:處理程序本身在專用線程上執行(實際爲併發調度隊列)。 爲了同步處理程序中共享訪問一個可以明確地指定的處理程序將被執行調度隊列:

- (void) foo 
{ 
    [self doSomethingAsync] 
    .thenOn(dispatch_get_main_queue()), ^id(id result) 
    { 
     // Here, we are executing on the main thread 
     // result is the eventual result of the asynchronous task 
     return nil; // return the result of the handler 

    }, id(NSError*error) 
    { 
     // Here, we are executing on the main thread 
     NSLog(@"ERROR: %@, error"); // the task failed with error 
     return nil; 
    }); 
} 

注意,在[self doSomethingAsync]執行的異步任務可以擁有自己的私人線程(或隊列)執行。

的多個異步任務「延續」,可以很容易地完成:

task1() 
.then(^id(id result1{ 
    return task2(result1); 
}, nil) 
.then(^id(id result2) { 
    return task3(result2); 
}, nil) 
.then(nil, ^id(NSError*error) 
    NSLog(@"Something went wrong in task1 or task2 or task3: %@", error); 
    return nil; 
); 

這個簡短的介紹後,您可以按如下解決您的問題:

定義你的異步任務,它會返回一個RXPromise:
- (RXPromise*) prepareWorkEntry1FromManagedObjectContext:(NSManagedObjectContext *)context; 
- (RXPromise*) prepareWorkEntry2FromManagedObjectContext:(NSManagedObjectContext *)context; 
- (RXPromise*) prepareWorkEntry3FromManagedObjectContext:(NSManagedObjectContext *)context; 


- (RXPromise*) prepareWorkEntry1FromManagedObjectContext:(NSManagedObjectContext *)context 
{ 
    RXPromise* promise = [RXPromise new]; 
    [context performBlock:^{ 
     Object *object = nil; 
     createObject(object); 

     if (object) { 
      // success 
      [promise fulfillWithValue:object]; 
     } 
     else { 
      // failure 
      [promise rejectWithReason:@"object creation failed"]; 
     } 
    }]; 

    return promise; 
} 

同樣,執行其他任務。

合併結果的處理:
- (void) foo 
{ 
    [self prepareWorkEntry1FromManagedObjectContext:moc] 
    .thenOn(self.sync_queue, (^id(id object)) { 
     [self.dataSourceArray addObject:object]; 
     return @"Finished 1"; 
    }, nil); 

    [self prepareWorkEntry2FromManagedObjectContext:moc] 
    .thenOn(self.sync_queue, (^id(id object)) { 
     [self.dataSourceArray addObject:object]; 
     return @"Finished 2"; 
    }, nil); 

    [self prepareWorkEntry3FromManagedObjectContext:moc] 
    .thenOn(self.sync_queue, (^id(id object)) { 
     [self.dataSourceArray addObject:object]; 
     return @"Finished 3"; 
    }, nil); 

} 

注意,dataSourceArray將在專用sync_queue訪問。這應該是一個串行調度隊列。

另請注意,該方法foo是有效異步的。

還有爲「異步等待」,直到一些異步任務已經完成的可能性:我這個庫,你可以在這裏找到的作者

- (void) foo { 

    NSArray* promises = @[ 

    [self prepareWorkEntry1FromManagedObjectContext:moc] 
    .thenOn(self.sync_queue, (^id(id object)) { 
     [self.dataSourceArray addObject:object]; 
     return @"Finished 1"; 
    }, nil), 

    [self prepareWorkEntry2FromManagedObjectContext:moc] 
    .thenOn(self.sync_queue, (^id(id object)) { 
     [self.dataSourceArray addObject:object]; 
     return @"Finished 2"; 
    }, nil), 

    [self prepareWorkEntry3FromManagedObjectContext:moc] 
    .thenOn(self.sync_queue, (^id(id object)) { 
     [self.dataSourceArray addObject:object]; 
     return @"Finished 3"; 
    }, nil) 

    ]; 

    [RXPromise all:promises] 
    .then(^id(id arrayOfPromises){ 
     // all tasks *and its handlers* finished. 
     ... 
     return nil; 
    }, nil); 
} 

RXPromise

+0

在你的 - (RXPromise *)準備..執行performBlock是一個異步調用,因此它會立即返回新創建的RXPromise對象,而不會遇到fulFill或reject方法。 –

+0

是的,承諾本身將立即返回,但結果只有在承諾被履行或拒絕後纔可用。這發生在塊被傳遞到上下文之後的任意時間執行。使用承諾代碼看起來就像是同步的。 ;) – CouchDeveloper

+0

也許簡短描述這裏發生的事情可能是有用的:當你調用'foo'時,你排隊三個塊 - 上下文的「工作量」。 'foo'立即返回 - 它是異步的。每個工作負載最終都會完成,並且會調用相應promise的處理程序,該處理程序將在sync_queue上執行。每個處理程序都會將任務的結果添加到'dataSourceArray'中。工作負載可以並行執行,但可能會按上下文順序執行。這與處理程序無關,它們在sync_queue上被序列化。 – CouchDeveloper