-->

object_setInstanceVariable with CGPoint

2019-02-19 01:21发布

问题:

Does anyone know how to set struct variables using object_setInstanceVariable? It is work fine for reference types:

object_setInstanceVariable(obj, "_variable", ref);

But do not work for structs such as CGPoint. Tried following ways:

1. object_setInstanceVariable(obj, "_variable", point);
2. object_setInstanceVariable(obj, "_variable", &point);
3. object_setInstanceVariable(obj, "_variable", (void **)&point);

I'm not sure I understand what I'm doing (especially for the last step)...

Thanks You in advance!!!

回答1:

object_setInstanceVariable and object_getInstanceVariable are really supposed to be used for objects only (though the interface declaration seems to indicate otherwise). You should not use those for builtins.

The more appropriate approach is to use class_getInstanceVariable and ivar_getOffset.

For example...

static void* getIvarPointer(id object, char const *name) {
    Ivar ivar = class_getInstanceVariable(object_getClass(object), name);
    if (!ivar) return 0;
    return (uint8_t*)(__bridge void*)object + ivar_getOffset(ivar);
}

That cast up there basically turns the object pointer into a pointer to a byte, so it can be added to the offset (which gives us the pointer to the iVar). We have to go through a void* first, because ARC does not allow a direct cast to uint8_t*.

It's use can be demonstrated thusly...

@implementation Foo {
    CGRect _rect;
}

- (CGRect*)rectPointer {
    CGRect *ptr = getIvarPointer(self, "_rect");
    NSAssert(ptr == &_rect, @"runtime discovered pointer should be same as iVar pointer");
    return ptr;
}

Now, you have a way to get the pointer of any iVar... just cast the returned void* appropriately.

BTW, I don't recommend doing this in general, but like any tool, there are uses for the runtime functions.

Oops. Forgot to show setting the value...

CGRect *rectPtr = getIvarPointer(someObject, "_rect");
if (rectPtr) {
    *rectPtr = CGRectMake(1, 2, 3, 4);
} else {
    // Handle error that the ivar does not exist...
}

EDIT

I'm sorry, it's still not really clear. Should I get this type (CGRect*) and then pass it to object_setInstanceVariable? Also, I have an error "Undefined symbols "_getIvarPointer"" - please let me know what H file should I include – Dmitry

No. You should not use object_setInstanceVariable at all. It's supposed to be used only for instance variables that are objects. Yours is a CGRect, so you should not use that function at all.

Use it like the very last example. Maybe this one is a bit more clear (it takes from your example).

Instead of doing something like your question asked:

object_setInstanceVariable(obj, "_variable", point);

Do this...

// Get a pointer to the instance variable.  Returns NULL if not found.
CGPoint *pointPtr = getIvarPointer(obj, "_variable");
if (pointPtr) {
    // Now, assign to the point, by de-referencing the pointer.
    *pointPtr = point;
}

Note, that I defined the getIvarPointer function as static which has file-local scope. It is not an apple-supplied function - you have to write it. If you are going to use this in multiple places, you need to remove the static so the linker can find it.



回答2:

ok, I found an answer. I look at difference between SELF address and variable address:

NSLog(@"%d",self);
NSLog(@"%d",&variable);

For example difference is 24 - then I write following code:

CGPoint *variable = (int)self + 24;
*variable = myCGPoint;

I can't even explain how much it is dirty hack!!! But it works :)

Thanks for the help Jody, I made a thumb up on you answer