【问题标题】:Removing an element from NSDictionary causes it to be deallocated prematurely从 NSDictionary 中删除一个元素会导致它过早地被释放
【发布时间】:2018-09-23 01:19:10
【问题描述】:

完整的项目可以在这里看到(上下文:https://github.com/atlas-engineer/next-cocoa

以下代码返回 EXC_BAD_ACCESS:

- (bool)windowClose:(NSString *)key
{
    NSWindow *window = [[self windows] objectForKey:key];
    [[self windows] removeObjectForKey:key];
    [window close];
    return YES;
}

但是,下面的代码可以工作

- (bool)windowClose:(NSString *)key
{
    [[self windows] removeObjectForKey:key];
    return YES;
}

如下:

- (bool)windowClose:(NSString *)key
{
    NSWindow *window = [[self windows] objectForKey:key];
    [window close];
    return YES;
}

只有当你把它们放在一起时,一切都会破裂。

作为参考,我在下面提供了 AutokeyDictionary 的实现,也就是上面例子中[self windows] 的值

//
//  AutokeyDictionary.m
//  next-cocoa
//
//  Created by John Mercouris on 3/14/18.
//  Copyright © 2018 Next. All rights reserved.
//

#import "AutokeyDictionary.h"

@implementation AutokeyDictionary
@synthesize elementCount;

- (instancetype) init
{
    self = [super init];
    if (self)
    {
        [self setElementCount:0];
        _dict = [[NSMutableDictionary alloc] init];
    }
    return self;
}

- (NSString *) insertElement:(NSObject *) object
{
    NSString *elementKey = [@([self elementCount]) stringValue];
    [_dict setValue:object forKey: elementKey];
    [self setElementCount:[self elementCount] + 1];
    return elementKey;
}

- (NSUInteger)count {
    return [_dict count];
}

- (id)objectForKey:(id)aKey {
    return [_dict objectForKey:aKey];
}

- (void)removeObjectForKey:(id)aKey {
    return [_dict removeObjectForKey:aKey];
}

- (NSEnumerator *)keyEnumerator {
    return [_dict keyEnumerator];
}

- (NSArray*)allKeys {
    return [_dict allKeys];
}

@end

最后,为了记录,打开僵尸确实可以使代码工作,尽管这显然不是解决方案。

【问题讨论】:

  • @matt 我删除了return 并将其保留为[_dict removeObjectForKey:aKey];,但仍然没有运气。
  • @matt,我还没有关闭 ARC,但是如果有办法检查它是否仍然启用,我很乐意提供帮助
  • “使用未声明的标识符保留”,因此必须启用 ARC,我很想将其关闭,因为它似乎比它更麻烦
  • 您窗口的releasedWhenClosed 属性可能默认为YES,这可能与ARC 的内存管理冲突。将其设置为NO
  • @matt 如果你想看看,你可以在此处克隆项目:github.com/atlas-engineer/next-cocoa server.m 是调用此方法的原因,也许它会导致问题成为块或其他东西的一部分,而不是确定

标签: objective-c memory-management exc-bad-access


【解决方案1】:

您窗口的releasedWhenClosed 属性可能默认为YES,这可能与ARC 的内存管理冲突。创建窗口时将其设置为NO

【讨论】:

  • 这应该被认为是一个错误吗?
  • @matt 我认为这是一个文档错误,没有指出与 ARC 的不兼容性。至于一个实现错误,很难看出它是如何被纠正的。 Cocoa 可能会或可能不会在内部使用 ARC,它必须为可能使用或不使用 ARC 的客户端工作(包括在 ARC 出现之前构建的客户端)。就个人而言,我一直对 releasedWhenClosed 的存在感到恼火,并且总是将其关闭以获得健全的内存管理语义。
【解决方案2】:

正确答案最终是以下代码序列

- (bool)windowClose:(NSString *)key
{
    NSWindow *window = [[self windows] objectForKey:key];
    [window setReleasedWhenClosed:NO];
    [window close];
    [[self windows] removeObjectForKey:key];
    return YES;
}

任何其他顺序的事件和对象都会被提前释放。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多