【问题标题】:Objective C class inheritance with factory methods带有工厂方法的Objective C类继承
【发布时间】:2013-06-13 05:46:23
【问题描述】:

我想从具有工厂方法的框架类继承。如何让工厂方法返回我继承的类类型的对象?我发现this useful article 描述了类似的情况,但在他们的情况下,您可以控制超类。例如,我怎么能写出UIImage 的子类,而imageNamed: 会返回我的子类类型的对象?

【问题讨论】:

  • 也许你可以通过在UIImage上写一个类别来实现你想要的?
  • 是的,我现在正在考虑这个方向。只是我需要一些额外的类成员(属性),并希望避免 objc_setAssociatedObject 和朋友的麻烦。还因为我觉得为完全不同的问题创建这种类型的解决方法并不优雅。但是我越来越害怕我别无选择。
  • 看看这个NSObject 类别。 setRuntimeProperty:name:runtimeProperty: 这两种方法有助于方便地设置和访问关联对象。

标签: iphone objective-c oop inheritance


【解决方案1】:

我想从具有工厂方法的框架类继承。如何让工厂方法返回我继承的类类型的对象?

这是你应该要做的所有事情:

@interface MONImage : UIImage
@end

@implementation MONImage
@end

然后:

MONImage * image = [MONImage imageNamed:name];

例如,我如何编写一个 UIImage 的子类,它 imageNamed: 会返回我的子类类型的对象?

+[UIImage imageNamed:] 的实现从这种方法中写出了子类。因此,您需要自己实现此方法。

这是一个应该声明工厂方法的方式:

+ (instancetype)imageNamed:(NSString *)pName;

以及一个应该如何实现它:

+ (instancetype)imageNamed:(NSString *)pName
{
  MONImage * image = [[self alloc] initWithThisDesignatedInitializer:pName];
                       ^^^^ NOTE: self, not a concrete class
  ...set up image...
  return image;
}

但他们没有那样做——+[UIImage imageNamed:] 写出子类并在你写MONImage * img = [MONImage imageNamed:pName]; 时返回UIImage。有时这样做是有充分理由的。有些方法应该具有“最终”语义。当您的方法可能返回多种类型时,这通常会出现,例如在类集群中。该语言没有表达“最终”方法——但至少应该记录这种方法。


所以来看看这个UIImage 案例:

@interface MONImage : UIImage

+ (instancetype)imageNamed:(NSString *)pName;

@end

@implementation MONImage

+ (instancetype)imageNamed:(NSString *)pName
{
    UIImage * source = [UIImage imageNamed:pName];
    CGImageRef cgImage = source.CGImage;
    if (cgImage)
        return [[self alloc] initWithCGImage:cgImage];
    // try it another way
    return nil;
}

@end

请注意,UIImages 和 CGImages 是不可变的。这不应导致图像数据的深层复制。

【讨论】:

  • 这是否也提供[UIImage imageNamed:]的缓存行为?如果没有,也许您应该在MONImage 中存储对原始图像的引用。但非常好的解决方案。
  • @JonathanCichon 它将保留+imageNamed: 的查找行为。它会加载图像,然后缓存它。缓存实现的确切行为是抽象的,所以我的结论是在这方面不能保证它是相同的。我假设在缓存方面也是一样的,但我还没有证明这一点。好问题。
【解决方案2】:

你的例子:

  • 子类 UIImage 到,比如说,MyImage
  • 实现imageNamed: 方法以执行您需要完成的任何特定操作。
  • 在该类上调用该方法:MyImage *newImage = [MyImage imageNamed:imageName];

【讨论】:

  • 这不会有什么帮助,因为你不能在你自己的imageNamed实现中调用super,因为这最终会返回一个UIImage类型的实例。
  • 谁说他需要打电话给super?他可以在方法中分配和初始化他的子类的一个对象并返回它。而且,众所周知,约定是子类调用其父类的指定初始化程序,它返回一个id 类型的对象。无论如何,这是一个基于 OP 要求的人为示例。细节留给用户,但便利构造函数分配和初始化它自己的类的对象的原则是合理的。
  • 是的,但在这种情况下,imageNamed: 中的“魔法”苹果非常有用,我认为他不想自己实现这一点。但你是对的,你的答案不是“错误”,但我认为在这种情况下不是很有用。
  • 我的特殊问题是我不想提供 imageNamed 的替代实现,这对我来说很好,因为它会返回一个 MyImage 实例。并且 UIImage 没有类似于 imageNamed: 的 init... 方法,它也考虑图像缓存,所以我什至无法覆盖它以保持相同的功能 - 正如乔纳森所说。
【解决方案3】:

解决我的问题的方法是使用类别而不是继承(在我的问题的 cmets 中感谢 Jonathan Cichon)。我使用Associative References 在类别实现中声明和存储附加数据,正如在 SO 中讨论的那样。我想引起人们对 Jonathan 提出的 NSObject category implementation 的关注,它可以非常轻松优雅地为任何对象添加关联引用。

【讨论】:

  • 虽然我很高兴您解决了您的问题 - 您的问题是关于覆盖便利构造函数,然后您将其更改为关于向类扩展之外的类添加存储的问题。所以实际上有两组问题和答案同时进行。
  • 我需要用额外的数据扩展原来的框架类,并且仍然能够使用它原来的便利构造函数。当我问最初的问题时,我不知道如何做到这一点,并认为它可以通过继承来实现。从我没有明确说明我需要向类中添加额外数据的角度来看,你是对的,而是写了“继承”。另一方面,即使不使用继承,乔纳森的解决方案也完美地解决了我的问题。
  • 注:我没有更改原始问题,并在此答案中添加了 assoc.refs 的提示,仅供未来读者参考。
  • 我只是指出,您所要求的和您真正想知道的是两件不同的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-08
  • 1970-01-01
  • 2020-10-25
  • 1970-01-01
  • 1970-01-01
  • 2019-04-03
  • 1970-01-01
相关资源
最近更新 更多