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
How to Convert String to Date Without Time in Swift 3
Swift Video to Document Directory
Crashlytics Doesn't Work with Xcode 10 Beta
Is There a Difference Between Global Initialization Vs Viewdidload Initialization in Swift
[Core Data]: Threw While Encoding a Value. with Userinfo of (Null)
Cannot Use Mutating Member ... Because Append
Loading Image from Remote Url Asynchronously in Swiftui Image Using Combine's Publisher
Swift: Get The Compile Time Name of Variable (Referencing to a Class)
Dealing with Octal Numbers in Swift
Implementing Undo and Redo in a UItextview with Attributedtext
Swiftui Remove Transition of Fullscreen Cover
F# Asynchronous Http Request - Parse JSON Response
How to Grab The Parent Object from a Subview
Upload Multiple Images to Ftp Server in iOS
Why Does a Simple Swift Arithmetic Operation Compile So Slow
User Already Authenticated in App and Firebase Realtime Database Returns Failed: Permission_Denied