Update:
I have boiled the issue down to simply not being able to use key value coding on a class I made seen below
#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>
@interface CMTransformation : NSObject
@property(nonatomic) GLKVector3 position;
@property(nonatomic) GLKVector3 scale;
@property(nonatomic) GLKVector3 rotation;
@property(nonatomic) GLKVector3 anchor;
@property(nonatomic) GLKMatrix4 matrix;
- (GLKMatrix4)calculateMatrixWithParentTransformation:(CMTransformation *)parentTransformation;
@end
It has been my understanding and expience that I should be able to grab non NSObjects out as NSValues, however, I am finding it impossible to access these items (which are defined as unions) using KVC syntax:
CMTransformation* trans = [[CMTransformation alloc] init];
temp = [trans valueForKey:@"position"];
Similarly, if I try to access the underlying variable:
CMTransformation* trans = [[CMTransformation alloc] init];
temp = [trans valueForKey:@"_position"];
Both of these throw an exception because the key is not found. What am I missing here?
Previous Question
I have written some code which allows me to access a (somewhat) arbitrary structure with a string such as "transformation.position"
For some reason the code stops working on the second jump when I am trying to read a property from an NSObject. Here is the
NSString* property = actionDetails[@"Property"];
PropertyParts = [[property componentsSeparatedByString:@"."] mutableCopy];
int count = [PropertyParts count];
id current_object = initial_object;
for(int i = 0; i < count; i++)
{
NSString* current_part = PropertyParts[i];
current_object = [current_object valueForKey:current_part];
}
I have tried all possible syntax for property access including Property, property and _property.
Here is the custom NSObject declaration
#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>
@interface CMTransformation : NSObject
@property(nonatomic) GLKVector3 position;
@property(nonatomic) GLKVector3 scale;
@property(nonatomic) GLKVector3 rotation;
@property(nonatomic) GLKVector3 anchor;
@property(nonatomic) GLKMatrix4 matrix;
- (GLKMatrix4)calculateMatrixWithParentTransformation:(CMTransformation *)parentTransformation;
@end
Additionally, I can see after the first loop that the debugger says that CMTransformation* is populating currrent_object, so I am at a loss as to why I can't access its properties?
Would you be satisfied with implementing valueForUndefinedKey: and setValue:forUndefinedKey:?
If so, this works:
union Test
{
CGFloat f ;
NSInteger i ;
};
@interface TestClass : NSObject
@property ( nonatomic ) union Test t ;
@end
@implementation TestClass
-(void)setValue:(nullable id)value forUndefinedKey:(nonnull NSString *)key
{
Ivar v = class_getInstanceVariable( [ self class ], [ [ NSString stringWithFormat:@"_%@", key ] UTF8String ] ) ;
if ( v )
{
char const * const encoding = ivar_getTypeEncoding( v ) ;
if ( encoding[0] == '(' ) // unions only
{
size_t size = 0 ;
NSGetSizeAndAlignment( encoding, &size, NULL ) ;
uintptr_t ptr = (uintptr_t)self + ivar_getOffset( v ) ;
[ (NSValue*)value getValue:(void*)ptr ] ;
return ;
}
}
objc_property_t prop = class_getProperty( [ self class ], [ key UTF8String ] ) ;
if ( prop )
{
char const * const encoding = property_copyAttributeValue( prop, "T" ) ;
if ( encoding[0] == '(' ) // unions only
{
objc_setAssociatedObject( self, NSSelectorFromString( key ), value, OBJC_ASSOCIATION_COPY ) ;
return ;
}
}
[ super setValue:value forUndefinedKey:key ] ;
}
-(nullable id)valueForUndefinedKey:(nonnull NSString *)key
{
Ivar v = class_getInstanceVariable( [ self class ], [ [ NSString stringWithFormat:@"_%@", key ] UTF8String ] ) ;
if ( v )
{
char const * const encoding = ivar_getTypeEncoding( v ) ;
if ( encoding[0] == '(' )
{
size_t size = 0 ;
NSGetSizeAndAlignment( encoding, &size, NULL ) ;
uintptr_t ptr = (uintptr_t)self + ivar_getOffset( v ) ;
NSValue * result = [ NSValue valueWithBytes:(void*)ptr objCType:encoding ] ;
return result ;
}
}
objc_property_t prop = class_getProperty( [ self class ], [ key UTF8String ] ) ;
if ( prop )
{
return objc_getAssociatedObject( self, NSSelectorFromString( key ) ) ;
}
return [ super valueForUndefinedKey:key ] ;
}
@end
int main(int argc, const char * argv[])
{
@autoreleasepool
{
union Test u0 = { .i = 1234 } ;
TestClass * const testClass = [ TestClass new ] ;
[ testClass setValue:[ NSValue valueWithBytes:&u0 objCType:@encode( typeof( u0 ) ) ] forKey:@"t" ] ;
assert( testClass.t.i == 1234 ) ;
NSValue * const result = [ testClass valueForKey:@"t" ] ;
union Test u1 ;
[ result getValue:&u1 ] ;
assert( u1.i == 1234 ) ;
}
return 0;
}
(Paste it into your main.m file to try)