As mentioned above, NSObject is the root class for all the Objective C classes. Its the one class that has no superclass. It provides all the methods that are fundamental to an instance. Its through NSObject that instances of all classes obtain their ability to behave as objects.

NSObject is an abstract class, meaning, you can use instance of classes that inherit from NSObject, but never NSObject itself.

Some Common Methods

NSObject is so deeply engrained in the Apple development ecosystem that we use load of its methods everyday without even knowing them. Lets take a look few methods that you might have used beforehand.

  • init()
  • isSubclass(of: AnyClass) -> Bool
  • isKind(of: AnyClass) -> Bool
  • isMember(of: AnyClass) -> Bool
  • responds(to: Selector!) -> Bool
  • conforms(to: Protocol) -> Bool
  • description() -> String
  • perform(Selector!, with: Any!, with: Any!) -> Unmanaged<AnyObject>!

Not only does NSObject provide you with these universal methods, but it also provide you Key-Value Observing and Key-Value Coding mechanism by default. This way NSObject ensures that all you objects, act like, well, object!

If you are interested to know more, check out the apple documentation.

Objective-C Runtime

Another fun use of NSObject is that inheriting from it will give you access to Objective-C runtime.

Now what the hell is Objective-C runtime? The Objective-C runtime is a runtime library that provides support for the dynamic properties of the Objective-C language, and as such is linked to by all Objective-C apps. Objective-C runtime library support functions are implemented in the shared library found at /usr/lib/libobjc.A.dylib.

PS. I copies this word for word from apple’s documentation.

Again, Why would you need this. Well, the straight forward answer is that you might not. Objective-C runtime is primarily used for developing bridge layers between Objective-C and other languages. But, in certain cases, you just might have no other option but to use them. Remember back in the good old days when every interview asked what is method swizzling. Well, method swizzling is achieved using Objective-C runtime.

But its not just method swizzling, a lot of apps, frameworks and libraries use the power of Objective-C runtime to create brilliant things. Look at Flex, which when integrated to an app, will give you all the runtime info like network calls, view hierarchy, iVars, memory usage, all done by harnessing the power of objc runtime.

Lets take a look at few of these APIs. We’ll start by creating a Person class

class Person: NSObject {
    var firstName: String
    var lastName: String
    var age: Int
    
    init(firstName: String, lastName: String, age: Int) {
        self.firstName = firstName
        self.lastName = lastName
        self.age = age
        super.init()
    }
}

let person = Person(firstName: "Adwin", lastName: "Ross", age: 30)

To find the superclass of the person object, you can simply use class_getSuperclass

let superclass: AnyClass = class_getSuperclass(type(of: person))! // output: NSObject

To get the class name, use class_getName

let classNameChar = class_getName(type(of: person))
let className = String(cString: classNameChar) // output: __lldb_expr_7.Person

To get the size of the object in bytes use class_getInstanceSize

let instanceSize = class_getInstanceSize(type(of: person)) // output: 48

To get a list of all the Ivars use

var count: UInt32 = 0
var ivars: UnsafeMutablePointer<Ivar> = class_copyIvarList(type(of: person), &count)!
var ivarNames = [NSString]()
for i in 0..<count {
    let ivarName = NSString(cString: ivar_getName(ivars[Int(i)])!, encoding: NSUTF8StringEncoding)
    ivarNames.append(ivarName)
}

Lets talk about the above code. Here we use class_copyIvarList which returns an array of pointers of type Ivar describing the instance variables declared by the class. Instance variables declared by the superclass are not included.

We traverse through this array to get the name of each instance variable using ivar_getName which returns a C string containing the instance variable’s name. You can then use these Ivar names to access the properties using KVC.

Lets step it up a notch. Lets create an object from scratch.

if let allocatedPersonClass: AnyClass = objc_allocateClassPair(NSObject.classForCoder(), "Person", 0) {
    let rawEncoding = "i"
    var size: Int = 0
    var alignment: Int = 0
    NSGetSizeAndAlignment(rawEncoding, &size, &alignment)
    if class_addIvar(allocatedPersonClass, "age", size, UInt8(alignment), "i") {
        print("age added")
    } else {
        print("failed")
    }
    if class_addIvar(allocatedPersonClass, "firstName", size, UInt8(alignment), "@") {
        print("firstName added")
    } else {
        print("failed")
    }
    if class_addIvar(allocatedPersonClass, "lastName", size, UInt8(alignment), "@") {
        print("lastName added")
    } else {
        print("failed")
    }
    objc_registerClassPair(allocatedPersonClass)
    let runTimeObject = allocatedPersonClass.alloc()
    runTimeObject.setValue(30, forKey: "age")
    runTimeObject.setValue("Adwin", forKey: "firstName")
    runTimeObject.setValue("Ross", forKey: "lastName")
    print("firstName: ", runTimeObject.value(forKey: "firstName")!)
    print("lastName: ", runTimeObject.value(forKey: "lastName")!)
    print("age: ", runTimeObject.value(forKey: "age")!)
}

Ohh boy, thats a scary looking piece of code. Don’t worry. Let me walk you though it.

Lets start with what objc_allocateClassPair does. This API lets you create a new class at runtime.The three parameters in order are superclass name, class name and extra bytes to be allocated during class creation.

NSGetSizeAndAlignment takes in a typePtr, sizep and alignp as parameters. It obtains the actual size and the aligned size of the first data type represented by typePtr and returns a pointer to the position of the next data type in typePtr.

class_addIvar lets you add Ivars to you class. It takes in the class object, variable name, size, alignment and variable type as a parameter. The i variable type signifies that the age is of Int type. @ variable type signifies that the firstName is of AnyClass type. A full list of these types can be found here.

objc_registerClassPair will register your class for use.

Now, you have created a class at runtime. the alloc() method lets you instantiate the class. After that use KVC to initialize and use the object.

This is just the tip of the iceberg. Objective-C runtime lets you do a lot cool things. Explore all the runtime APIs here.

Download the full code here.