在Cocoa中编写自己的@dynamic属性

时间:2010-08-24 19:37:46

标签: objective-c cocoa

假设(为了参数)我有一个包含NSDictionary的视图类。我想要一大堆属性,所有属性都可以访问该字典的成员。

例如,我想要@property NSString* title@property NSString* author

对于这些属性中的每一个,实现都是相同的:对于getter,调用[dictionary objectForKey:propertyName];,对于setter,使用setObject:forKey:。

这需要花费大量时间并使用大量的复制粘贴代码来编写所有这些方法。有没有办法自动生成它们,就像Core Data使用NSManagedObject子类的@dynamic属性一样?为了清楚起见,我只希望通过这种方式访问​​我在标题中定义的属性,而不仅仅是任意键。

我遇到过valueForUndefinedKey:作为键值编码的一部分,它可以处理getter,但我不完全确定这是否是最好的方法。

我需要这些是显式属性,所以我可以在Interface Builder中绑定它们:我最终计划为这个视图编写一个IB调色板。

(顺便说一句,我知道我使用NSDictionary存储这些的例子有点人为。我实际上是在编写WebView的子类,属性将引用HTML元素的ID,但这对于逻辑并不重要我的问题!)

5 个答案:

答案 0 :(得分:17)

我在完成了Objective-c运行时文档之后设法解决了这个问题。

我实现了这个类方法:

+ (BOOL) resolveInstanceMethod:(SEL)aSEL
{
    NSString *method = NSStringFromSelector(aSEL);

    if ([method hasPrefix:@"set"])
    {
        class_addMethod([self class], aSEL, (IMP) accessorSetter, "v@:@");
        return YES;
    }
    else
    {
        class_addMethod([self class], aSEL, (IMP) accessorGetter, "@@:");
        return YES;
    }
    return [super resolveInstanceMethod:aSEL];
}

接下来是一对C函数:

NSString* accessorGetter(id self, SEL _cmd)
{
    NSString *method = NSStringFromSelector(_cmd);
    // Return the value of whatever key based on the method name
}

void accessorSetter(id self, SEL _cmd, NSString* newValue)
{
    NSString *method = NSStringFromSelector(_cmd);

    // remove set
    NSString *anID = [[[method stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:@""] lowercaseString] stringByReplacingOccurrencesOfString:@":" withString:@""];

    // Set value of the key anID to newValue
}

由于此代码尝试实现在类上调用但尚未实现的任何方法,因此如果有人尝试调用您需要注意的内容,则会导致问题。我计划添加一些健全性检查,以确保名称符合我的预期。

答案 1 :(得分:2)

您可以混合使用建议的选项:

  • 使用@dynamic关键字
  • 覆盖valueForKey:和setValue:forKey:访问字典
  • 使用objective-c reflection API的方法class_getProperty并检查它是否为nil。如果不是零你的班级有这样的财产。如果是的话就不会这样。
  • 然后在没有这样的属性的情况下调用super方法。

我希望这会有所帮助。可能看起来有点hacky(使用反射),但实际上这是一个非常灵活,也绝对“合法”的解决方案...

PS:coredata的方式是可能的,但在你的情况下会完全矫枉过正......

答案 2 :(得分:2)

与宏交朋友?这可能不是100%正确。

#define propertyForKey(key, type) \
    - (void) set##key: (type) key; \
    - (type) key;

#define synthesizeForKey(key, type) \
    - (void) set##key: (type) key \
    { \
         [dictionary setObject];// or whatever \
    } \
    - (type) key { return [dictionary objectForKey: key]; }

答案 3 :(得分:0)

听起来你应该使用类而不是字典。你正在接近手工执行语言试图给你的东西。

答案 4 :(得分:0)

有一个很好的博客,其示例代码在https://tobias-kraentzer.de/2013/05/15/dynamic-properties-in-objective-c/处对动态属性进行了更强大的检查,这也是Objective-C dynamic properties at runtime?上非常好的答案。

答案上有几点意见。可能希望在接口中声明@property以允许typeahead也在实现中将属性声明为动态。