【问题标题】:How do I properly override a class method in an Objective-C in a subclass?如何在子类中正确覆盖 Objective-C 中的类方法?
【发布时间】:2012-01-29 08:30:23
【问题描述】:

在他的 iOS 编程书的第二章中,Joe Conway 描述了在子类化事件中在类方法中使用“self”。我理解这个概念并对子类化问题有疑问。

背景:我们创建了一个Possession类,其类方法+randomPossession如下所示:

+(id)randomPossession
{
NSArray *randomAdjectiveList = [NSArray arrayWithObjects:@"Fluffy", @"Rusty", @"Shiny", nil];
NSArray *randomNounList = [NSArray arrayWithObjects:@"Bear", @"Spork", @"Mac", nil];

unsigned long adjectiveIndex = rand() % [randomAdjectiveList count];
unsigned long nounIndex = rand() % [randomNounList count];

NSString *randomName = [NSString stringWithFormat:@"%@ %@", [randomAdjectiveList objectAtIndex:adjectiveIndex], [randomNounList objectAtIndex:nounIndex]];

int randomValue = rand() % 100;

NSString *randomSerialNumber = [NSString stringWithFormat:@"%c%c%c%c%c", 
                                '0' + rand() % 10, 
                                'A' + rand() % 10, 
                                '0' + rand() % 10, 
                                'A' + rand() % 10,
                                '0' + rand() % 10];

Possession *newPossession = [[self alloc] initWithPossessionName:randomName valueInDollars:randomValue serialNumber:randomSerialNumber];

return [newPossession autorelease];
}

我知道返回值应该是 id 类型,这样 id newPossession = ...

我将 Possession 子类化并创建了一个名为 BallGlove 的类,其中包括一个新的 iVar、brandName、一个 NSString *

我按如下方式覆盖了 BallGlove 中的 +randomPossession:

+(id)randomPossession
{
BallGlove *myGlove = [super randomPossession];

NSArray *brandNames = [NSArray arrayWithObjects:@"Rawlings", @"Mizuno", @"Wilson", nil];

unsigned long randomNameIndex = rand() % [brandNames count];

[myGlove setBrandName:[brandNames objectAtIndex:randomNameIndex]];

NSLog(@"myGlove is of type class: %@", [self class]);

return myGlove;
}

