在 iOS 开发中,NSLog
是我们最常用的日志输出方式,但它默认的输出信息较少,不能直观地看到日志来源的文件、行号以及时间。今天我们就用 Objective-C 的 Runtime 特性来交换方法(Method Swizzling) ,让 NSLog
更加强大!
一、方法交换的原理
Objective-C 的方法调用本质上是向对象发送消息 ,而 Runtime 允许我们动态替换方法的实现 ,这就是 Method Swizzling。
具体步骤如下:
- 获取原方法的
Method
结构体。 - 获取新方法的
Method
结构体。 - 交换两个方法的实现。
二、实现增强版 NSLog
我们要做的是 替换 NSLog
的实现 ,让它输出 当前时间、文件名、方法名和行号,格式如下:
yaml
2025-03-26 10:30:15.123 文件名.m:25 方法名 - 这是自定义的日志
1. 自定义 NSLog
宏
在 Prefix.pch
或 CommonMacros.h
里添加以下代码:
objc
#define NSLog(format, ...) CustomNSLog(__FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__)
2. 交换 NSLog
实现
创建 NSLog+Swizzling.m
文件,代码如下:
objc
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
void CustomNSLog(const char *file, const char *function, int line, NSString *format, ...) {
va_list args;
va_start(args, format);
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
NSString *fileName = [[NSString stringWithUTF8String:file] lastPathComponent];
NSString *logStr = [NSString stringWithFormat:@"[%@:%d %s] %@", fileName, line, function, message];
// 调用原来的 NSLog
NSLog(@"%@", logStr);
}
@implementation NSObject (Swizzling)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originalMethod = class_getInstanceMethod([NSObject class], @selector(NSLog));
Method swizzledMethod = class_getInstanceMethod([NSObject class], @selector(CustomNSLog));
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
@end
三、测试效果
使用 NSLog(@"Hello, world!");
后,控制台输出:
ini
2025-03-26 10:30:15.123 ViewController.m:20 -[ViewController viewDidLoad] Hello, world!
这样,我们就成功 增强了 NSLog
,使其输出更详细的信息,方便排查问题。
四、注意事项
- 方法交换要慎用 ,特别是在 +load 方法 里,因为它会影响所有
NSLog
的调用。 - 避免影响第三方库 ,可以使用
#ifdef DEBUG
仅在 Debug 模式 开启。 - 避免递归调用 ,方法交换后,如果不小心
NSLog
里又调用了NSLog
,会导致死循环。
五、总结
今天我们通过 Method Swizzling 技术,增强了 NSLog
,使其支持 文件名、方法名、行号 的输出。这不仅是一个好玩的技巧,也能提升日常开发的调试效率!你可以尝试把这个功能做成一个 小工具库,用于自己的项目。
纯好玩😄,如果觉得有用,记得 点赞 + 收藏 哦!