【问题标题】:How to inherit from NSDictionary?如何从 NSDictionary 继承?
【发布时间】:2009-07-28 00:19:55
【问题描述】:

我有一个名为 Settings 的对象,它继承自 NSMutableDictionary。当我尝试使用

初始化此对象时
Settings *settings = [[Settings alloc] initWithContentsOfFile: @"someFile"]

它返回一个NSCFDictionary 类型的对象。结果,它无法识别我的其他方法。例如,当我调用选择器“保存”时,它对象:

[NSCFDictionary save]: unrecognized selector sent to instance 0x524bc0

当然,我用花园品种初始化就可以了

Settings *settings = [[Settings alloc] init]

我尝试再次将其投射到“设置”,但没有成功。这看起来很简单——我错过了什么?

谢谢

【问题讨论】:

  • 当我读到第一句话时,我立刻想到,也许你可以使用 NSUserDefaults 而不是创建一个 Settings 类?当然我不知道 NSUserDefaults 是否适合您的需求,但它旨在存储设置,因此可能无需构建您自己的。只是一个想法。
  • 谢谢。这听起来确实像它会做我想要的。这不是我要存储的“默认”值本身,但这可能会很好。

标签: objective-c


【解决方案1】:

NSDictionary 是一个类簇。这意味着从它的 init 方法返回的值并不是严格意义上的 NSDictionary,而是实现实际功能的子类。在几乎所有情况下,最好给你的类一个 NSDictionary 作为实例变量,或者简单地在 NSDictionary 上定义一个类别。

【讨论】:

  • 像这样的其他“类”是(最值得注意的)NSString、NSArray 和 NSSet。我偶尔会遇到引用“NSBigMutableString”的日志语句,这些语句曾经让我感到困惑,直到我了解了类集群。 =)
  • 我同意这一点。最好的可能是使用 has-a 关系,而不是 is-a 关系。 (更喜欢组合而不是子类化)。您可以通过覆盖-setValue:forKey:-valueForKey: 等,让您的班级表现得像字典一样。
【解决方案2】:

Chuck 关于 NSDictionary(以及 Dave,通过扩展,关于 NSArray/Set/String)和类集群是正确的。奇怪的是-[NSDictionary initWithContentsOfFile:] 调用了与-init 不同的初始化程序,这就是为什么它将分配的Settings 实例换成NSMutableDictionary 的另一个子类。 (从文件中读取时的初始化动作可能会选择一个特定的已知子类 NSDictionary,它在从文件中加载等方面表现良好。)

我会赞同 Chuck 的指导,即几乎总是使用组合或类别比继承 NSDictionary 更好。很有可能您可以以更简单的方式完成您对类别所做的工作,并且在此过程中暴露给自己的潜在错误更少。在决定子类之前,请考虑一下自己受到警告。

话虽如此,NSDictionary 和 NSMutableDictionary 都旨在支持子类化,在极少数情况下这样做是正确的。在尝试之前仔细考虑一下。如果您发现它是您设计的正确选择,以下是一些需要了解并根据需要执行的关键点:

  1. 覆盖NSDictionary 中的以下原始方法:
    • -count
    • -objectForKey:
    • -keyEnumerator
    • -initWithObjects:forKeys:count:(指定初始化器)
  2. NSMutableDictionary 重写以下原始方法:
    • -setObject:forKey:
    • -removeObjectForKey:
  3. 如果您支持NSCoding,请注意classForKeyedArchiverreplacementObjectForKeyedArchiver:(都是来自NSObject 的实例方法)——它们可以完全改变您的类的响应方式,并且您经常会无意中从NS(可变)字典。 (您可以通过在它们上设置断点来验证它们是否是罪魁祸首,或者实现它们以调用 super 并中断您自己的代码。)

我已经在我自己的 NSMutableDictionary 子类中实现了其中的一些点。您可以check it out 并使用代码,但可能对您有所帮助。一个对我特别有帮助(并且可能是您的问题的解决方案)的方法是重载指定的初始化程序,该初始化程序目前未记录在案 (Radar #7046209)。

要记住的是,尽管这些项目符号涵盖了最常见的用途,但总有一些边缘情况和不太常见的功能需要考虑。例如,-isEqual:-hash 用于测试相等性等。

【讨论】:

  • 谢谢 - 你已经把我从子类化 NSDictionary 吓得够呛,至少在我更老练之前是这样。所以我要么使用 NSUserDefaults,要么使用组合模式。
【解决方案3】:

如果你真的阅读了 NSDictionary 的规范(我知道这是一个轻率的行为),你会发现一个名为“子类化注释”的部分。在里面你会读到:

如果你确实需要继承 NSDictionary,你需要考虑 由一个类簇表示——因此有几个 方法在概念上基于的原始方法:

initWithObjects:forKeys:

count

objectForKey:

keyEnumerator

在子类中,您必须覆盖所有这些方法。

【讨论】:

    【解决方案4】:

    来自https://stackoverflow.com/a/1191351/467588,这是我为制作 NSDictionary 作品的子类所做的工作。我只是将一个 NSDictionary 声明为我的类的实例变量并添加一些更多必需的方法。不过我不知道该怎么称呼他们。

    我在这里发布了我的代码示例https://stackoverflow.com/a/10993594/467588

    【讨论】:

    【解决方案5】:

    这个问题已经很老了,由于大部分答案都已发布,Apple 引入了object subscripting,它允许您使自己的类的行为更像NSMutableArrayNSMutableDictionary。这比上面讨论的替代方案更简单。

    至少,您必须重写这些方法:

    //Array-style
    - (id)objectAtIndexedSubscript:(NSUInteger)idx;
    - (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;
    
    //Dictionary-style
    - (id)objectForKeyedSubscript:(id <NSCopying>)key;
    - (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
    

    这里有一个很好的 tutorial 说明如何做到这一点。

    【讨论】:

      猜你喜欢
      • 2018-03-10
      • 2016-06-04
      • 2018-04-12
      • 1970-01-01
      • 1970-01-01
      • 2013-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多