OC 基于Runtime Swizzle 机制实现检测内存

本文详细介绍了如何在Objective-C中使用方法交换(Method Swizzling)技术来改变类的行为,包括NSObject、UIViewController和UINavigationController的生命周期方法修改。通过具体实例展示了方法交换的实现过程及其在内存管理和视图控制器生命周期监控中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题记:疫情无情 人有情 人间值得

实现思路:

NSObject+Swizzle 实现方法交换;UIViewController+hook实现将viewController生命周期方法交换;UINavigationController+hook实现popViewController交换.

实现过程如下

NSObject+Swizzle.h 中申明

+ (void)swizzleMethod:(SEL)originalSEL swizzleSelector:(SEL)SwizzleSEL;

NSObject+Swizzle.m 中实现方法交换

+ (void)swizzleMethod:(SEL)originalSEL swizzleSelector:(SEL)SwizzleSEL{

    Class class = [self class];

    Method originalMthod = class_getInstanceMethod(class, originalSEL);

    Method swizzleMethod = class_getInstanceMethod(class, SwizzleSEL);

    IMP originalIMP = method_getImplementation(originalMthod);

    IMP swizzleIMP = method_getImplementation(swizzleMethod);

    //是否t添加成功g方法:添加了原始方法 实现内容指向目标方法

    BOOL isAdd = class_addMethod(class, originalSEL, swizzleIMP, method_getTypeEncoding(swizzleMethod));

    if (isAdd) {

        //初始化指向目标,那么把目标的内容指向初始

        class_replaceMethod(class, SwizzleSEL, originalIMP, method_getTypeEncoding(originalMthod));

    }else{

        //没有添加成功说明已经存在,就交换

        //注意这里交换实现的是IMP 交换

        method_exchangeImplementations(originalMthod, swizzleMethod);

    }

}

UIViewController+hook.m 中

+(void)load{

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        [self swizzleMethod:@selector(viewWillAppear:) swizzleSelector:@selector(lg_viewWillAppear:)];

        [self swizzleMethod:@selector(viewDidDisappear:) swizzleSelector:@selector(lg_viewDidDisappear:)];

    });

}

增加一个场字符串popFlag变量 用以标记视图控制器pop状态

const char  *popFlag = "popFlag";

lg_viewWillAppear方法中 运行时增加一个BOOL变量默认赋值为NO

- (void)lg_viewWillAppear:(BOOL)animated{

    [self lg_viewWillAppear:animated];

      objc_setAssociatedObject(self, popFlag, @(NO), OBJC_ASSOCIATION_ASSIGN);

}

lg_viewDidDisappear方法中 判断BOOL变量 是否为YES 如果为YES 就开启内存检测

- (void)lg_viewDidDisappear:(BOOL)animated{

    [self lg_viewWillAppear:animated];

    if ([objc_getAssociatedObject(self, popFlag) boolValue]) {

        [self lg_dealloc];

    }

}

- (void)lg_dealloc{

    __weak typeof(self) weakSelf = self;

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        __strong typeof (self) strongSelf = weakSelf;

        NSLog(@"lg_dealloc----%@",strongSelf);

    });

}

UINavigationController+hook.m中

+ (void)load{

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        [self swizzleMethod:@selector(popViewControllerAnimated:) swizzleSelector:@selector(lg_popViewControllerAnimated:)];

    });

}

- (UIViewController *)lg_popViewControllerAnimated:(BOOL)animated{

    UIViewController *popVC = [self lg_popViewControllerAnimated:animated];

    //导入外部变量

    extern const char  *popFlag ;

    objc_setAssociatedObject(popVC, popFlag, @(YES), OBJC_ASSOCIATION_ASSIGN);

    return  popVC;

}

四个典型的直接使用 Runtime 方法进行方法交换的风险。我稍作整理,以方便你查看,并便于你理解后续的内容。

第一个风险是,需要在 +load 方法中进行方法交换。因为如果在其他时候进行方法交换,难以保证另外一个线程中不会同时调用被交换的方法,从而导致程序不能按预期执行。

第二个风险是,被交换的方法必须是当前类的方法,不能是父类的方法,直接把父类的实现拷贝过来不会起作用。父类的方法必须在调用的时候使用,而不是方法交换时使用。

第三个风险是,交换的方法如果依赖了 cmd,那么交换后,如果 cmd 发生了变化,就会出现各种奇怪问题,而且这些问题还很难排查。特别是交换了系统方法,你无法保证系统方法内部是否依赖了 cmd。

第四个风险是,方法交换命名冲突。如果出现冲突,可能会导致方法交换失败。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值