Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift native base class or NSObject

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?

like image 525
Jasper Blues Avatar asked Jun 05 '14 10:06

Jasper Blues


People also ask

Do all Swift classes inherit from NSObject?

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.

What is NSObject in Swift?

The root class of most Objective-C class hierarchies, from which subclasses inherit a basic interface to the runtime system and the ability to behave as Objective-C objects.

Why do we need NSObject in Swift?

NSObject exists to mark a class in Swift that you will provide that basic functionality — basically Objective-C needs you to build up from a base class with your functionality on top (That is, NSObject is a Universal Base Class).


2 Answers

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 { } 
like image 178
Greg Parker Avatar answered Oct 05 '22 04:10

Greg Parker


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!

like image 42
Pete Avatar answered Oct 05 '22 05:10

Pete