Skip to main content

Objective-c vs Swift

Today we will discuss about one of the feature in which Objective-c is better than swift.  We all have read Turtle & Rabbit story. To start with today's discussion, let's treat topic as turtle & rabbit race where Objective-c is Turtle & Swift is Rabbit.

Today Discussion Topic:

1) Run Time Flexibility 

2) Dynamism

On Your Mark:

Apple introduced swift with the tagline "more safer than Objective-C". As we know to win something, have to lose something. So to become safer swift becomes Statically Typed Language. Statically Typed Language is one which does type checking at compile time. However, the old turtle (Objective-C) is a runtime language i.e. Most of the linkage between your methods,  ivar and classes are checked at runtime.

What benefit you will get with run time flexibility?
1) Fix Critically issue in deployed application on app store: Method Swizzling can be used to fix any critical issue in the application which already distributed through the app store. Let suppose you created an application by supporting method swizzling for every class. In load method of every class you check if any new implementation comes for the method then replace with the new implementation. You can push new implementation from the server.

2) Fix Crash in Third Party Library: You can analyze the private API of Apple iOS SDK and third-party frameworks (not open source). You can even modify the third party framework source code as per your requirement but at your own risk. Let suppose you are using a third party library which has some amazing features but the developer left a bug in that framework. He/She missed to implement a method and your application start crashing because run time did not find method implementation. Now there are two option either you contact the developer and ask him to fix the issue and provide you updated code or you can use runtime feature and add(class_addMethod) a implementation in the class. 

3) JSON Parsing: Runtime can be used to map between JSON and Modal Class. You can introspect the class properties and assign the value at run time. 

These all features are great and give you more flexibility but there is a phrase that "With Great power comes great responsibility :)"    

If you are using Objective-c runtime for your implementation, you need to take care of some points as behavior is unexpected and debugging is very difficult with Dynamic code.




Get Set:

To use the objective-c runtime feature you need to have the objective-c run time library and good news is that it by default exist with Xcode. You just need to import in your code and following is the code snippet:

#import <objc/runtime.h>

Before going through the various feature of runtime, lets check what is object and class in Objective-c.
Just Tap on the above line in Xcode and you will land to "runtime.h" interface. Just check the definition of struct "objc_class". 
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

It is a simple struct and contains list of members. Let's go through the members required to understand runtime of objective-c:
isa:
   Points to meta class of an object. If it is a meta class then it points to super meta class. If it is root class(NSObject) then isa points to itself.
super_class:
        As the name suggests points to the super class. If it is root class(NSObject) then points to Null.
ivars:
         pointer to objc_ivar_list data structure containing list of all the instance variable of class. 
info:
    Contains bit flags used by the Objective-c runtime to check various property related to class like:  CLS_METHOD_ARRAY (0x100L) bit flag indicates that the methodLists field is an array of pointers to objc_method_list data structures rather than a pointer to a single objc_method_list data structure.
methodLists:
      Based on the flag CLS_METHOD_ARRAY in info member It will be array of pointer to objc_method_list data structure which collectively contains all the instance methods for class if flag is not set then it is pointer to objc_method_list which contains all the instance methods. Array of Pointer to objc_method_list help to maintain category method of a class.
protocols:
           pointer to objc_protocol_list data structure containing list of all the protocols implemented by the class.


Let's check what is object in objective-c:
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

It is much simpler than objective-c class struct. Just contain a single member "isa".
isa:
   Points to the class definition of this instance.

Let's check some example of isa pointer. I have declared the following class:
@interface RuntimeTest : NSObject
@end

@implementation RuntimeTest
@end

Guess !!! Output of following:

RuntimeTest *test = [[RuntimeTest alloc] init];
Class testClass = test->isa; //Guess Output
testClass = testClass->isa; //Guess Output
testClass = testClass->isa; //Guess Output

Let's start the Race Now and check various capability provided by the runtime of objective-c.


Go:

Hurdle 1 (Adding a Class or Member):
Adding a Class at runtime:
We saw that Class and Object are very simple struct in objective-c. This provides us a great feature to create a class at runtime. It is used in many ways like if you do not know what keys and value will come in JSON data then you can create a class at run time and add the ivars according to the current properties. 
Class JsonModal = objc_allocateClassPair([NSObject class], "JsonClass", 0);
objc_registerClassPair(JsonModal);

