Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing private variable in Category results in linker error

EDIT: I'm not going to do this, I now realize how dangerous this can be. But, the question stays for purely academic purposes.

I'm trying to implement a category on NSCollectionView that will let me access the private variable _displayedItems. I need to be able to access it in my subclass. So, I've created the following category:

@interface NSCollectionView (displayedItems)

- (NSMutableArray *)displayedItems;

@end


@implementation NSCollectionView (displayedItems)

- (NSMutableArray *)displayedItems
{
    return _displayedItems;
}

@end

...which seems like it should work perfectly. However, when I try to compile this, the linker gives me the following error:

Undefined symbols:
  "_OBJC_IVAR_$_NSCollectionView._displayedItems", referenced from:
      -[NSCollectionView(displayedItems) displayedItems] in NSCollectionView+displayedItems.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

I know for a fact that _displayedItems exists in NSCollectionView, I've looked at the interface and also printed it's contents using gdb. Does anyone know of a way to fix this?

Thanks in advance!
Billy

like image 673
vilhalmer Avatar asked Dec 13 '10 13:12

vilhalmer


2 Answers

_displayedItems is a private ivar, so you shouldn't access it, even from a category.

That said, you should try compiling the same code with

gcc -arch i386

and

gcc -arch x86_64

and see the difference. In the 32 bit mode you don't see the error. This shows how fragile the situation is. You really shouldn't.

That said, there's a way to get that ivar by abusing KVC:

@implementation NSCollectionView (displayedItems)

- (NSMutableArray *)myDisplayedItems
{
    return [self valueForKey:@"displayedItems"];
}

@end

Note that you shouldn't name your method just as displayedItems. That would make an infinite loop, because the KVC machinery would find your method earlier than the ivar. See here.

Or you can access any hidden ivar using Objective-C runtime functions. That's also fun.

However, let me say again. There's a big difference in knowing you can do one thing and doing that thing for real. Just think of any hideous crime. and doing that by yourself.

DON'T DO THAT!!!!!

like image 87
Yuji Avatar answered Nov 13 '22 20:11

Yuji


You shouldn't really, but access it like a pointer to a member of a struct:

-(NSMutableArray *)displayedItems {
  return self->_displayedItems;
}

This is a fragile thing to do, as I'm sure you're aware however ;)

UPDATE: Since you've mentioned the above doesn't work, try dropping down to the runtime:

-(NSMutableArray *)displayedItems {
        NSMutableArray *displayedItems;
        object_getInstanceVariable(self, "_displayedItems", (void *)&displayedItems);
        return displayedItems;
}

(Tested, works)

like image 5
d11wtq Avatar answered Nov 13 '22 22:11

d11wtq