Vis*_*ngh 10 multithreading core-data nsmanagedobjectcontext ios swift
许多论坛都讨论过这个话题,但我仍然无法完全理解其performBlockAndWait
实际效果如何.根据我的理解,context.performBlockAndWait(block: () -> Void)
将在阻塞调用程序线程的同时在其自己的队列中执行该块. 文档说:
您将"标准"消息分组以发送到块中的上下文以传递给这些方法之一.
什么是"标准"消息?它还说:
基于队列的托管对象上下文的Setter方法是线程安全的.您可以直接在任何线程上调用这些方法.
这是否意味着我可以设置托管对象的属性,该对象是在performBlock
*API之外的上下文的performBlock
API中获取的?
根据我的理解,调用performBlockAndWait(block: () -> Void)
具有并发类型的上下文.MainQueueConcurrencyType
将在从主线程调用时创建死锁并永久阻止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)
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,我在测试代码中不小心粘贴了performBlockAndWait
两次fetchDepartment
方法.
fetchAllStudentsOfDepartment
方法,它不会造成任何死锁
.但是一旦我调用
fetchAllStudentsOfDepartment
,任何fetchDepartment
方法调用都会永久阻止UI.print(student.firstName)
的fetchAllStudentsOfDepartment
方法,那么它不会阻止.这意味着,只有在我访问关系的属性时才会阻止UI.privateContext
已经concurrencyType
设定为.PrivateQueueConcurrencyType
.上面的代码块UI只有当privateContext
的parentContext
已concurrencyType
设置为.MainQueueConcurrencyType
.
我已经测试了与其他代码相同的代码,.xcdatamodel
我现在确信它只会阻止访问关系的属性.我目前.xcdatamodel
看起来像:
请原谅我,如果信息是无关紧要的,但我只是花了8个小时后才分享我的所有观察结果.当UI被阻止时,我可以发布我的线程堆栈.总而言之,我有三个问题:
performBlock
**之外的上下文中获取的performBlock
吗?performBlockAndWait
在我的测试代码中行为不端并导致UI阻塞.测试代码:您可以从这里下载测试代码.
标准消息是旧的Objective-C术语.这意味着您应该在performBlock
或者对ManagedObjectContext及其子ManagedObjects执行所有常规方法调用performBlockAndWait
.在块之外的私有上下文中允许的唯一调用是init
和setParentContext
.其他任何事情都应该在一个街区完成.
不可以.只能在该私有上下文的队列中访问从私有上下文获取的任何托管对象.从另一个队列访问(读取或写入)违反了线程限制规则.
您遇到阻塞问题的原因是因为您有两个级别的"mainQueue"上下文,并且"超越"队列系统.这是流程:
由于主要队列上下文的两个级别,它导致死锁,通常队列系统会看到潜在的死锁并避免死锁.
您可以通过将mainContext
变量更改为:
lazy var mainContext: NSManagedObjectContext = {
let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
return appDelegate!.managedObjectContext
}
Run Code Online (Sandbox Code Playgroud)
而你的问题就消失了,因为队列系统会看到阻止并避开它.您甚至可以通过在其中放置一个断点来查看发生的情况,performBlockAndWait()
并看到您仍然在主队列中.
最后,没有理由在父/子设计中有两级主队列上下文.如果有的话,这不是一个很好的论据.
我错过了你已经改变appDelegate中的模板代码并将整体上下文转换为私有上下文.
每个vc具有主MOC的模式抛弃了Core Data的许多好处.虽然在顶部有一个私有和一个主MOC(整个应用程序存在,而不仅仅是一个VC)是一个有效的设计,如果你performBlockAndWait
从主队列这样做,它将无法工作.
performBlockAndWait
当你阻止整个应用程序时,我不建议永远使用主队列. performBlockAndWait
打电话时应该永远只能使用TO主队列(或者是一个背景到另一个背景).
- 什么是“标准”消息?
发送到托管对象上下文或任何托管对象的任何消息。请注意,文档继续澄清......
There are two exceptions:
* Setter methods on queue-based managed object contexts are thread-safe.
You can invoke these methods directly on any thread.
* If your code is executing on the main thread, you can invoke methods on the
main queue style contexts directly instead of using the block based API.
Run Code Online (Sandbox Code Playgroud)
因此,除了 MOC 上的 setter 方法之外的任何内容都必须从performBlock
. MOC 上的任何方法都NSMainQueueConcurrencyType
可以从主线程调用,而无需包装在performBlock
.
- 我们可以设置在performBlock* 外部上下文的performBlock* API 内获取的托管对象的属性吗?
不可以。对托管对象的任何访问都必须受到performBlock
托管对象所在托管对象上下文内部的保护。请注意驻留在主队列 MOC 中的托管对象从主队列访问的例外情况。
- 为什么 PerformBlockAndWait 行为不当并导致我的测试代码中出现 UI 阻塞。
这不是行为不端。 performBlockAndWait
是可重入的,但仅当已经处理performBlock[AndWait]
呼叫时。
performBlockAndWait
除非您别无选择,否则切勿使用。对于嵌套上下文来说尤其有问题。
performBlock
代替使用。
归档时间: |
|
查看次数: |
3986 次 |
最近记录: |