我的问题是:我重写此类方法的方式是否适当且被社区接受(即通过在变量中捕获超级实现来并行 -init 格式,相应地操作变量然后返回它?我的输出显示返回的对象是 BallGlove 的一个实例,但是我对可接受的实现很感兴趣。提前致谢。

【问题讨论】:

    标签: objective-c class methods overriding


    【解决方案1】:

    技术上没问题。

    不过,我会提出一个替代方案。如果您的基类上已经有一个指定的公共初始化程序(无论如何您可能希望创建并从该工厂类方法调用),然后在子类的类中使用该初始化程序(甚至是您的子类中的新初始化程序)方法。

    代码不多,但在我看来更容易遵循和面向未来。初始化程序有时也可能派上用场,但当然它不是每个应用程序的解决方案。

    【讨论】:

    • 基类中的 +randomPossession 实际上调用了指定的初始化器。类方法还为该初始值设定项创建“随机”值。如果我按照您的建议去做,我将不得不在重写的类方法(+randomPossession)中复制那些“随机”值的代码,并重写指定的初始化程序以调用 BallGlove 类的新初始化程序,这将添加我的其他 iVar 信息。为什么在一般意义上重写我提出的类方法是有害的?不能比作链接初始化器吗?
    • 我不认为它太难看——它只是不是一个很常见的模式。这个问题本身就是一个很好的指标。 :) 子类化有点模糊的一件事是方法的名称,良好的命名是关于objective-c的最好的事情之一。因此,在您的情况下,方法 +randomBallGlove 可能比覆盖更好。
    • 您是在建议我创建一个名为 +randomBallGlove 的类方法,除了所需的自定义实现之外,还包括 +randomPossession 的实现?听起来不错。对于 Possession 的子类仍然可以使用 +randomPossession,您有什么建议?重写该方法并让它抛出一个异常来指示使用+randomBallGlove?这通常是此类事件的正常协议吗?我知道这个例子在现实中可能不存在,我只是在学习,并且很好奇如果它发生时可以接受的实现。感谢您的帮助!
    • 另外,我知道这可能不是一个非常常见的模式,但 Big Nerd Ranch 仅仅承认这样的事件就表明我的问题有一定的有效性。它们不是全部,但了解这些实现细节可以在未来的编码中扩展思想。我真的很感谢您的帮助!
    • 这确实是一个有趣的话题,我认为在这方面没有给开发人员很多指导。抛出异常对我来说是错误的和不自然的。 Obj-C 始终支持相互尊重并协同工作的友好代码块。例如,它并没有竭尽全力禁止调用私有方法。
    【解决方案2】:

    是的,可以这样进行初始化。事实上,在大多数情况下,它就是这样做的。我的意思是,这就是首先从超类继承的原因。除了超类中存在的东西之外,您还想要其他东西。因此,您将代码插入到特定于继承类的任何内容中,并且应该这样做。

    我认为您希望如何初始化 BallGlove 对象也是您定义继承方法的一个因素。问题出现在调用Possession init 或调用BallGlove init(并不是说创建类的实例是使用类方法的唯一地方)。因此,它归结为创建对象的逻辑,即您描述BallGlove 对象的程度如何-您是否确保您的类方法以符合BallGlove 对象标准的方式描述它并且不会成为通用@987654326 @ 目的。我的回答是,如果你能正确实现,使用并行的类方法是可以接受的。

    此外,如果您在超类中返回 Possession 类型也没关系,因为 id 可以指向任何类类型的对象

    【讨论】:

    • 感谢您的回复。然而,这并没有真正回答这个问题。该问题与覆盖类方法的格式有关。除了为子类生成便利方法的必要性之外,我不确定重写类方法的原因。因此,我还没有看到任何关于此的内容。是否有任何示例可以验证我使用的格式是否合适?
    • @BrianPalma 对不起。也许我没有正确回答。我真的不明白为什么以与初始化程序相同的方式对类方法执行此操作是不可接受的。我认为它的工作方式是,类方法倾向于调用 init 方法,执行一些代码并返回你正在做的自动发布版本。我不知道是否有验证您的格式的示例,但我也看不出它可能会出现任何问题。让我检查一些示例代码并回复您。
    【解决方案3】:

    当您覆盖一个类方法时,您可以决定是否借助超级实现来实现它。这完全取决于你。重写 init 是一个完全不同的故事,不仅因为它是一个实例方法,而且因为它有一个与之相关的约定/契约。请记住,对于 example 方法,不应违反 Liskov Subtitution 原则。

    您对类方法的覆盖非常好,尽管我认为覆盖类方法是一种设计气味。尽管在 Objective-C 中很有可能,但在其他语言中却没有,这是有充分理由的。多态性作为一个概念更好地绑定到可以用作彼此替代的实例,而使用类方法会破坏这个概念(即没有真正的替代)。它很聪明,但不一定直观和灵活。

    【讨论】:

    • 感谢您的回复。正如我在下面提到的,我不知道在子类中覆盖特定类方法的任何用途,除了在便利方法中使用更少的代码。在这一点上,我没有这样做的打算。如果出现这种情况,我只是对正确实施感到好奇。
    • @BrianPalma:除了“便利方法”之外,还有其他理由重写类方法。例如,NSView 的 defaultMenurequiresConstraintBasedLayout 类方法的存在只是为了被覆盖。
    • @Chuck 对不起,我想我说错了或者我太新了!我的意思是就模型对象而言的“便利方法”。我知道在 iOS 和 OS X 中有一些类方法纯粹是为了被覆盖并需要 [super someClassMethodHere];
    • @BrianPalma 看看这个答案stackoverflow.com/questions/5222083/…
    • @MadhavanRP 同样,这并不能回答问题。我相信查克已经充分回答了这个问题。事实上,我确实需要超类的实现来正确设置我的子类继承的 iVar 中的变量。问题集中在适当的实施上。谢谢。
    【解决方案4】:

    是的,这是一种非常明智的做法。类方法和普通方法并没有什么特别的区别——只是一个由类执行,另一个由实例执行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-09
      • 1970-01-01
      相关资源
      最近更新 更多