从 NSDictionary 中删除一个元素会导致它被过早释放

Removing an element from NSDictionary causes it to be deallocated prematurely

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

如下代码returnsEXC_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

最后,郑重声明,打开僵尸程序确实可以使代码正常工作,尽管这显然不是解决方案。

您的 window 的 releasedWhenClosed 属性 可能默认为 YES,这可能与 ARC 的内存管理冲突。创建 window.

时将其设置为 NO

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

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

任何其他顺序的事件和对象都会过早释放。