簡體   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