当前位置:  开发笔记 > IOS > 正文

核心数据privateQueue在访问关系时执行BlockAndWait死锁

如何解决《核心数据privateQueue在访问关系时执行BlockAndWait死锁》经验,为你挑选了1个好方法。

许多论坛都讨论过这个话题,但我仍然无法完全理解其performBlockAndWait实际效果如何.根据我的理解,context.performBlockAndWait(block: () -> Void)将在阻塞调用程序线程的同时在其自己的队列中执行该块. 文档说:

您将"标准"消息分组以发送到块中的上下文以传递给这些方法之一.

什么是"标准"消息?它还说:

基于队列的托管对象上下文的Setter方法是线程安全的.您可以直接在任何线程上调用这些方法.

这是否意味着我可以设置托管对象的属性,该对象是在performBlock*API之外的上下文的performBlockAPI中获取的?

根据我的理解,调用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)
    }
}

请注意,我在测试代​​码中不小心粘贴了performBlockAndWait两次fetchDepartment方法.

如果我没有调用fetchAllStudentsOfDepartment方法,它不会造成任何死锁 .但是一旦我调用 fetchAllStudentsOfDepartment,任何fetchDepartment方法调用都会永久阻止UI.

如果我删除print(student.firstName)fetchAllStudentsOfDepartment方法,那么它不会阻止.这意味着,只有在我访问关系的属性时才会阻止UI.

privateContext已经concurrencyType设定为.PrivateQueueConcurrencyType.上面的代码块UI只有当privateContextparentContextconcurrencyType设置为.MainQueueConcurrencyType.

我已经测试了与其他代码相同的代码,.xcdatamodel我现在确信它只会阻止访问关系的属性.我目前.xcdatamodel看起来像: 数据模型

请原谅我,如果信息是无关紧要的,但我只是花了8个小时后才分享我的所有观察结果.当UI被阻止时,我可以发布我的线程堆栈.总而言之,我有三个问题:

    什么是"标准"消息?

    我们可以设置托管对象的属性,该托管对象是在performBlock**之外的上下文中获取的performBlock吗?

    为什么performBlockAndWait在我的测试代码中行为不端并导致UI阻塞.

测试代码:您可以从这里下载测试代码.



1> Marcus S. Za..:

    标准消息是旧的Objective-C术语.这意味着您应该在performBlock或者对ManagedObjectContext及其子ManagedObjects执行所有常规方法调用performBlockAndWait.在块之外的私有上下文中允许的唯一调用是initsetParentContext.其他任何事情都应该在一个街区完成.

    不可以.只能在该私有上下文的队列中访问从私有上下文获取的任何托管对象.从另一个队列访问(读取或写入)违反了线程限制规则.

    您遇到阻塞问题的原因是因为您有两个级别的"mainQueue"上下文,并且"超越"队列系统.这是流程:

    您在主队列上创建上下文,然后将其创建为另一个主队列上下文的子项.

    您创建该第二层主队列上下文的私有子级

    您可以访问该专用队列上下文,使其尝试在当前已加载到主队列上下文中的对象中发生故障.

由于主要队列上下文的两个级别,它导致死锁,通常队列系统会看到潜在的死锁并避免死锁.

您可以通过将mainContext变量更改为:

lazy var mainContext: NSManagedObjectContext = {
    let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
    return appDelegate!.managedObjectContext
}

而你的问题就消失了,因为队列系统会看到阻止并避开它.您甚至可以通过在其中放置一个断点来查看发生的情况,performBlockAndWait()并看到您仍然在主队列中.

最后,没有理由在父/子设计中有两级主队列上下文.如果有的话,这不是一个很好的论据.

更新

我错过了你已经改变appDelegate中的模板代码并将整体上下文转换为私有上下文.

每个vc具有主MOC的模式抛弃了Core Data的许多好处.虽然在顶部有一个私有和一个主MOC(整个应用程序存在,而不仅仅是一个VC)是一个有效的设计,如果你performBlockAndWait从主队列这样做,它将无法工作.

performBlockAndWait当你阻止整个应用程序时,我不建议永远使用主队列. performBlockAndWait打电话时应该永远只能使用TO主队列(或者是一个背景到另一个背景).

推荐阅读
路人甲
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有