Search code examples
iosobjective-cinheritancepolymorphismnsurlcache

iOS how to add custom code to a class method without subclassing?


If I use a core iOS class in my app, like NSURLCache; I would like to print a log message anytime [[NSURLCache sharedURLCache] removeAllCachedResponses] gets called. How can I extend a class method like that without explicitly subclassing NSURLCache and replacing all references of that within a project with a custom class? Is there some way to do this with category?


Solution

  • You can do it using Method Swizzling. Here is a page with a nice overview of the technique from which I borrowed most of the code below:

    @interface SwizzleNSURLCache : NSURLCache
    +(void)load;
    -(void)swzl_removeAllCachedResponses;
    @end
    
    +(void)load {
        Class class = [SwizzleNSURLCache class];
        SEL originalSelector = @selector(removeAllCachedResponses);
        SEL swizzledSelector = @selector(swzl_removeAllCachedResponses);
    
        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);
        }
    }
    
    -(void) swzl_removeAllCachedResponses {
        // This is your replacement method. You can do whatever you want here.
        NSLog(@"Running removeAllCachedResponses");
        // If you need to call the actual implementation, do it like this:
        [self swzl_removeAllCachedResponses];
    }
    

    You do not need to replace references of NSURLCache with this subclass. All you need is to load it once from some place in your program.

    Note: I am not sure if there is an easier way to do it. Method swizzling is definitely a heavy artillery, but it gets the job done.