【问题标题】:Swift native base class or NSObjectSwift 本机基类或 NSObject
【发布时间】:2014-07-26 06:56:25
【问题描述】:

我用 Swift 测试了一些isa swizzling,发现它只在 NSObject 是超类(直接或更高级)或使用“@objc”装饰时才有效。否则它将遵循静态和 vtable-dispatch 样式,如 C++。

在没有 Cocoa/NSObject 基类的情况下定义 Swift 类是否正常?如果我担心这意味着放弃了 Objective-C 的大部分动态,例如方法拦截和运行时自省。

动态运行时行为是属性观察器、核心数据、Aspect Oriented ProgrammingHigher Order Messaging、分析和日志框架等功能的核心。

使用 Objective-C 的方法调用风格会在方法调用中添加大约 20 个机器代码操作数,因此在某些情况下(对具有小主体的方法的许多紧密调用)C++ 风格的静态和 vtable 调度可以表现更好。

但是考虑到一般的 95-5 规则(95% 的性能提升来自调整 5% 的代码),从强大的动态特性开始并强化 where 是否有意义?必要的?

【问题讨论】:

标签: objective-c swift objective-c-runtime


【解决方案1】:

我还发现,如果基于 NSObject 的 Swift 类,我会看到一些可能隐藏编码错误的意外运行时行为。这是一个例子。

在这个示例中,我们基于 NSObject,编译器正确地发现了 testIncorrect_CompilerShouldSpot 中的错误, 报告“...'MyClass' 不能转换为 'MirrorDisposition'”

class MyClass {
  let mString = "Test"

  func getAsString() -> String {
    return mString
  }

  func testIncorrect_CompilerShouldSpot() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject == myString) {
        // Do something
      }
  }

  func testCorrect_CorrectlyWritten() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject.getAsString() == myString) {
        // Do something
      }
  }
}

在这个示例中,我们基于 NSObject,编译器没有发现 testIncorrect_CompilerShouldSpot 中的错误:

class myClass : NSObject {
  let mString = "Test"

  func getAsString() -> String {
    return mString
  }

  func testIncorrect_CompilerShouldSpot() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject == myString) {
        // Do something
      }
  }

  func testCorrect_CorrectlyWritten() {
    var myString = "Compare to me"
      var myObject = MyClass()
      if (myObject.getAsString() == myString) {
        // Do something
      }
  }
}

我想道德是,仅基于您真正需要的 NSObject!

