109

I tested out some isa swizzling with Swift, and found that it only works when NSObject is a super-class (directly or further up), or by using the '@objc' decoration. Otherwise it will follow a static- and vtable-dispatch style, like C++.

Is it normal to define a Swift class without a Cocoa/NSObject base class? If it is I'm concerned this means foregoing much of the dynamism of Objective-C, such as method interception and run-time introspection.

Dynamic run-time behavior sits at the heart of features like property observers, Core Data, Aspect Oriented Programming, Higher Order Messaging, analytical & logging frameworks and so on.

Using Objective-C's style of method invocation adds around 20 machine code operands to a method call, so in certain situations (many tight calls to methods with small bodies) C++ style static and vtable dispatch can perform better.

But given the general 95-5 rule (95% of performance gains come from tuning 5% of the code), doesn't it makes sense to start with the powerful dynamic features and harden where necessary?

Community
  • 1
  • 1
Jasper Blues
  • 28,258
  • 22
  • 102
  • 185

6 Answers6

112

Swift classes that are subclasses of NSObject:

  • are Objective-C classes themselves
  • use objc_msgSend() for calls to (most of) their methods
  • provide Objective-C runtime metadata for (most of) their method implementations

Swift classes that are not subclasses of NSObject:

  • are Objective-C classes, but implement only a handful of methods for NSObject compatibility
  • do not use objc_msgSend() for calls to their methods (by default)
  • do not provide Objective-C runtime metadata for their method implementations (by default)

Subclassing NSObject in Swift gets you Objective-C runtime flexibility but also Objective-C performance. Avoiding NSObject can improve performance if you don't need Objective-C's flexibility.

Edit:

With Xcode 6 beta 6, the dynamic attribute appears. This allows us to instruct Swift that a method should use dynamic dispatch, and will therefore support interception.

public dynamic func foobar() -> AnyObject {
}
Jasper Blues
  • 28,258
  • 22
  • 102
  • 185
Greg Parker
  • 7,972
  • 2
  • 24
  • 21
  • 1
    What kind of dispatch does Swift use instead of objc_msgSend? Is it static? – Bill Jun 06 '14 at 20:16
  • 13
    Swift may use objc_msgSend dispatch, virtual table dispatch, direct dispatch, or inlining. – Greg Parker Jun 06 '14 at 22:02
  • static: less then 1.1ns. vtable 1.1ns, msgSend 4.9ns . . (Depends on hardware of course). . It seems 'pure' Swift makes a great language for system-level programming, but for apps, I'd be reluctant to forego dynamic features, though I'd be happy if they moved to the compiler as-in Garbage Collection vs ARC. . . I've heard it said that static dispatch allows for better branch prediction (and thus performance) on multi-core sytems. True? – Jasper Blues Apr 23 '15 at 03:44
  • "Swift Classes that are not subclasses of NSObject are Objective-C classes" - can you provide a link to where you found that stated? – Matt S. Nov 12 '15 at 19:59
  • using `Cmmd + N` to create a new Cocoa touch class, I **wasn't** able to create a `swift` without having it not be an NSObject Subclass, I mean the button `Next` was greyed out... So when you say 'not subclass of NSObject' do you mean erasing the `:NSObject` from inside the Swift file itself? – mfaani Jun 19 '16 at 23:33
  • 1
    So in summary I should only subclass NSObject in Swift if I need to communicate with Objective C code? – MobileMon Dec 19 '16 at 13:52
14

According to the language reference, there is no requirement for classes to subclass any standard root class, so you can include or omit a superclass as needed.

Note that omitting a superclass from the class declaration, doesn't assign a implicit base superclass of any kind. It defines a base class, which will effectively become the root for an independent class hierarchy.

From the language reference:

Swift classes do not inherit from a universal base class. Classes you define without specifying a superclass automatically become base classes for you to build upon.

Trying to reference super from a class without a super class (i.e. a base class) will result in a compile time error

'super' members cannot be referenced in a root class
Cezar
  • 55,636
  • 19
  • 86
  • 87
  • 1
    Yeah. If you try to create a Cocoa Or Cocoa Touch source file in the context of a project, the form in which you insert the subclass actually prevents you from creating the class while leaving it blank. You can remove the superclass later once it has been created. – Cezar Jun 05 '14 at 10:42
  • 1
    Thanks. Using static and vtable dispatch makes sense for systems programming or performance tuning. For consistency with native Cocoa, I think dynamic *should* be the default. (Unless someone can convince me otherwise, hence this question). – Jasper Blues Jun 09 '14 at 23:27
