0

I tried to replace the dictionary's setObject:<#(nonnull id)#> forKey:<#(nonnull id<NSCopying>)#> method with the runtime, but it failed.

Code:

#import "NSMutableDictionary+MD5MutableDictionary.h"
#import <objc/runtime.h>

@implementation NSMutableDictionary (MD5MutableDictionary)
+(void)load
{
    Method A = class_getInstanceMethod(self, @selector(setObject:forKey:));

    Method B = class_getInstanceMethod(self ,@selector(testsetObject:forKey:));

    method_exchangeImplementations(A, B);

}

- (void)testsetObject:(id)anObject forKey:(id<NSCopying>)aKey{
    [self testsetObject:anObject forKey:aKey];
    NSLog(@"success");
}

Called directly outside through a variable dictionary object for example:

NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setObject:@"jack" forKey:@"name"];
Nirav Kotecha
  • 2,493
  • 1
  • 12
  • 26
sfm.json
  • 17
  • 5

1 Answers1

0

If you really want to achieve your goal, just try code below and take care of the private class __NSDictionaryM, it should work.

@implementation NSMutableDictionary (MD5MutableDictionary)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSString *className = @"__NSDictionaryM";
        Class class = NSClassFromString(className);

        SEL originalSelector = @selector(setObject:forKey:);
        SEL swizzledSelector = @selector(testsetObject:forKey:);

        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)testsetObject:(id)anObject forKey:(id<NSCopying>)aKey{
    [self testsetObject:anObject forKey:aKey];
    NSLog(@"success");
}
@end
Neal.Marlin
  • 494
  • 5
  • 17
  • Yes, I have tried it and can work normally. Looks like a dictionary, the internal implementation of the array has a variety of mapping relationships, the class that is actually called will have different problems, thank you for your answer – sfm.json Aug 16 '19 at 08:01
  • Class cluster is a really tough thing. Hooking may not be a good choice because of the various kind of inner subclasses, and you may try to make a subclass from it. Still tough thing, good luck. – Neal.Marlin Sep 03 '20 at 03:52