jmercouris
jmercouris

Reputation: 348

Removing an element from NSDictionary causes it to be deallocated prematurely

Full project can be seen here (for context: https://github.com/atlas-engineer/next-cocoa)

The following code returns EXC_BAD_ACCESS:

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

The following code, however, works

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

As does the following:

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

It is only somehow when you put them together that everything breaks.

For reference, I've provided the AutokeyDictionary implementation below, which is the value of [self windows] in the examples above

//
//  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

Lastly, for the record, turning on zombies does make the code work, though that is obviously not a solution.

Upvotes: 0

Views: 55

Answers (2)

jmercouris
jmercouris

Reputation: 348

The correct answer ended up being the following sequence of code

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

any other order of events and the object would be prematurely released.

Upvotes: 0

Ken Thomases
Ken Thomases

Reputation: 90571

Your window's releasedWhenClosed property is probably defaulting to YES, which is likely to conflict with ARC's memory management. Set it to NO when you create the window.

Upvotes: 2

Related Questions