[英]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: 在这里!
我有一个只读属性:
@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_getInstanceVariable
, object_setInstanceVariable
和object_setIvar
方法 。
您可以猜测属性的值存储在实例变量中,该实例变量的名称是使用_
前缀命名的属性。 但是,这只是一个约定。 编译器将约定用于自动综合属性,但编译器不强制执行约定。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.