Adding a Method at runtime:
Let suppose you find an amazing library for network operation. You used the library in your application for all the network task. One day while using the library, you find a crash in the library that 
[KNetworkOp logOperation:] Unrecognized selector sent to Instance KNetworkOp. 
How to deal with this situation. You do not have source Code of the library. Again Objective-C Runtime here to rescue you:

You can add a new definition here as by crash logs you can see that method is only for the logging the operation. You can add an empty definition of the operation:

void logOperation ( id self, SEL _cmd, id operation)
{
    NSLog (@"Hello");
}

class_addMethod([KNetworkOp class], @selector(logOperation:), (IMP)logOperation,"v@:@");

Hurdle 2 (Introspection)
It is the capability of an object to disclose details about itself at runtime. NSObject class and protocol provide various introspection methods to know the various properties of an object at runtime. Like a class of object can respond to the message (method), whether it conforms to a specific protocol. Following is the list of introspection methods:

NSString* introspectionTest = @"Test";

//Check object type at runtime
if([introspectionTest isKindOfClass:[NSString class]]){
    
}

//Check can respond to message
if([introspectionTest respondsToSelector:@selector(isEqualToString:)]){
    [introspectionTest isEqualToString:@"Test"];
}

//Check confirms to protocol
if([introspectionTest conformsToProtocol:@protocol(NSCopying)]){
    NSString *copyText = [introspectionTest copy];
}

Now question arises how at runtime these things are known?
Every Instance has isa member which point to the class of the instance and class has the list of all the protocol and method to which instance can respond. So, in this way the simple struct of object and class gives great flexibility to the runtime of objective-c.

is it possible in swift? 🤔
Yes, you can use the same method in swift if your swift object is inherited from NSObject. i.e you need to use objective-c runtime.  

//Check object type at runtime
if self.isKind(of: UIViewController.self){
}

//Check can respond to message
if self.responds(to: #selector(setter: title)){
    self.title = "Test"
}

//Check confirms to protocol
if self.conforms(to: (NSCoding.self)){
    
}

These methods only available if object class directly or indirectly derived from NSObject.  So you cannot call these methods with swift object like String, Dictionary, Array.


Hurdle 3 (Category):
Adding a ivar in category:
One of the challenges, we as iOS developer, are facing that we cannot add a stored property in a category. Again objective-c runtime come here to help us by providing following are set of methods:
  1. objc_setAssociatedObject
  2. objc_getAssociatedObject
Let suppose you have to show the day of the birthdate of users. If it is very frequent, you can use stored property on NSDate Category like following:
//interface
@interface NSDate (AssociatedObject)

@property (nonatomic, assign) NSNumber *day;

@end



//Implementation
#import "NSDate+AssociatedObject.h"
#import <objc/runtime.h>

@implementation NSDate (AssociatedObject)
@dynamic day;

- (void) setDay:(NSNumber*)day{
    objc_setAssociatedObject(self, @selector(day), day, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSNumber*) day{
     return objc_getAssociatedObject(self, @selector(day));
}

@end

So, by using objective-c runtime you can add the stored property in the category and it is very helpful in various scenarios like keep request type in URLSessionTask class, maintaining the state with UIComponent like NSIndexPath with UIButton.
Swift extensions are similar to objective-c. However, to add any member variable in swift extension you need to use Objective-c runtime.

Hurdle 4 (Method Swizzling):
Changing Method Implementation at runtime:
This one is the most well know dynamic feature that you can use with the run time. Method swizzling gives you the power to swap/exchange the implementation of methods.

For example, let's say you are using an analytic tool in your application and Business wants to track that which screen is most viewed by the user in your application. So, they gave you requirements to track appear on each view controller. There are multiple ways to do it:


1) Write a tracking logic for each view controller. In this way, there is duplicate code in each view controller.

2) Subclass is the other option which reduce the duplicity at some extent not fully. As you need to subclass different type of View controller like UIViewController, UITableViewController, UICollectionViewController. 
3) Method Swizzling is the other way. It has also some disadvantage like code readability, Debugging but it solve our code duplicity problem.

