object_setInstanceVariable with CGPoint

ios, iphone, iphone-privateapi

Solution

`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.

Problem

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

Original source

Related problems