【问题标题】:-[NSError retain]: message sent to deallocated instance-[NSError 保留]:消息发送到已释放的实例
【发布时间】:2016-12-09 02:44:02
【问题描述】:

启用 Zombies 后,我在以下 saveToURL 调用中收到标题错误(发送到已释放的 NSError 实例的消息):

[aDocument saveToURL:aDocument.fileURL
forSaveOperation:UIDocumentSaveForOverwriting
completionHandler:^(BOOL success) { ...

堆栈跟踪如下所示:

aDocument 是 UIManagedDocument 的子类的一个实例。我进行了并发调试,我查看了是否有任何线程冲突,但还没有找到。我该如何调试?

编辑:还尝试了以下代码,但发生了相同的崩溃

__weak typeof(self) weakSelf = self;

    [aDocument saveToURL:aDocument.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:^(BOOL success) {
        if (success) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf documentSaved:aDocument forRestoredAssessment:patientAssessment];
            });
        }
    }];

编辑:添加赏金

【问题讨论】:

  • 你在completionHandler里面做什么?
  • 这个方法被称为... [self documentSaved:aDocument forRestoredAssessment:patientAssessment];它做了很多事情。
  • 一般来说,从不是主队列的任何线程中触摸 UIKit 中的任何内容或与 UIKit 直接相关的任何内容都是不安全的。事实上,我认为这是问题的核心。
  • aDocument 是局部变量还是属性?
  • @BradThomas 我在 UIDocument 文档中注意到这一行:Instead, in this case, the error is available to your app in the handleError:userInteractionPermitted: method and in the UIDocumentStateChangedNotification notification. 这里:developer.apple.com/reference/uikit/uidocument/…。也许如果您收听此通知,您可能会弄清楚 NSError 发生了什么。

标签: objective-c memory-management nserror nszombie uimanageddocument


【解决方案1】:

我们发现当父上下文发生合并冲突时会发生这种僵尸。如果您在子类中覆盖[UIManagedDocument writeContents:toURL:forSaveOperation:originalContentsURL:error] 方法,请使用:NSManagedObjectContext *context = [(NSDictionary *)contents objectForKey:@"parentContext"]; 访问父上下文。如果存在更改,请使用以下标准代码保存父上下文。如果通过设置合并策略或重置上下文来解决冲突,则不会出现僵尸。

- (BOOL)writeContents:(id)contents
                toURL:(NSURL *)url
     forSaveOperation:(UIDocumentSaveOperation)saveOperation
  originalContentsURL:(NSURL *)originalContentsURL
                error:(NSError * _Nullable __autoreleasing *)outError {

    NSError *error = nil;

    NSManagedObjectContext *context = self.managedObjectContext.parentContext;

    if (context.hasChanges) {
        [context performBlockAndWait:^{

            NSError *saveError = nil;
            if (![context save:&saveError]) {
                NSLog(@"Document Writing: error saving context %@", saveError);
                [context reset];
            }
        }];
    }
    return [super writeContents:contents
                          toURL:url
               forSaveOperation:saveOperation
            originalContentsURL:originalContentsURL
                          error:outError];
}

【讨论】:

    【解决方案2】:

    我相信我找到了造成这种情况的原因,因为我进行了以下更改,然后错误就消失了。这个错误很难解决,因为它没有直接指出这是原因。

    我正在使用 UIDocumentSaveForOverwriting,但我发现有时该文件已被另一个进程删除。

    因此,为了解决我测试文件存在的问题,然后基于此使用 UIDocumentSaveForOverwriting 或 UIDocumentSaveForCreating。

    【讨论】:

    • 你能推荐一个检查文件存在的代码吗?
    • @GregoryWilsonPullyattu 很抱歉,我无法再访问该项目的源代码,而且距离我编写 ObjC 已经 3 年了,所以我必须像你一样用谷歌搜索答案
    【解决方案3】:

    看起来调用完成块代码的对象可能不在那个时候,所以你需要一些方法来保持它的存在。

    试试这个代码:

    __weak typeof(self) weakSelf = self;
    
    dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf documentSaved:aDocument forRestoredAssessment:patientAssessment]; 
    });
    

    如果您仍有问题,请告诉我。

    【讨论】:

    • 我尝试了您的解决方案。我犯了同样的错误。我真的不认为它会在街区内崩溃。我怀疑这可能与尝试保存另一个线程可能正在尝试执行相同操作有关。
    • @BradThomas aDocument 是函数/方法中的局部变量还是某个类变量?
    • aDocument是传入方法的参数
    • @BradThomas 在完成块内是否有有效值?
    • 是的,它似乎是。但是当它崩溃时,它甚至没有进入完成块。它在 aDocument saveToURL 调用时崩溃
    猜你喜欢
    • 2015-05-23
    • 1970-01-01
    • 2014-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多