Objective C - 在实现工厂模式时,init方法是否是一个不好的选择?

3
我实现了旧的工厂模式,但在某些特定情况下(但并非所有情况!),分析器会发出关于内存泄漏的警告。实际上,查看Cocoa Memory Management Policy规则表明,alloc而不是init可以返回+1-retain-count对象。
因此,似乎:
1. 释放self并从init返回一个新对象,严格来说,违反了规则。 2. 因为alloc/init的串联性质,在互联网上有许多地方推广这种技术,所以这确实起作用。 3. 分析器有时会抱怨,有时则不会。
那么...我们一直都做错了吗?

3
你能否发布一个导致编译器错误的代码片段?正如你所指出的那样,这是一种常见的模式,也是大多数类簇实现的方式。此外,在init可能失败的情况下,处理失败的pre-arc方法是释放self并返回nil。 - gaige
类簇基于抽象工厂设计模式。Objective-C编程概念:类簇 - vikingosegundo
3个回答

2

您可以像这样实现init,这应该会释放self以平衡从alloc调用中的保留计数。

- (id)initWithSomething:(id)something
{
    [self release]; // don't need this line for ARC
    self = nil;
    return [[PrivateSubClass alloc] initWithSomething:something];
}

在IT技术中,很多时候都会采用init作为工厂方法。例如NSArrayNSDictionaryNSString


0

就像gaige说的那样,如果你贴上一段代码而不是解释的话,会更加清晰明了。

不过无论如何,你可以将你的工厂方法移动到类方法中,这样你就完全不会遇到这样的问题。我的意思是像这样:

MyClass* instance = [MyClass instanceWithParameters:params];

@interface MyClass
+ (MyClass*) instanceWithParameters:(ParamType)params;
@end

1
"static" 应该被重命名为 "class"。 - Lorenzo B
如果我理解 OP 正确的话,你的代码应该返回 MyClass *。并且它的名称必须以 newalloc 开头,这样 ARC 就知道它返回一个具有 +1 引用计数的对象。 - Alexei Sholik
而且使用 MyClass* 会更好 ;) - Lorenzo B
3
类工厂方法不以new或alloc开头,通常以它们所属的类名开头。例如,在NSString中的stringWithFormat: 或NSNumber中的numberWithBool:。 - e1985
是的,在使用C#取代Objective-C几个月后,我忘记添加*了。 - Morion

0

不知道是哪段代码导致了分析器的行为,很难说,但通常来说,以下是一些编译器友好的定义init/工厂方法的方式。

经典的alloc/init

- (instancetype)initWithParameter:(id)parameter {
    if(self = [super init]) {
       _parameter = parameter; 
    }
    return self;
}

使用方法

MyCustomClass * myInstance = [[MyCustomClass alloc] initWithParameter:foo];

这将产生一个保留计数为+1的实例。在ARC下,由于它遵循NARC规则(New,Alloc,Retain,Copy),它将被自动管理,因此可以正确地进行处理。

出于同样的原因,在ARC之前的环境中,客户端必须明确释放它。

自定义工厂方法

ARC

+ (instancetype)canIHazInstanceWithParameter:(id)parameter {
    return [[self alloc] initWithParameter:parameter]; // assuming -initWithParameter: defined
}

Pre-ARC

+ (instancetype)canIHazInstanceWithParameter:(id)parameter {
    return [[[self alloc] initWithParameter:parameter] autorelease]; // assuming -initWithParameter: defined
}

使用方法

MyCustomClass * myInstance = [MyCustomClass canIHazInstanceWithParameter:foo];

在 ARC 和 pre-ARC 中,该方法返回一个自动释放的实例(在 pre-ARC 实现中这更明确),无需由客户端管理。

备注

  • 您可能已经注意到了 instancetype 关键字。这是 Clang 引入的一个方便的语言扩展,可将编译器变成实现自己的构造函数/工厂方法时的好朋友。我曾经写过一篇文章讨论这个问题,它可能与您有关。

  • 工厂方法是否比 init 方法更可取是有争议的。从客户端的角度来看,在 ARC 下并没有太大区别,只要您仔细遵循命名约定,即使我个人倾向于在接口中公开工厂方法,而仅在内部实现自定义 init 方法(如上面的示例所示)。这更多是风格问题,而不是实际的实用问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接