为什么 return id 来自 sharedInstance

Why return id from sharedInstance

sharedInstance模式的许多例子return一个id:

+ (id)sharedInstance
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

而不是 class 实例:

+ (MyClass*)sharedInstance
{
    static MyClass *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

这是为什么?一个一定比另一个好吗?

是的,从设计和可维护性的角度来看,returning idinstancetype 或特定的 classes 之间存在重大差异。

id: 最老的。除非在 +alloc-init 系列的方法中使用它,否则类型推断将被放弃,并且假定 returned 对象具有任何类型。它通常由编译器隐式转换为您在声明中碰巧使用的任何具体类型。 init-family-style 推理可以通过 class 方法通过在方法名称前加上 allocnew 前缀来恢复,在实例方法中通过在方法名称前加上 autoreleaseinitretainself.

instancetype:是一个新手,它向具有 "related" return 类型的方法授予 -init-family-style 类型推断(与 class 他们仅限于)。它放宽了推理命名限制 id 通过将这些语义授予任何标记为 return 类型的方法。通常,它比 id 更可取,因为您可以恢复更多安全性(或至少获得更好的警告)关于不包含在 id 限制中的某些方法的不正确使用。

A class:仅当您绝对确定您出售的实例不会具有特定子类型或希望隐藏它具有的事实时才应使用。它对于像 UIColor 这样的 class 簇或不应该有像 UIPasteboard 这样的子类型的具体 classes 很有用。因此,它通常用作从特定 class 继承的标记是不安全的。

tl;dr 如果您想阻止 subclassing 那么请具体说明,否则 instancetype.

CodaFi 清楚地阐明了 (+1) 基本 id vs instancetype vs 显式 return 类型问题。

但我认为值得注意的是单例的 sharedInstance 方法给讨论增加了一个独特的皱纹: id and/or instancetype 的整个想法是subclass 更容易。但这在单例构造方法的上下文中并没有多大意义。

假设您有一些代码库,其中有一些 sharedInstance 用于某些代码库 class 的单例方法。假设在稍后的某个时间点,您决定 subclass this class,但忽略了为 subclass 实现新的 sharedInstance。因此,您将继承相同的基础 class 实现,只有一个 sharedInstance 静态。如果您使用 instancetype,编译器甚至会让您相信 [SubClass sharedInstance] 将 return 是子 class.

的共享实例

问题是,如果您在子class 上调用sharedInstance,您真的无法确定您将获得哪种类型的对象。如果您不小心有一些旧代码在基础 class 上调用它,那么从 [SubClass sharedInstance] 中编辑的对象 return 可能会混淆地成为基础 class 的实例!但是,如果您足够幸运,记得在项目的其他地方删除了对 base class sharedInstance 方法的任何引用,您将正确地收到 subclass 的实例。显然,这不是理想的情况,因为此继承方法的行为可能会根据代码中其他地方首次调用它的方式而改变。这与单身人士的概念完全相反。

底线,虽然通常建议使用继承方法 returning instancetype(或者在过去,id)使 classes 可扩展,但在对于单身人士的情况,我通常不会就 sharedInstance 方法提出建议。它错误地暗示了在这种情况下并不完全适用的可扩展性。

相反,我建议 (a) 使用 sharedInstance 方法,您拥有 return 显式数据类型; (b) 如果你 subclass 代码,在 subclass 上实现一个新的 sharedInstance。这消除了歧义。