在Objective-C中使用KVC读取联合属性

时间:2015-06-23 15:24:51

标签: ios objective-c introspection key-value-coding

更新

我已经将问题归结为根本无法在我在下面看到的课程中使用键值编码

#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作为NSValues获取,但是,我发现使用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];
}

我已经尝试了所有可能的属性访问语法,包括Property,property和_property。

这是自定义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,所以我不知道为什么我无法访问它的属性?

2 个答案:

答案 0 :(得分:1)

您是否满意实现 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文件中以尝试)

答案 1 :(得分:0)

我相当肯定KVC不能与C工会合作。请参阅Objective-C KVO doesn't work with C unions中的bbum讨论。我知道这与KVO有关,但他说:

  噢......整洁。 KVO w /工会根本不起作用。似乎运行时根本不会识别该类有一个名为vectorUnionValue的键。

这可能是你的问题。您可能需要制作一些其他属性才能访问它们。也许是这样的事情:

@property(nonatomic) GLKVector3 positionValue;

- (NSValue *)positionValue {
    return [NSValue valueWithPointer:&_positionValue];
}

我不喜欢那样;并且可能有更好的方法,但我怀疑你需要这些方面的东西。你可能无法以这种方式进入工会。

这是内置于KVC。如果您的对象具有transformation对象属性,并且该对象具有position属性,则路径transformation.position将返回该属性。没有必要自己分手。

transformation必须是符合键值编码的对象本身。有符合KVC的规则,你的变量的不规则随机命名可能会破坏它。可可变量和性质应该是领先的较低的camelcase。你永远不应该有一个领先的上限(这是一个类),你应该避免内部下划线。 KVC使用此命名约定允许它自动查找属性。例如,它可以使用它来知道foo的setter是setFoo:(不是大写)。

所有中间级别必须是对象。例如,GLKVector3不是对象,因此您无法使用KVC来查询其子组件。你必须返回整个矢量。遗憾的是,您只能通过查询头文件或文档来了解GLKVector3是否是一个对象(或者根据经验对其进行直观了解;我绝不会期望它成为一个对象,因为它&#39; sa&#34; mathy thing&#34; with通常是结构;这是结构的联合。)