2016-06-16 124 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) 
    }