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

Objective c 在Cocoa中编写我自己的@dynamic属性,objective-c,cocoa,Objective C,Cocoa,假设(为了论证)我有一个视图类,它包含一个NSDictionary。我想要一大堆属性,所有这些属性都可以访问字典的成员 例如,我想要@property NSString*title和@property NSString*author 对于这些属性中的每一个,实现都是相同的:对于getter,调用[dictionary objectForKey:propertyName],对于setter,对setObject:forKey:执行相同的操作 编写所有这些方法需要大量的时间和大量的复制粘贴代码。是否

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

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

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

编写所有这些方法需要大量的时间和大量的复制粘贴代码。是否有一种方法可以像核心数据使用NSManagedObject子类的@dynamic properties那样自动生成它们?要明确的是,我只希望对我在标题中定义的属性使用这种访问方式,而不仅仅是任何任意键

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

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


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

听起来你应该使用类而不是字典。您即将手动实现该语言试图提供给您的功能。

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

  • 使用@dynamic关键字
  • 覆盖valueForKey:和setValue:forKey:以访问字典
  • 使用objective-c反射API的方法class_getProperty并检查它是否为nil。如果不是nil,你的类就有这样一个属性。如果是的话,那就没有了
  • 然后在不存在此类属性的情况下调用super方法
我希望这有帮助。可能看起来有点骇客(使用反射),但实际上这是一个非常灵活而且绝对“合法”的问题解决方案


注:coredata方法是可能的,但在您的情况下,这完全是过火了…

与宏交朋友?这可能不是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]; }

在仔细阅读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
}

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

有一个很好的博客,其中包含示例代码,在一个非常好的SO回答中对动态属性进行了更可靠的检查


答案有两点。可能想在接口中声明一个@property,以允许typeahead在实现中也将属性声明为动态属性。

是的,字典就是一个人为的例子。实际上,我使用它来创建一个HTML文档的视图,您可以通过绑定连接到该文档。你是对的,如果这就是我所做的一切,那将是一个糟糕的计划:)我想我会在我的解决方案中添加class_getProperty位。谢谢。我要做的唯一调整是只将第一个字母小写(并确保_cmd字符串至少有5个字符):NSString*property=NSStringFromSelector(_cmd);NSString*firstLetter=[[property substringWithRange:NSMakeRange(3,1)]小写字符串];property=[firstLetter stringByAppendingString:[property substringWithRange:NSMakeRange(4,[property length]-5)];很好的例子。我已经得到了一种冻结的IMP(例如,对于
setName:
我的IMP是
setValueForName
),但是你使用
NSStringFromSelector(_cmd)
给了我一种精神上的刺激,我想把它推广到
setWhatever:
。谢谢你写下来。