【讨论】:

    【解决方案2】:

    作为 NSObject 子类的 Swift 类:

    • 本身就是 Objective-C 类
    • 使用objc_msgSend() 调用(大部分)他们的方法
    • 为(大部分)方法实现提供 Objective-C 运行时元数据

    不是 NSObject 子类的 Swift 类:

    • 是 Objective-C 类,但只实现了少数 NSObject 兼容性的方法
    • 不要使用objc_msgSend() 调用他们的方法(默认)
    • 不为其方法实现提供 Objective-C 运行时元数据(默认情况下)

    在 Swift 中子类化 NSObject 可以为您提供 Objective-C 运行时的灵活性,同时也可以提高 Objective-C 的性能。如果您不需要 Objective-C 的灵活性,那么避免使用 NSObject 可以提高性能。

    编辑:

    在 Xcode 6 beta 6 中,动态属性出现了。这允许我们指示 Swift 方法应该使用动态调度,因此将支持拦截。

    public dynamic func foobar() -> AnyObject {
    }
    

    【讨论】:

    • Swift 使用什么样的调度来代替 objc_msgSend?是静态的吗?
    • Swift 可以使用 objc_msgSend 调度、虚拟表调度、直接调度或内联。
    • 静态:小于 1.1ns。 vtable 1.1ns,msgSend 4.9ns。 . (当然取决于硬件)。 .看起来“纯粹”的 Swift 为系统级编程提供了一种很棒的语言,但是对于应用程序,我不愿意放弃动态功能,尽管如果它们像垃圾收集与 ARC 一样迁移到编译器,我会很高兴。 . .我听说静态分派可以在多核系统上实现更好的分支预测(以及性能)。是吗?
    • “不是 NSObject 子类的 Swift 类是 Objective-C 类” - 你能提供一个链接到你发现的地方吗?
    • 所以总而言之,如果我需要与 Objective C 代码通信,我应该只在 Swift 中子类化 NSObject?
    【解决方案3】:

    根据语言参考,类不需要子类化任何标准根类,因此您可以根据需要包含或省略超类。

    请注意,从类声明中省略超类不会分配任何类型的隐式基超类。它定义了一个基类,它将有效地成为独立类层次结构的根。

    来自语言参考:

    Swift 类不继承自通用基类。给你上课 在不指定超类的情况下定义自动成为基类 供您构建的类。

    尝试从没有超类(即基类)的类中引用 super 将导致编译时错误

    'super' members cannot be referenced in a root class
    

    【讨论】:

    • 是的。如果您尝试在项目上下文中创建 Cocoa 或 Cocoa Touch 源文件,则插入子类的表单实际上会阻止您在创建类时将其留空。创建超类后,您可以稍后将其删除。
    • 谢谢。使用静态和 vtable 调度对系统编程或性能调整很有意义。为了与原生 Cocoa 保持一致,我认为动态 应该 是默认设置。 (除非有人能说服我否则,所以这个问题)。
    【解决方案4】:

    以下内容摘自 Apple 的 Swift-eBook,并为您的问题提供了适当的答案:

    定义基类

    任何不继承自另一个类的类都称为基类。

    Swift 类不继承自通用基类。您在未指定超类的情况下定义的类会自动成为您构建的基类。


    参考

    https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_251

    【讨论】:

      【解决方案5】:

      我相信绝大多数 Swift 数据不会是objc。只有那些确实需要与目标 C 基础设施通信的部分才会被明确标记为这样。

      我不知道将在何种程度上将运行时内省添加到语言中。只有在方法明确允许的情况下,方法拦截才可能成为可能。这是我的猜测,但只有 Apple 内部的语言设计人员才真正知道他们的真正目标是什么。

      【讨论】:

      • 对我来说,将动态设置为默认值是有意义的(通过扩展 NSObject,使用 '@objc' 装饰或其他方法)。似乎当没有基类时,Swift 会偏爱静态/vtable 调度,它的性能更好,但在大多数情况下,这不是必需的。 . (90% 的收益来自调整 10% 的代码)。我希望与 Objective-C 的活力保持一致的是选择退出而不是选择加入。
      • 语言的设计方式是可选的。据我们所知,未来很可能会添加非原生 objc 的系统 API。在中/长期,他们甚至可以将现有库迁移到非 objc 代码,并在 objc 中使用薄的兼容层调用非 objc 代码。我们只是不知道。几年后我们可能会制作educated guesses。但目前这只是一个很大的问号。
      • @JasperBlues 我认为大多数时候不需要动态,我宁愿在默认情况下获得性能,然后选择需要动态行为。我猜每个人都有自己的:)
      • @Lance 嗯。可能是。我还是关心:观察者、AOP、测试模拟框架、分析框架等。而关于性能的问题是 90% 的收益来自调整 10%。 .所以这就是我的理由——选择加入那些 10% 的案例。我认为 AOP 对于 iOS 企业应用程序来说将是一件大事,但它可以使用 C++ 中的编译器工具来完成。 .当然,游戏、科学计算、图形等开发人员会欢迎更高的性能:)
      【解决方案6】:

      这很正常。看看 Swift 的设计目标:目标是让大量的编程问题消失。方法调配可能不是你想用 Swift 做的事情之一。

      【讨论】:

      • Method swizzling 是一种在运行时实现拦截模式的方法。其用途之一是根据面向方面编程的原则应用横切关注点。 Apple 自己的通知、属性观察器(swift 内置)、核心数据都使用 swizzling 来获得良好的效果。 .尽管 Objective-C 语法笨拙,但让人们迷恋的一件事就是这种活力,它使得在需要拦截模式时提供优雅的解决方案变得很容易。 .实际上Objc并没有正式的AOP框架,因为原材料太好了。
      • 现在这样做并不意味着明天也会这样做。正如你所说的,通知、属性观察者、代表和类似的东西是或者可能是本地提供的。语言会发展,我们不知道朝哪个方向发展。在对函数式编程的支持方面,它获得了很多。但它以一种不同寻常的方式做到这一点,所以我不知道丢失了多少。然而,据我们所知,它们可能正朝着阻止突变和鼓励纯函数式编程的方向发展。如果 UI 的未来是响应式的怎么办?还没有办法知道。
      • 是的,但是所有这些都依赖于拦截,可以通过两种方式完成:编译时(C++)或运行时(Ruby、Objective-C、Java(通过 asm、cglib、ApsectJ 等) ))。现代语言倾向于在运行时执行此操作。人们喜欢 Objective-C,因为它表现得像一门现代语言,尽管它是一个老家伙。就像您说的那样,语言中包含的内容越多越好(前提是做得好!)。 .但是现在我们可以使用 Objc/Foundation 提供的遗留物,这就是为什么我希望在未来几年扩展 NSObject 成为日常应用程序的标准做法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-30
      • 2014-09-10
      相关资源
      最近更新 更多