realm-swift: EXC_BAD_ACCESS crash when using NSOutlineView

I’m creating an NSOutlineView populated by objects saved on Realm. The object is called Person. And this is how it looks:

@interface Person : RLMObject

@property NSString *name;
@property NSInteger indent;
@property NSInteger order;

// Ignored properties
@property NSArray *contacts;

The contacts array returns a query. Those contacts are child of the person, but they also have the same object class, Person.

And this is the view controller:

#import "MainWindowController.h"
#import "Person.h"

@interface MainWindowController () <NSOutlineViewDataSource, NSOutlineViewDelegate>

@property (strong) IBOutlet NSOutlineView *outlineView;

@property (nonatomic, strong) RLMResults *array;
@property (nonatomic, strong) RLMNotificationToken *notification;

@end

@implementation MainWindowController

- (id)init
{
    self = [super initWithWindowNibName:@"MainWindow"];

    if (self) {
        __weak typeof(self) weakSelf = self;

        self.notification = [RLMRealm.defaultRealm addNotificationBlock:^(NSString *note, RLMRealm *realm) {
            [weakSelf.outlineView reloadData];
        }];

        self.array = [[Person objectsWhere:@"indent == 1"] sortedResultsUsingProperty:@"order" ascending:YES];
    }

    return self;
}

- (void)windowDidLoad
{
    [super windowDidLoad];

    [self.outlineView setRowHeight:50];
}

#pragma mark - NSOutlineView

- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{
    if (item == nil) {
        return self.array[index];
    }
    else if ([item isKindOfClass:[Person class]]) {
        return [item contacts][index];
    }

    return nil;
}

- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
    if (item == nil) {
        return NO;
    }
    else if ([item isKindOfClass:[Person class]]) {
        return [item contacts].count > 0;
    }

    return NO;
}

- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
    if (item == nil) {
        return self.array.count;
    }
    else if ([item isKindOfClass:[Person class]]) {
        return [item contacts].count;
    }

    return 0;
}

- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
    NSView *cellView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 250, 0)];

    NSTextField *titleTextField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 15, 250, 18)];
    [titleTextField setEditable:NO];
    [titleTextField setBordered:NO];
    titleTextField.backgroundColor = [NSColor clearColor];
    titleTextField.textColor = [NSColor blackColor];
    titleTextField.focusRingType = NSFocusRingTypeNone;
    titleTextField.stringValue = [item name];
    [cellView addSubview:titleTextField];

    return cellView;
}

The “root” cells are displayed correctly, but then, when I click the arrow to expand one of those root cells, the app crashes with an EXC_BAD_ACCESS error. I used NSZombie to know more about this crash, and that’s what it tells me:

-[RLMAccessor_v0_Person retain]: message sent to deallocated instance 0x60800016ba00

I debugged all the data source methods of the NSOutlineView when tapping the arrow to expand the row, but no method is called.

Any idea of what’s happening?

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Comments: 23 (11 by maintainers)

Most upvoted comments

I am having EXC_BAD_ACCESS when expandItem is called (similar to expand group in UI)

When I try to use debugger to po item in viewForTableColumn delegate method, it shows as [Deleted Object], but it is the Realm Object if I log it using dump(item)

My solution to this, is copying the array, and call expandItem as late as possible (in my case viewDidAppear)

Hope this provides an idea about the issue.

i ran into the same issue and ended up solving it in a similar way as @lm2s—however this doesn’t seem like a very performant solution.

are there any examples of using realm properly with NSOutlineView?

For future reference of anyone having this problem, like I did.

Instead of using the RLMResults array directly in - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item.

Copy the objects into a NSArray to make sure that they are not released and then use the NSArray.

Using the above code as an example:

  • Declare a NSArray *objectsArray
  • Implement a setter for RLMResults *array.
  • In this setter copy the objects in RLMResults array into the objectsArray.
  • Use the objectsArray instead of array