【问题标题】:Creating a convenience method for a subclassed SKNode object for all iOS versions为所有 iOS 版本的子类 SKNode 对象创建便捷方法
【发布时间】:2014-10-27 20:42:13
【问题描述】:

我正在使用下面的代码为子类 SKNode 对象创建一个方便的方法。这似乎工作正常。让我烦恼的是,SKNode 类参考明确指出 -init 符号仅适用于 iOS 8.0 及更高版本。我的应用程序的部署目标是 iOS 7.1。

+ (instancetype)postGameContent
{
    return [[self alloc] init];
}

- (id)init
{
    if (self = [super init]) {

        [self setupContent];

    } return self;
}

我担心我的代码中的 init 方法被转移到 iOS7.1 的继承链上,绕过了由 SKNode 自己的便利方法(+节点)或初始化程序(-init)初始化的任何内容。根据 iOS 版本号,我的代码是否有可能导致节点(甚至对象)有所不同?与此相关,我写的时候:

return [[self alloc] init];

Xcode 6 自动补全给“init”一个红色删除线,但没有解释原因 (screenshot)。这是因为潜在的版本问题,还是其他原因?

【问题讨论】:

  • 必须有一条带有红色删除线的消息。你也能发一下吗?我认为它与 init 方法无关,而可能是对 self 的引用。
  • 谢谢,我提供了截图:imgur.com/nGFEJIl
  • 我非常怀疑这会带来什么问题,很可能文档是不正确的。
  • 是的,也许这让我感到不快。该代码按预期工作。不过,有没有办法找出来?

标签: objective-c sprite-kit xcode6 sknode


【解决方案1】:

您可以在便捷方法本身中执行所需的操作,而不是重写 init 方法。

+ (instancetype)postGameContent
{
    MyNode* node = [super node]; //Create node from superclass, i.e. SKNode
    [node setupContent]
    return node;
}

这实际上使它在版本兼容性方面更加安全。

【讨论】:

  • 也许我没有正确理解 Apple 文档。鉴于 Objective-C 类也是对象(第 22 页),并且“在类工厂方法中使用 self 意味着您指的是类本身”(第 42 页)而不是实例;使用 [[self alloc] init] 使得子类可以创建正确的实例而不覆盖方法。 (来源:用 Objective-C 编程,Apple 开发人员)
  • 嗯...你是对的,我自己可能并不完全清楚。但是,我发现了一篇可能有帮助的文章:mintknow.biz/zener/2014/09/17/…
  • 谢谢,但链接让我离开了我目前的位置。据我了解,关于 SKNode 是 NSObject 的子类也是不正确的。 UIResponder 是 SKNodes 的超类,继承自 NSObject。但是,再次感谢您为此竭尽全力。
  • 这实际上是我最初的做法,但这不是很沮丧吗?我知道这也有效,但就良好的代码设计而言,应该重新考虑。我也在考虑不要子类化 SKNode,而是创建一个类来创建对象并为场景处理它们。
  • 上面的代码基本上就是这样了。你可以创建一个工厂类,但即使这样也需要从某些东西中继承,那么为什么不直接使用 SKNode 呢?
【解决方案2】:

这是我发现的:

  • 类方法 [SKNode node] 调用 init,这使得上述代码在潜在的绕过和缺少正确的节点初始化方面是安全的。
  • 当涉及到跨 iOS 版本的“节点一致性”时,这并没有多大帮助,因为 SKNode 类在 iOS 8 中已经进行了重大修订。

我是这样发现的:

如果 -init 符号仅在 iOS 8 版本中可用,这意味着 SKNode 的类工厂方法调用了另一个初始化器而不是 init,至少对于 iOS 7 而言。为了测试这一点,我将 alloc init 替换为 node 中的方便的方法,并在 init 方法中添加了一条日志消息:

+ (instancetype)postGameContent
{
    return [self node];
}

- (id)init
{
    if (self = [super init]) {

        NSLog(@"init called!");

    } return self;
}

现在,如果节点方法调用 init,我的子类初始化程序会告诉我,并且它适用于 iOS 8 和 iOS 7。在我看来,这使得类引用中显式的 iOS 8 特定 - init 符号变得多余。如果我错了,请纠正我。

除了新的初始化器、属性和方法之外,iOS 8 SKNode 类还改变了 hash 和 isEqual 方法的实现。这导致了各种奇怪的行为,包括collectionsphysics。这意味着无论哪种方式,您的 iOS 7 节点在 iOS 8 中的行为可能会有所不同。

为什么是红色删除线,我不知道。我有一个关于 Apple DTS 的请求,如果我听到任何相关信息,我会反馈给我。

更新:我一直与加利福尼亚的圣教会保持联系。混乱是基于我的误解。我假设可以——无论是技术上还是遵守 API 合同——使用 alloc] init] 进程初始化任何对象,例如:

SKNode *node = [[SKNode alloc ] init];

这个假设是错误的。应始终使用框架参考中指定的初始化程序。这意味着对于 iOS 7.1 及更低版本,只有一种方法可以初始化 SKNode 对象,即使用 +node 符号。使用 init 初始化 SKNode 在技术上是可行的并不重要,即使我们知道 init 在 iOS 7 的 SKNode 类中被覆盖。它违反了 API 契约。

【讨论】:

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