14

I also found that if basing a Swift class on NSObject, I saw some unexpected run-time behaviour that could hide coding bugs. Here is an example.

In this example, where we don't base on NSObject, the compiler correctly spots the error in testIncorrect_CompilerShouldSpot, reporting "... 'MyClass' is not convertible to '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
      }
  }
}

In this example, where we base on NSObject, the compiler doesn't spot the error in 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
      }
  }
}

I guess the moral is, only base on NSObject where you really have to!

Pete
  • 784
  • 8
  • 9
2

The following is copied from Apple's Swift-eBook and gives an appropriate answer to your question:

Defining a Base-Class

Any class that does not inherit from another class is known as a base class.

Swift classes do not inherit from a universal base class. Classes you define without specifying a superclass automatically become base classes for you to build upon.


Reference

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_251
Dennis Zoma
  • 2,621
  • 2
  • 17
  • 27
1

I believe that the vast majority of Swift data will not be objc. Only those parts that do need to communicate with te Objective C infrastructure will be explicitly marked as such.

To which extent runtime introspection will be added to the language, I do not know. Method interception will likely become only possible if the method explicitly allows it. This is my guess, but only the language designers within Apple really know where they are really heading.

Analog File
  • 5,280
  • 20
  • 23
  • To me it makes sense make dynamism the default (through extending NSObject, using the '@objc' decoration or some other approach). It seems when there's no base class, Swift will favor static/vtable dispatch, which performs better, but in most cases this won't be necessary. . (90% of gains come from tuning 10% of code). My hope is that consistency with Objective-C's dynamism is opt-out rather than opt-in. – Jasper Blues Jun 05 '14 at 10:34
  • The way the language is designed, it is optional opt-in. As far as we know it may very well be that in the future system APIs will be added that are not natively objc. In the mid/long term they may even migrate existing libraries to non-objc code with a thin compatibility layer in objc that calls into the non objc code. We just do not know. We'll probably be able to make `educated guesses` in a few years from now. But for the moment it's just a big question mark. – Analog File Jun 05 '14 at 10:39
  • @JasperBlues I would argue that dynamism is not needed most of the time and I'd rather have performance by default and then opt in to dynamic behavior has needed. To each his own I guess :) – Lance Jun 07 '14 at 04:03
  • @Lance Hmmmm. Maybe. I'm still concerned about: observers, AOP, test mocking frameworks, analytic frameworks, etc. . and the thing about performance is that 90% of gains come from tuning 10%. . so that's my rationale - opt-in for those 10% cases. I think AOP will be a big deal for iOS enterprise apps, but it could be done using a compiler tool as it is in C++ . . certainly games, scientific computing, graphics, etc devs will welcome more performance :) – Jasper Blues Jun 07 '14 at 04:24
-6

It is normal. Look at the design goals of Swift: The goal is to make huge classes of programming problems disappear. Method swizzling is probably not one of the things that you want to do with Swift.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • 3
    Method swizzling is a way to achieve the intercept pattern at runtime. One of the uses for this is to apply cross-cutting concerns according to the principles of Aspect Oriented Programming. Apple's own notifications, property observers (built-in to swift), core data all use swizzling to good effect. . One of the things that enamored people to Objective-C despite its clunky syntax was this dynamism, which makes it easy to provide elegant solutions whenever the intercept pattern is required . . in fact there was no formal AOP framework for Objc, because the raw materials were so good. – Jasper Blues Jun 05 '14 at 10:26
  • The fact that this is how it's done now does not mean that it is how it'll be done tomorrow. As you say notifications, property observers, delegates and similar are or may be provided natively. The language will evolve, and we do not know in which direction. It has gained __a lot__ in terms of support for functional programming. But it does it in an unusual way, so I have no idea how much is missing. Yet as far as we know they may be heading toward discouraging mutation and encouraging pure functional programming. What if the future of the UI is reactive? No way to know yet. – Analog File Jun 05 '14 at 10:50
  • 1
    Yes, but all of these things depend on interception, which can done two ways: compile-time (C++) or runtime (Ruby, Objective-C, Java (via asm, cglib, ApsectJ, etc)). The modern languages tend to do it at runtime. People loved Objective-C, because it behaved like a modern language despite being an old codger. Like you say, the more of this that's baked into the language the better (provided its done well!). . but *for now* we can hook into the legacy provided by Objc/Foundation, which is why I hope extending NSObject becomes standard practice for everyday apps over the next few years. – Jasper Blues Jun 05 '14 at 11:10