【问题标题】:Objective C - is init a bad place to implement a factory?目标 C - init 是实现工厂的坏地方吗?
【发布时间】:2013-06-09 13:02:37
【问题描述】:

我实现了旧的 init-as-a-factory 模式,但在一种特殊情况下(但不是其他情况!)我从分析器收到有关内存泄漏的警告。事实上,查看Cocoa Memory Management Policy 规则,它是alloc,而不是init,它可以返回+1-retain-count 对象。

看来:

  1. 严格来说,释放self 并从init 返回一个新对象是违反规则的。
  2. 互联网上的许多地方都在推广这种技术,并且由于 alloc/init 的串联性质,这确实有效。
  3. 分析器有时会抱怨这一点,有时则不会。

那么……我们一直都做错了吗?

【问题讨论】:

  • 你能发布一个导致编译器错误的sn-p吗?正如您所指出的,这是一种常见的模式,也是大多数类集群的实现方式。此外,在 init 可能失败的情况下,处理失败的弧前方法是释放 self 并返回 nil。
  • 类集群基于抽象工厂设计模式。 Concepts in Objective-C Programming: Class Clusters

标签: objective-c


【解决方案1】:

你可以像这样实现init,它应该释放self以平衡来自alloc调用的保留计数。

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

如果经常将init 实现为工厂方法。例如NSArrayNSDictionaryNSString

【讨论】:

    【解决方案2】:

    正如gaige所说,如果你发布一段代码而不是解释会更清楚。

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

    MyClass* instance = [MyClass instanceWithParameters:params];
    
    @interface MyClass
    + (MyClass*) instanceWithParameters:(ParamType)params;
    @end
    

    【讨论】:

    • static 应重命名为 class
    • 如果我正确理解了 OP,您的代码应该返回 MyClass *。并且它的名称必须以newalloc 开头,以便ARC 知道它返回一个保留计数为+1 的对象。
    • MyClass* 会更好;)
    • 类工厂方法不以 new 或 alloc 开头。他们通常以他们的班级名称开头。例如 NSString 中的 stringWithFormat: 或 NSNumber 中的 numberWithBool:。
    • 是的,在使用 c# 而不是 Objective-c 几个月后忘记添加 *
    【解决方案3】:

    在不知道导致分析器行为的代码是什么的情况下,很难判断,但作为一般规则,这里有几种编译器友好的方式来定义 init/factory 方法。

    经典分配/初始化

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

    用法

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

    这将产生一个具有 +1 保留计数的实例。在 ARC 下,这将自动得到妥善管理,因为它遵循 NARC 规则(新建、分配、保留、复制)。 出于同样的原因,在 ARC 之前的环境中,它必须由客户端显式发布。

    自定义工厂方法

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

    弧前

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

    用法

    MyCustomClass * myInstance = [MyCustomClass canIHazInstanceWithParameter:foo];
    

    在 ARC 和 pre-ARC 中,该方法都返回一个自动释放的实例(这在 ARC 之前的实现中显然更加明确),它不必由客户端管理。

    备注

    • 您可能已经注意到 instancetype 关键字。这是 Clang 引入的一个方便的language extension,它在实现你自己的构造函数/工厂方法时将编译器变成了亲爱的朋友。我在这个主题上写了an article,这可能与你有关。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多