繁体   English   中英

在Objective-C中的运行时动态设置未知的只读属性

[英]Dynamically setting unknown readonly property at runtime in Objective-C

我正在构建一个类,该类在运行时从plist动态设置子类的属性,其工作方式如下:

属性列表编辑器屏幕截图

您可以在子类中声明属性以匹配键的名称:

#import "PlistModel.h"

@interface CustomModel : PlistModel

@property (strong, nonatomic) NSString * StringPropertyKey;
@property (strong, nonatomic) NSDate * DatePropertyKey;
@property (strong, nonatomic) NSArray * ArrayPropertyKey;
@property (strong, nonatomic) NSDictionary * DictionaryPropertyKey;

@property int IntPropertyKey;
@property BOOL BoolPropertyKey;
@property float FloatPropertyKey;

@end

而已! 这些值将在运行时自动填充,而无需任何其他代码:

[CustomModel plistNamed:@"CustomModel" inBackgroundWithBlock:^(PlistModel *plistModel) {

    CustomModel * customModel = (CustomModel *)plistModel;

    NSLog(@"StringProperty: %@", customModel.StringPropertyKey);
    NSLog(@"DateProperty: %@", customModel.DatePropertyKey);
    NSLog(@"ArrayProperty: %@", customModel.ArrayPropertyKey);
    NSLog(@"DictionaryProperty: %@", customModel.DictionaryPropertyKey);
    NSLog(@"IntProperty: %i", customModel.IntPropertyKey);
    NSLog(@"BoolProperty: %@", customModel.BoolPropertyKey ? @"YES" : @"NO");
    NSLog(@"FloatProperty: %f", customModel.FloatPropertyKey);

}];

问题

我在运行时设置属性,方法是生成选择器,并使用要设置的值调用它,如下所示:

SEL propertySetterSelector = NSSelectorFromString(@"set<#PropertyName#>:"); 
void (*func)(id, SEL, id) = (void *)imp;
func(self, propertySetterSelector, objectToSet);

但是,如果由于某种原因属性是readonly则选择器将不存在,因此我正在寻找替代方法。 我在这里找到了一种识别属性为只读的方法:

- (NSMutableArray *) getPropertyNames {

    // Prepare Package
    NSMutableArray * propertyNames = [NSMutableArray array];

    // Fetch Properties
    unsigned count;
    objc_property_t *properties = class_copyPropertyList([self class], &count);

    // Parse Out Properties
    for (int i = 0; i < count; i++) {
        objc_property_t property = properties[i];
        const char * name = property_getName(property);
        // NSLog(@"Name: %s", name);
        const char * attributes = property_getAttributes(property);
        NSLog(@"Attributes: %s", attributes);
        NSString * attributeString = [NSString stringWithUTF8String:attributes];
        NSArray * attributesArray = [attributeString componentsSeparatedByString:@","];
        if ([attributesArray containsObject:@"R"]) {
            // is ReadOnly
            NSLog(@"%s is read only", name);

            // -- CAN I SET THE PROPERTY HERE? -- //
            // property = @"Set"; ?
        }
        // Add to our array
        [propertyNames addObject:[NSString stringWithUTF8String:name]];
    }

    // Free our properties
    free(properties);

    // Send it off
    return propertyNames;
}

也许如果有一种方法可以直接设置objc_property_t ref。

更新资料

通过评论,我意识到有些困惑。 我认为我的问题的核心是,除了像我正在做的那样调用选择器之外,是否有可能在运行时设置未知属性。

资源资源

完整项目: 在这里!

提示此问题的CodeReview Post: 在这里!

SetValue:forKey:更新

我有一个只读属性:

@property (readonly) NSString * readOnlyProperty;

我在课堂上声明:

+ (BOOL) accessInstanceVariablesDirectly {
    return YES;
}

我称之为:

[self setValue:@"HI" forKey:[NSString stringWithUTF8String:name]];

// -- OR -- // 

[self setValue:@"HI" forKey:[NSString stringWithFormat:@"_%@",[NSString stringWithUTF8String:name]]];

无论哪种方式,值仍然为null。

更新

如果目标类已将类方法accessInstanceVariablesDirectly定义为返回YES ,并且该属性将其值存储在常规命名的实例变量中, 可以尝试将setValue:forKey:用于readonly属性。 请参阅键值编码编程指南 》中的“ setValue:forKey:的默认搜索模式 如有必要,KVC会将原始值拆箱。

原版的

除非通过调用属性的setter,否则无法设置属性 ,因为将属性定义为getter和(可选)setter。 您可以使用键-值编码(KVC)的setValue:forKey:方法,这比自己构造设置器名称更简单,更可靠,但是在隐藏之下仍然调用该属性的设置器。

可以使用Objective-C运行时设置实例变量 查看class_getInstanceVariableobject_setInstanceVariableobject_setIvar方法

您可以猜测属性的值存储在实例变量中,该实例变量的名称是使用_前缀命名的属性。 但是,这只是一个约定。 编译器将约定用于自动综合属性,但编译器不强制执行约定。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM