Root Class of All Classes in Swift

Root class of all classes in Swift?

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.

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html

Is that what you were looking for? :)

Why is there no universal base class in Swift?

There are several object-oriented languages where one can define new root classes, including C++, PHP, and Objective-C, and they work fine, so this is definitely not a special thing.

There is a reason why Objective-C has a universal base class

As Sulthan mentioned, this is not true. There are multiple root classes in Objective-C, and you can define a new root class by simply not specifying a superclass. As Sulthan also mentioned, Cocoa itself has several root classes, NSObject, NSProxy, and Object (the root class of Protocol in ObjC 1.0).

The original Objective-C language was very flexible and someone could in theory come along and create his own root class and create his own framework that is completely different from Foundation, and uses methods completely different from retain, release, alloc, dealloc, etc., and could even implement a completely different way of memory management if he wanted. This flexibility is one of the things so amazing about the bare Objective-C language -- it simply provides a thin layer, all the other things like how objects are created and destroyed, memory management, etc., can all be determined by the user frameworks sitting on top.

However, with Apple's Objective-C 2.0 and modern runtime, more work needed to be done to make your own root class. And with the addition of ARC, in order to use your objects in ARC, you must implement Cocoa's memory management methods like retain and release. Also, to use your objects in Cocoa collections, your class must also implement things like isEqual: and hash.

So in modern Cocoa/Cocoa Touch development, objects generally must at least implement a basic set of methods, which are the methods in the NSObject protocol. All the root classes in Cocoa (NSObject, NSProxy) implement the NSObject protocol.

So, what's up with that? Are Swift classes with no defined
superclasses just NSObjects that pose as proper root classes under the
hood? Or is the default object-behaviour duplicated for each new
root-class? Or have they created another Swift-baseclass?

This is a good question, and you can find out by introspection with the Objective-C runtime. All objects in Swift are, in a sense, also Objective-C objects, in that they can be used with the Objective-C runtime just like objects from Objective-C. Some members of the class (the ones not marked @objc or dynamic) may not be visible to Objective-C, but otherwise all the introspection features of the Objective-C runtime work fully on objects of pure Swift classes. Classes defined in Swift look like any other class to the Objective-C runtime, except the name is mangled.

Using the Objective-C runtime, you can discover that for a class that is a root class in Swift, from the point of view of Objective-C, it actually has a superclass named SwiftObject. And this SwiftObject class implements the methods of the NSObject protocol like retain, release, isEqual:, respondsToSelector:, etc. (though it does not actually conform to the NSObject protocol). This is how you can use pure Swift objects with Cocoa APIs without problem.

From inside Swift itself, however, the compiler does not believe that a Swift root class implements these methods. So if you define a root class Foo, then if you try to call Foo().isKindOfClass(Foo.self), it will not compile it complaining that this method does not exist. But we can still use it with a trick -- recall that the compiler will let us call any Objective-C method (which the compiler has heard of) on a variable of type AnyObject, and the method lookup produces an implicitly-unwrapped optional function that succeeds or fails at runtime. So what we can do is cast to AnyObject, make sure to import Foundation or ObjectiveC (so the declaration is visible to the compiler), we can then call it, and it will work at runtime:

(Foo() as AnyObject).isKindOfClass(Foo.self)

So basically, from the Objective-C point of view, a Swift class either has an existing Objective-C class as root class (if it inherited from an Objective-C class), or has SwiftObject as root class.

Extend a functionality for whole the classes

Your question is not clear but guessing I think you wish to be able to extend all classes in your program as you might in, say Objective-C, the equivalent does not exist in Swift.

In Objective-C/Cocoa the vast majority of classes[1] derive directly, or through ancestors, from the NSObject class - NSObject is called a base class (often used in relation to Objective-C & Swift) or a root class (often used in relation to Java and C#). By using an Objective-C category you can add methods to NSObject which are then inherited by all its subclasses.

Java and C#, among others, are languages which like Objective-C have a root class – Object & System.Object respectively.

Swift and the Swift Standard Library do not have a single base class, a class may be declared to inherit from a superclass but if no superclass is specified the class is a base class. Given there is no common base class there is no class you can extend which would result in methods being added to all types.

The Swift types Any, AnyObject and AnyClass are not class types and do not represent a base class. AnyClass is a metatype, that is the type of a type, and as your error messages says Swift does not support extension of metatypes.

HTH


[1] Objective-C itself does not define a common base class, in that way it is similar to Swift. However the primary (Apple) Cocoa framework does, in this it differs from the Swift Standard Library which does not.

Swift native base class or NSObject

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 {
}

What use cases are there for defining a new root class?

As far as I can tell, there should be no reason for creating your own root class, because short of implementing all of the NSObject protocol methods yourself, you're going to be missing out on a lot of functionality, and going to be making a lot of calls to the Objective-C runtime that should essentially be done for you.

Unless you really had to implement the protocol differently from the default (NSProxy is a special case that does have to), you shouldn't need to make your own root class. I mean, you'd have to be writing a class that cannot fundamentally be represented by NSObject and the protocol as implemented by Apple, and in that case, why are you even writing it in Objective-C?

That's what I think. Maybe someone can come up for a creative use for it.

(People researching the topic should go look at the NSObject Class Reference, NSObject Protocol Reference, 'Core Competencies: Root Class' document, and the 'Root Class' section of the Fundamentals Guide: Cocoa Objects document.)

What's required to implement root class of Objective-C?

I just came to this question because I had the same "academic" question. After working through it a bit, I have found that the other answers to this question aren't completely correct.

It is true that on the Apple Objective-C 2.0 runtime, you must implement certain methods in order for your code to work. There is actually only one method that you need to implement: the class method initialize.

@interface MyBase 
+ (void)test;
@end
@implementation MyBase
+ (void)initialize {}
+ (void)test {
// whatever
}
@end

The runtime will automatically call initialize when you first use your class (as explained in Apple's documentation). Not implementing this method is the reason for the message forwarding error.

Compiling with clang test.m -Wall -lobjc (or gcc) will allow you to call the class method test without any issue. Making object allocation work is a different story. At the very least, you'll need an isa pointer on your base class if you're using instance variables. The runtime expects this to be there.

NSClasses vs Swift new classes

Swift classes provide type safety which means you assume less while working with objects inside collections. So it always better to use Swift variants than Objective-C wherever possible.

Swift init from unknown class which conforms to protocol

You will likely want to rethink this code in the future, to follow more Swift-like patterns, but it's not that complicated to convert, and I'm sure you have a lot of existing code that relies on behaving the same way.

The most important thing is that all the objects must be @objc classes. They can't be structs, and they must subclass from NSObject. This is the major reason you'd want to change this to a more Swifty solution based on Codable.

You also need to explicitly name you types. Swift adds the module name to its type names, which tends to break this kind of dynamic system. If you had a type Person, you would want to declare it:

@objc(Person)  // <=== This is the important part
class Person: NSObject {
required init(json: NSDictionary) { ... }
}

extension Person: JsonProtocol {
func convertToJSON() -> NSDictionary { ... }
}

This makes sure the name of the class is Person (like it would be in ObjC) and not MyGreatApp.Person (which is what it normally would be in Swift).

With that, in Swift, this code would be written this way:

if let className = obj[JSON_CLASS] as? String,
let jsonClass = NSClassFromString(className) as? JsonProtocol.Type {
arr.add(jsonClass.init(json: obj))
}

The key piece you were missing is as? JsonProtocol.Type. That's serving a similar function to +conformsToProtocol: plus the cast. The .Type indicates that this is a metatype check on Person.self rather than a normal type check on Person. For more on that see Metatype Type in the Swift Language Reference.

Note that the original ObjC code is a bit dangerous. The -initWithJSON must return an object. It cannot return nil, or this code will crash at the addObject call. That means that implementing JsonProtocol requires that the object construct something even if the JSON it is passed is invalid. Swift will enforce this, but ObjC does not, so you should think carefully about what should happen if the input is corrupted. I would be very tempted to change the init to an failable or throwing initializer if you can make that work with your current code.

I also suggest replacing NSDictionary and NSArray with Dictionary and Array. That should be fairly straightforward without redesigning your code.



Related Topics



Leave a reply



Submit