Wednesday 6 May 2015

OC: Swizzling: Category a class and override a method, not able to call super method, so Swizzling is the solution for you.

Hello Objective C, Apple iOS / Mac lovers,

Swizzling a technique may be useful for you in some scenario. Think a case If a super class method you want to override, but problem with that, That super class objects are already created in project. 
Now we have two ways: 
First: Traditional way, inherit that super class and override that method. But problem with this way is you have to manually change the new child class name from super class name. 
Class C1 has a method M1 which we want to override,
We made another class C2 : C1, and override method M1. 
In your project there are already many other classes have a object of C1. 
C1 *c11, *c12, *c13;
Now in find all class object which is Made of C1 and now replace to C2,
C2 *c11, *c12, *c13;
Don't you think it is too much of laborious work. Now consider the second way,

Second: Category concept in Objective C, Ok you can Category class C1 and override method M1. But Now there is another problem arises. You cannot call original method C1, because it is replaced by override method. It does mean, If you have 15 lines of code in M1 in C1, and Category CT1 override method M1, that 15 lines of codes is not available. So you have now two ways to solve:
A: You can rewrite those 25 lines of code in Category CT1 method M1. which is like again laborious work, less laborious but still you are a labour.
B: (RECOMMENDED Swizzling) Choose Swizzling technique to achieve that. In simple word, inheritance and category concept is now mix. In category if you will able to call super method then you don't have to write those 15 lines of code. This technique may be good for you if you are thinking of custom modification in lots of methods and you don't want to inherit because class name change is really pain. And you don't want to copy paste code of your super class overridden method. 
I think it is very approachable concept 


Lets take it simple Way.
I have created a category of NSObject Class. NSObject+Swizzling.h
//
//  NSObject+Swizzling.h
//
//  Created by gauravds on 5/6/15.
//  Copyright (c) 2015 punchh. All rights reserved.
//
#import <foundation/Foundation.h>
#import <objc/runtime.h>

@interface NSObject (Swizzling)
+ (void)superClass:(Class)class
     orignalMethod:(SEL)originalSelector
         newMethod:(SEL)swizzledSelector;
@end

@implementation NSObject (Swizzling)
+ (void)superClass:(Class)class
     orignalMethod:(SEL)originalSelector
         newMethod:(SEL)swizzledSelector {
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    
    // When swizzling a class method, use the following:
    // Class class = object_getClass((id)self);
    // ...
    // Method originalMethod = class_getClassMethod(class, originalSelector);
    // Method swizzledMethod = class_getClassMethod(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);
    }
}
@end
Ok now we have to write simple category codes :
//
//  ExtentionTest.m
//
//  Created by gauravds on 5/6/15.
//  Copyright (c) 2015 punchh. All rights reserved.
//

#import "ExtentionTest.h"
#import "NSObject+Swizzling.h"

@implementation ViewController(SwizzlingTest)
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self superClass:[self class]
           orignalMethod:@selector(viewDidLoad)
               newMethod:@selector(gds_viewDidLoad)];
        
        [self superClass:[self class]
           orignalMethod:@selector(viewWillAppear:)
               newMethod:@selector(gds_viewWillAppear:)];
    });
}

- (void)gds_viewDidLoad {
    NSLog(@"Child Class viewDidLoad");
    [self gds_viewDidLoad]; // super calling
}

- (void)gds_viewWillAppear:(BOOL)animated {
    [self gds_viewWillAppear:animated]; // super calling
    NSLog(@"Child class viewWillAppear:");
}

@end

You cannot checkout full code with example: https://github.com/dayitv89/Swizzling.
May be this technique useful for you if you like smart work rather than hard work. Happy coding, Voila. 

No comments:

Post a Comment