1

I tried to get OpenGL ES native window (_win) from class:

@interface CAEAGLLayer : CALayer <EAGLDrawable>
{
@private
  struct _CAEAGLNativeWindow *_win;
}

so I extended it with category:

@interface CAEAGLLayer(MyLayer)
- (void*) fetchWin;
@end

@implementation CAEAGLLayer(MyLayer)
- (void*) fetchWin
{
    return self->_win;
}
@end

And use it in another Class:

@implementation MyClass
- (void)setupLayer
{
    _eaglLayer = (CAEAGLLayer*)self.layer;
    _eaglLayer.opaque = YES;
    NSLog(@"_eaglLayer _win: %p", [_eaglLayer fetchWin]);
}
@end 

But when building, met a link error:

Undefined symbols for architecture x86_64:
  "_OBJC_IVAR_$_CAEAGLLayer._win", referenced from:
      -[CAEAGLLayer(MyLayer) fetchWin] in OpenGLView.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Chogri
  • 326
  • 3
  • 15

1 Answers1

1

The linker can't find the symbol because, by default, "ivar symbols for @private and @package ivars are not exported." So you can't access _win directly by name in this manner, even if you have a header that refers to it.

However, you can dive into the ObjC runtime and pull out instance variables. In your case, you might try something like this in your -setupLayer (after #importing <objc/objc-runtime.h>):

Ivar winIvar = class_getInstanceVariable([CAEAGLLayer class], "_win");
void * winptr = (__bridge void *)object_getIvar(_eaglLayer, winIvar);

(You may also be able to use a simple -valueForKey: on the layer using @"_win" as the name of the key, but I prefer the runtime methods because they read clearer for what you're trying to do, which is basically circumvent the intended abstraction.)

Ben Zotto
  • 70,108
  • 23
  • 141
  • 204
  • when I use like this: Ivar ivar = class_getInstanceVariable([CAEAGLLayer class], "_win"); void * winptr = (__bridge void *)(object_getIvar(_eaglLayer, ivar)), it crashed. – Chogri Nov 03 '14 at 06:58
  • the crash stack like this: #0 0x0197b0be in objc_msgSend () #1 0x0197be3c in objc_retain () – Chogri Nov 03 '14 at 07:20
  • I used this with ARC, and that will cause crash. according http://stackoverflow.com/questions/8356232/object-getivar-fails-to-read-the-value-of-bool-ivar, I use it like "unsigned long winPtr; winPtr =((unsigned long(*)(id, Ivar)) object_getIvar)(_eaglLayer, winIvar);" that will work correctly ,thanks a lot. – Chogri Nov 03 '14 at 07:30
  • Good catch. Yes, because _win is a struct* and not an object, ARC wants to insert the retain (and Xcode makes you put in the bridge). Your suggestion from the other answer abuses the compiler enough to shut up the error and make it work at the expense of some theoretical fragility. I also edited the answer to remove the parens around the function call, which also seems to confuse the compiler enough to let it by, although it's just as lame, really. I'm trying to think of a cleaner way. – Ben Zotto Nov 03 '14 at 15:21