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?
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.
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.
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).
Swift classes that are subclasses of NSObject:
objc_msgSend()
for calls to (most of) their methodsSwift classes that are not subclasses of NSObject:
objc_msgSend()
for calls to their methods (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 { }
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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With