2015-06-23 7 views
2

Update:Чтения союзных свойств с помощью KVC в Objective-C

Я вареные вопросы вниз, чтобы просто не в состоянии использовать ключевое кодирование значения на классе я сделал видел ниже

#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 

Я понял и понял, что я должен ухватить не NSObjects как 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.

Вот обычай 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, так что я в недоумении, почему я не могу получить доступ к своим свойствам?

+0

ли 'current_object' в CMTransformation? Почему вы делаете его типом 'id' вместо того, чтобы быть явным? – timgcarlson

+0

Это предназначено, чтобы иметь возможность выполнять итерацию любой вложенной структуры NSObject, а не только конкретный пример. В ARC вы можете использовать кодирование KV даже для переменных класса. – David

+0

вы можете попробовать реализовать _-valueForUndefinedKey: _ для исправления этого случая. (Или, может быть, _ + resolInstanceMethod: _) – nielsbot

ответ

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 попробовать)

+0

Ты потрясающий! Я как раз собирался сдаться и уладить дешевый хак, но это работает фантастично! Он по существу заполняет любую дыру, которую Apple имеет в своей реализации, чтобы она работала так же, как и с Союзами. Я буду добавлять эти функции в наши базовые классы. Это теперь работает так, как ожидалось: id transform = [node valueForKey: @ "transform"]; id pos = [transform valueForKey: @ "position"]; GLKVector3 vect; [pos getValue: &vect]; – David

+1

спасибо. Я на самом деле потратил много времени, пытаясь заставить методы более низкого уровня работать, но выглядит как-то где-то в среде выполнения obj-c, которая активно терпит неудачу в типах union. к счастью, они все еще используют типы union/unbox union, поэтому реализуется valueForUndefinedKey: работает. – nielsbot

+1

FYI, я просто представил это как ошибку для Apple. (Ошибка: 21526543). Надеюсь, он будет исправлен в будущем выпуске. – David

0

Я уверен, что KVC не работает с C-соединениями. См. Обсуждение bbum в Objective-C KVO doesn't work with C unions. Я знаю, что это относится к KVO, но он говорит:

Oooh ... аккуратный. KVO w/union просто не работает. Похоже, что среда выполнения просто не признает, что у класса есть ключ, называемый vectorUnionValue.

Это, скорее всего, ваша проблема. Вероятно, вам нужно будет сделать другое свойство для доступа к ним. Возможно, что-то вроде:

@property(nonatomic) GLKVector3 positionValue; 

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

Мне это не нравится; и может быть лучший подход, но я подозреваю, что вам понадобится что-то в этом направлении. Вы, вероятно, просто не можете попасть в профсоюзы таким образом.


Это встроенный в KVC. Если у вас есть объект с объектным свойством transformation, и этот объект имеет свойство position, тогда путь transformation.position вернет его. Не нужно расстраивать себя.

Но transformation должен быть сам объект, который соответствует кодированию с ключом.Существуют правила для соответствия KVC, и ваше нерегулярное случайное присвоение переменных может нарушить это. Переменные и свойства какао должны быть лидирующими нижними камнями. У вас никогда не должно быть ведущей шапки (это класс), и вам следует избегать внутренних подчеркиваний. KVC использует это соглашение об именах, чтобы позволить ему автоматически находить свойства. Например, он может использовать это, чтобы знать, что установщик для foo равен setFoo: (а не капитализации).

И все промежуточные уровни должны быть объектами. Например, GLKVector3 не является объектом, поэтому вы не можете использовать KVC для опроса его подкомпонентов. Вы должны вернуть весь вектор. Вы, к сожалению, можете только знать, является ли GLKVector3 объектом или нет, консультируясь с файлом заголовка или документами (или, имея интуицию об этом на основе опыта, я бы никогда не ожидал, что это будет объект, потому что это «матовая вещь» с как правило, это структуры, это объединение структур).

+0

Не знал о возможности иметь период в синтаксисе, но для наших целей (которые здесь не показаны) нам нужно повторить. Я обновляю вопрос, чтобы отразить первопричину моей проблемы. – David