在 Objective-C 中使用 KVC 读取联合属性
Reading union properties using KVC in Objective-C
更新:
我已将问题归结为无法在下面看到的 class 上使用键值编码
#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
根据我的理解和经验,我应该能够将非 NSObject 作为 NSValue 提取出来,但是,我发现无法使用 KVC 语法访问这些项目(定义为联合):
CMTransformation* trans = [[CMTransformation alloc] init];
temp = [trans valueForKey:@"position"];
同样,如果我尝试访问基础变量:
CMTransformation* trans = [[CMTransformation alloc] init];
temp = [trans valueForKey:@"_position"];
这两个都抛出异常,因为找不到密钥。我在这里错过了什么?
上一题
我写了一些代码,允许我访问带有字符串的(某种程度上)任意结构,例如 "transformation.position"
出于某种原因,当我尝试从 NSObject 读取 属性 时,代码在第二次跳转时停止工作。这是
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];
}
我已经尝试了所有可能的 属性 访问语法,包括 属性、属性 和 _属性.
这是自定义的 NSObject 声明
#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
此外,我可以在第一个循环后看到调试器说 CMTransformation* 正在填充 currrent_object,所以我不知道为什么我不能访问它的属性?
我相当确定 KVC 不能与 C 联合一起使用。请参阅 Objective-C KVO doesn't work with C unions 中 bbum 的讨论。我知道这与 KVO 有关,但他说:
Oooh... neat. KVO w/unions simply doesn't work. It appears that the runtime simply does not even recognize that the class has a key called vectorUnionValue at all.
这可能是您的问题。您可能需要制作其他一些 属性 才能访问它们。也许是这样的:
@property(nonatomic) GLKVector3 positionValue;
- (NSValue *)positionValue {
return [NSValue valueWithPointer:&_positionValue];
}
我不喜欢那个;并且可能有更好的方法,但我怀疑您需要这些方面的东西。您可能无法通过这种方式加入工会。
这是内置于 KVC 中的。如果您有一个对象有一个 transformation
对象 属性,并且该对象有一个 position
属性,那么路径 transformation.position
将 return它。不用自己拆了
但是transformation
必须是对象本身,符合key-value编码。有符合 KVC 的规则,您对变量的不规则随机命名可能会破坏它。 Cocoa 变量和属性应该是前导-小驼峰式。你永远不应该有前导上限(即 class),你应该避免内部下划线。 KVC 使用此命名约定以允许它自动查找属性。例如,它可以使用它来知道 foo
的 setter 是 setFoo:
(不是大写)。
并且所有的中间层次都必须是对象。例如,GLKVector3
不是一个对象,因此您不能使用 KVC 来询问它的子组件。您必须 return 整个矢量。不幸的是,您只能通过查阅头文件或文档(或根据经验对它有直觉;我永远不会期望它是一个对象,因为它是一个 "mathy thing" 通常是结构;这个是结构的联合)。
您对实施 valueForUndefinedKey: 和 setValue:forUndefinedKey: 是否满意?
如果是这样,这有效:
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;
}
(将其粘贴到您的 main.m 文件中进行尝试)
更新:
我已将问题归结为无法在下面看到的 class 上使用键值编码
#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
根据我的理解和经验,我应该能够将非 NSObject 作为 NSValue 提取出来,但是,我发现无法使用 KVC 语法访问这些项目(定义为联合):
CMTransformation* trans = [[CMTransformation alloc] init];
temp = [trans valueForKey:@"position"];
同样,如果我尝试访问基础变量:
CMTransformation* trans = [[CMTransformation alloc] init];
temp = [trans valueForKey:@"_position"];
这两个都抛出异常,因为找不到密钥。我在这里错过了什么?
上一题
我写了一些代码,允许我访问带有字符串的(某种程度上)任意结构,例如 "transformation.position"
出于某种原因,当我尝试从 NSObject 读取 属性 时,代码在第二次跳转时停止工作。这是
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];
}
我已经尝试了所有可能的 属性 访问语法,包括 属性、属性 和 _属性.
这是自定义的 NSObject 声明
#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
此外,我可以在第一个循环后看到调试器说 CMTransformation* 正在填充 currrent_object,所以我不知道为什么我不能访问它的属性?
我相当确定 KVC 不能与 C 联合一起使用。请参阅 Objective-C KVO doesn't work with C unions 中 bbum 的讨论。我知道这与 KVO 有关,但他说:
Oooh... neat. KVO w/unions simply doesn't work. It appears that the runtime simply does not even recognize that the class has a key called vectorUnionValue at all.
这可能是您的问题。您可能需要制作其他一些 属性 才能访问它们。也许是这样的:
@property(nonatomic) GLKVector3 positionValue;
- (NSValue *)positionValue {
return [NSValue valueWithPointer:&_positionValue];
}
我不喜欢那个;并且可能有更好的方法,但我怀疑您需要这些方面的东西。您可能无法通过这种方式加入工会。
这是内置于 KVC 中的。如果您有一个对象有一个 transformation
对象 属性,并且该对象有一个 position
属性,那么路径 transformation.position
将 return它。不用自己拆了
但是transformation
必须是对象本身,符合key-value编码。有符合 KVC 的规则,您对变量的不规则随机命名可能会破坏它。 Cocoa 变量和属性应该是前导-小驼峰式。你永远不应该有前导上限(即 class),你应该避免内部下划线。 KVC 使用此命名约定以允许它自动查找属性。例如,它可以使用它来知道 foo
的 setter 是 setFoo:
(不是大写)。
并且所有的中间层次都必须是对象。例如,GLKVector3
不是一个对象,因此您不能使用 KVC 来询问它的子组件。您必须 return 整个矢量。不幸的是,您只能通过查阅头文件或文档(或根据经验对它有直觉;我永远不会期望它是一个对象,因为它是一个 "mathy thing" 通常是结构;这个是结构的联合)。
您对实施 valueForUndefinedKey: 和 setValue:forUndefinedKey: 是否满意?
如果是这样,这有效:
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;
}
(将其粘贴到您的 main.m 文件中进行尝试)