@implementation UIViewController (Runtime)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        
        SEL originalSelector = @selector(viewWillAppear:);
        SEL swizzledSelector = @selector(swizzle_viewWillAppear:);
        
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        
        BOOL didAddMethod =
        class_addMethod(class,
                        originalSelector,
                        method_getImplementation(swizzledMethod),
                        method_getTypeEncoding(swizzledMethod));
        
        if (didAddMethod) {
            class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

#pragma mark - Swizzle

- (void)swizzle_viewWillAppear:(BOOL)animated {
    [self swizzle_viewWillAppear:animated];
    [TAnalyticTool trackLoadViewController:self];
}



Presentation Ceremony:

Let discuss about swift how it can handle the above use case:

Swift:
As you know that swift is statically typed language and the default types in swift are safe. It makes sure everything should be initialized before use. So, dynamism in swift can only be achieved by using objective-c.
In swift, there are two keywords @objc & @dyanmic which expose swift to Objective-c runtime. So you can use all the runtime feature in swift also using objective-c runtime. 

Now the question comes what swift can do standalone without the help of objective-c lets check:

Introspection:
Yes, there is an alternative of isMemberOfClass method of objective-c in swift. You can use ' is' to introspect at run time. It works with swift value type also:

let test = ["key": "Introspection", "key1":"Swift"]
if test is String{
    print("test is string")
}else{
    print("test is dictionary")
}


Category:

Swift also provide the powerful feature of adding features/methods to the existing object. Similar to objective-c, swift also does not support adding instance variable directly. But you can use objective-c runtime feature in swift to add variables.

import ObjectiveC

// Declare a global var to produce a unique address as the assoc object handle
private var AssociatedObjectHandle: UInt8 = 0

extension MyClass {
    var stringProperty:String {
        get {
            return objc_getAssociatedObject(self, &AssociatedObjectHandle) as! String
        }
        set {
            objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

Ref:is-there-a-way-to-set-associated-objects-in-swift

Class & Member Addition:
You cannot add method and class in swift at runtime by using swift only. However, by using objective-c runtime method in swift you can add method & class at run time.

Method Swizzling:
Again swift does not has the capability to swizzle a method definitions at run time. But here also you can use the objective-c run time to swizzle methods in swift.

Overall, Swift is an exciting language with so many new features. Dynamism is the one feature in which swift is still lacking. Let us hope that apple soon introduce this features in swift.

Open Question:

Why the following code snippet is not infinite recursion in case of method swizzling? You can answer in comments :)

- (void)swizzle_viewWillAppear:(BOOL)animated {
    [self swizzle_viewWillAppear:animated];
    [TAnalyticTool trackLoadViewController:self];
}

Comments

Popular posts from this blog

Asynchronous Request with NSOperationQueue

Today post is about how to run a asynchronous task in NSOperationQueue.  Generally we do not run a Asynchronous task in NSOperationQueue. It is also not recommended for any programmer to do that. This post is only for learning purpose what will happen if we schedule a asynchronous task in a queue and how can we complete that task:). So let us move to the learning: NSOperationQueue: In iOS NSOperationQueue is a class, which provide a way to perform operation concurrently. We also have others way to perform concurrent operation: 1) GCD 2) NSThread 3) pThread NSOperationQueue is a wrapper on GCD, which provides a very convenient way to execute operation concurrently. To create a Queue for Operation you have to simply allocate a object of the class: NSOperationQueue * opertionQueue = [[ NSOperationQueue alloc ] init ]; For this post let suppose you are making a queue to handle all Http request in your application. So i want to create a queue in Handl...

Shake Effect in iOS

Animation Animation always capture the user attention. We can use animation to update things on the screen.  For developer also animations fascinated things to learn and implement. Today we will try shake effect with Various  API. CABasicAnimation: Here we animate view's frame y coordinate from one position to another position. It is simple example of changing the position with respect to time. CABasic Animation is deal with the single keyframe.                                        y(t) = y 0 + t*d(y) You can use CABasic Animation if you have to play with single value. You need to move object from  one position to another without any intermediate steps. CABasicAnimation * shakeAnimation = [CABasicAnimation animationWithKeyPath: @ "position" ]; shakeAnimation . duration = 0.05 ; shakeAnimation . autoreverses = YES; shakeAnimation . repe...

NSThread with Asynchronous Request

NSThread: It is class used in Cocoa framework to create a thread.You have to specify a target and selector to create a new thread in objective c. So the selector or method is main entry point of your thread.  Detail: What happen once the thread execute all the code in the method (Thread main routine)? Thread is terminated after execute all the code in the entry(its main routine) routine. So how we can run an asynchronous request on NSThread ? What is Async request? It is non blocking request which permit the application to do other task side by side as request is in continue. So it enhance parallelism in the application. Now question is how we can prepare a asynchronous request in iOS: - ( void ) startAsyncRequestWithUrl:( NSString *)urlString{     assert (urlString != nil && urlString. length > 0 );     NSURL * url = [[ NSURL alloc ] initWithString :urlString];     NSURLRequest * urlRequest = [[ NSURLReq...