how to override/swizzle a method of a private class in runtime objective-c?

邮差的信 提交于 2019-12-01 00:31:52

this did it.. i'm not sure if this counts as an actual answer since i just got around the problem by simply calling [self layoutIfNeeded] instead of [self layoutSubviews]

void _autolayout_replacementLayoutSubviews(id self, SEL _cmd)
{
    // calling super
    struct objc_super superTarget;
    superTarget.receiver = self;
    superTarget.super_class = class_getSuperclass(object_getClass(self));
    objc_msgSendSuper(&superTarget, @selector(layoutSubviews));

    // to get around calling layoutSubviews and having
    // a recursive call
    [self layoutIfNeeded];

    objc_msgSendSuper(&superTarget, @selector(layoutSubviews));
}

- (void)replaceGMSUISettingsViewImplementation
{
    class_addMethod(NSClassFromString(@"GMSUISettingsView"), @selector(_autolayout_replacementLayoutSubviews), (IMP)_autolayout_replacementLayoutSubviews, "v@:");

    Method existing = class_getInstanceMethod(NSClassFromString(@"GMSUISettingsView"), @selector(layoutSubviews));
    Method new = class_getInstanceMethod(NSClassFromString(@"GMSUISettingsView"), @selector(_autolayout_replacementLayoutSubviews));

    method_exchangeImplementations(existing, new);
}

UPDATE

If you want to preserve original implementation you can get it and call it inside the blockImp. Then after that call [[aClass superclass] layoutSubviews] or call it with function pointer. Note that all this code need some error check and exception prevents.

//get method encoding
const char * encoding = method_getTypeEncoding(class_getInstanceMethod(NSClassFromString(@"GMSUISettingsView"), @selector(layoutSubviews)));

void(^oldImplementation)(id aClass, SEL aSelector) = imp_getBlock(method_getImplementation(class_getInstanceMethod(NSClassFromString(@"GMSUISettingsView"), @selector(layoutSubviews))));

id blockImp = ^void(id aClass, SEL aSelector)
{
    //old imlpementation
    oldImplementation(aClass, aSelector);

    //calling [super layoutSubviews]
    IMP aImp = [[aClass superclass] methodForSelector:@selector(layoutSubviews)];
    id (*func)(id, SEL) = (void *)aImp;
    func([aClass superclass], @selector(layoutSubviews));
};
IMP newImp = imp_implementationWithBlock(blockImp);
class_replaceMethod(NSClassFromString(@"GMSUISettingsView"), @selector(layoutSubviews), newImp, encoding);

ORIGINAL ANSWER

I'm sorry but I didn't fully understand do you want to completely override 'layoutsubviews' or to have original implementation and change just part of it. If it's the first you could use class_replaceMethod instead of swizzling. It will have performance hits though! Something like this should do the trick:

//get method encoding
const char * encoding = method_getTypeEncoding(class_getInstanceMethod(NSClassFromString(@"GMSUISettingsView"), @selector(layoutSubviews)));

id blockImp = ^void(id aClass, SEL aSelector)
{
    //what you want to happen in layout subviews
};
IMP newImp = imp_implementationWithBlock(blockImp);
class_replaceMethod(NSClassFromString(@"GMSUISettingsView"), @selector(layoutSubviews), newImp, encoding);

I've not tested this on device/simulator but something like this should work for you. I assume you know it's not a really bright idea to manipulate private classes ;)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!