2016-06-16 122 views
0

更新說明:我使用Big Nerd Ranch CoreDataStack,如果你們想知道。NSManagedObjectContext - FetchRequest死鎖

我一直在努力解決這個問題一段時間了。基本上我試圖從CNContactStore中獲取聯繫人並在自定義的NSOperation中獲取ContactDetails(NSManagedObject)。

現在我正試圖在單元測試上運行整個過程。到目前爲止,這是我的代碼的樣子。

單元測試

func testThatLoaderOperationWorks() 
{ 
    var coreDataStack: CoreDataStack? 

    let semaphore = dispatch_semaphore_create(0); 

    CoreDataStack.constructSQLiteStack(withModelName: "DataModel") { result in 
     switch result 
     { 
     case .Success(let stack): 
      coreDataStack = stack 
     case .Failure(let error): 
      coreDataStack = nil 
      print (error) 
     } 


     dispatch_semaphore_signal(semaphore); 
    } 

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) 


    let contactStore = CNContactStore() 

    let loaderOperation = LoaderOperation.init(withWorkerContext: (coreDataStack?.newChildContext())!, andContactStore: contactStore) 
    loaderOperation.completionBlock = { 
     XCTAssert(true) 
    } 

    let operationQueue = NSOperationQueue() 
    operationQueue.maxConcurrentOperationCount = 1 
    operationQueue.addOperation(loaderOperation) 
    loaderOperation.waitUntilFinished() 
} 

操作子類

override func main() 
{  
    let keysToFetch = [ 
     CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName), 
     CNContactEmailAddressesKey, 
     CNContactPhoneNumbersKey, 
     CNContactImageDataAvailableKey, 
     CNContactThumbnailImageDataKey] 


    var allContainers: [CNContainer] = [] 
    do 
    { 
     allContainers = try contactStore.containersMatchingPredicate(nil) 
    } 
    catch 
    { 
     print("Error fetching containers") 
    } 

    var contactList: [CNContact] = [] 


    for container in allContainers 
    { 
     let fetchPredicate = CNContact.predicateForContactsInContainerWithIdentifier(container.identifier) 

     do 
     { 
      let containerResults = try contactStore.unifiedContactsMatchingPredicate(fetchPredicate, keysToFetch: keysToFetch) 
      contactList.appendContentsOf(containerResults) 
     } 
     catch 
     { 
      print("Error fetching results for container") 
     } 
    } 

    self.workerContext.performBlockAndWait 
    {   
     let fetchRequest = NSFetchRequest(entityName: "ContactDetails") 
     do 
     { 
      let list = try self.workerContext.executeFetchRequest(fetchRequest) 
      print("The List: \(list)") 
     } 
     catch 
     { 
      print(error) 
     } 
    } 
} 

技術上我想要做到的,是能夠提取通訊錄,跨數據的進行參考他們,我從CoreData獲取。但是,當我運行executeFetchRequest時會發生死鎖。我在某處做錯了什麼?

回答

0

我找到了答案。真的很簡單。這個主要原因是我試圖讓測試方法等待waitUntilFinished(),直到操作結束,並且在操作本身中,我試圖強制性地等待performBlockAndWait(),從而導致死鎖。

刪除waitUntilFinished()並用performBlock()替換performBlockAndWait()

此外,爲了做一個單元測試異步代碼,你必須expectationWithDescription("description")

基本上單元測試方法應該看起來像這樣

let asyncExpectation = expectationWithDescription("longRunningFunction") 

    .... some stuff here .... 


    let loaderOperation = LoaderOperation.init(withWorkerContext: (coreDataStack?.newChildContext())!, andContactStore: contactStore) 
    loaderOperation.completionBlock = { 
     asyncExpectation.fulfill() 
    } 

    let operationQueue = NSOperationQueue() 
    operationQueue.addOperation(loaderOperation) 


    self.waitForExpectationsWithTimeout(10) { (error) in 
     XCTAssertNil(error, "Something went wrong") 
     XCTAssert(true) 
    }