【发布时间】:2016-04-06 10:40:27
【问题描述】:
这个话题已经在很多论坛上讨论过,但我仍然无法完全理解performBlockAndWait 的实际工作原理。据我了解,context.performBlockAndWait(block: () -> Void) 将在阻塞调用者线程的同时在自己的队列中执行阻塞。 Documentation 说:
您将“标准”消息分组以发送到块内的上下文 传递给这些方法之一。
什么是“标准”消息?它还说:
基于队列的托管对象上下文的 Setter 方法是线程安全的。 您可以直接在任何线程上调用这些方法。
这是否意味着我可以设置在performBlock* API 内部获取的托管对象的属性,该上下文在performBlock* API 之外的上下文中?
根据我的理解,在并发类型为.MainQueueConcurrencyType 的上下文中调用performBlockAndWait(block: () -> Void) 会在从主线程调用时创建死锁并永远阻塞UI。但在我的测试中,它没有造成任何死锁。
我认为它应该创建死锁的原因是,performBlockAndWait会先阻塞调用者线程,然后在自己的线程上执行阻塞。由于上下文必须执行其块的线程与已经阻塞的调用者线程相同,因此它将永远无法执行其块并且线程永远保持阻塞状态。
但是我在一些奇怪的情况下面临僵局。我有以下测试代码:
@IBAction func fetchAllStudentsOfDepartment(sender: AnyObject) {
let entity = NSEntityDescription.entityForName("Department", inManagedObjectContext: privateContext)
let request = NSFetchRequest()
request.entity = entity
request.relationshipKeyPathsForPrefetching = ["students"]
var department: Department?
privateContext.performBlockAndWait { () -> Void in
department = try! self.privateContext.executeFetchRequest(request).first as? Department
print(department?.name)
guard let students = department?.students?.allObjects as? [Student] else {
return
}
for student in students {
print(student.firstName)
}
}
}
@IBAction func fetchDepartment(sender: AnyObject) {
let entity = NSEntityDescription.entityForName("Department", inManagedObjectContext: privateContext)
let request = NSFetchRequest()
request.entity = entity
privateContext.performBlockAndWait { () -> Void in
let department = try! self.privateContext.executeFetchRequest(request).first as? Department
print(department?.name)
}
privateContext.performBlockAndWait { () -> Void in
let department = try! self.privateContext.executeFetchRequest(request).first as? Department
print(department?.name)
}
}
请注意,我在测试代码的fetchDepartment 方法中不小心粘贴了两次performBlockAndWait。
- 如果我没有调用它不会造成任何死锁
fetchAllStudentsOfDepartment方法。但是一旦我打电话fetchAllStudentsOfDepartment,对fetchDepartment方法的任何调用 永远阻塞用户界面。 - 如果我在
fetchAllStudentsOfDepartment方法中删除print(student.firstName),那么它不会阻塞。这意味着,只有当我访问关系的属性时,它才会阻止 UI。 -
privateContext已将concurrencyType设置为.PrivateQueueConcurrencyType。仅当privateContext的parentContext将concurrencyType设置为.MainQueueConcurrencyType时,上述代码才会阻止UI。我也用其他
.xcdatamodel测试了相同的代码,我现在确信它只会在访问关系的属性时阻塞。我当前的.xcdatamodel看起来像:
如果信息无关紧要,请原谅我,但我只是在花了大约 8 小时后才分享我的所有观察结果。当 UI 被阻塞时,我可以发布我的线程堆栈。总而言之,我有三个问题:
- 什么是“标准”消息?
- 我们能否设置在
performBlock* API 内部获取的托管对象的属性,该 API 在performBlock* 外部的上下文中? - 为什么
performBlockAndWait在我的测试代码中行为不端并导致 UI 阻塞。
测试代码:您可以从here下载测试代码。
【问题讨论】:
标签: ios multithreading swift core-data nsmanagedobjectcontext