You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Xcode中按顺序打印文件中所有已执行方法(仅打印一次)?

实现Xcode中按顺序打印已执行方法(仅打印一次)

嘿,我懂你想要的效果——既能拥有系统NSLog自带的时间、进程信息,又能按执行顺序记录每个方法的首次调用,不会重复打印同个方法的日志。下面给你两种实用方案,适配不同的场景:

方案一:全局自动拦截(无需手动修改方法)

这个方案利用Objective-C的**方法交换(Method Swizzling)**特性,自动拦截所有OC方法的调用,记录首次执行的方法,全程无需手动给每个方法加日志代码,非常适合全局调试。

步骤1:创建NSObject分类

新建两个文件:NSObject+MethodLogger.h 和 NSObject+MethodLogger.m

头文件代码:

#import <Foundation/Foundation.h>

@interface NSObject (MethodLogger)

/// 启用方法日志记录(load方法会自动执行,此方法仅作为显式入口)
+ (void)enableMethodLogging;

@end

实现文件代码:

#import "NSObject+MethodLogger.h"
#import <objc/runtime.h>

// 存储已打印过的方法签名,避免重复
static NSMutableSet *_loggedMethods;

@implementation NSObject (MethodLogger)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _loggedMethods = [NSMutableSet set];
        
        // 交换forwardInvocation:方法,拦截所有方法调用
        Method originalMethod = class_getInstanceMethod(self, @selector(forwardInvocation:));
        Method swizzledMethod = class_getInstanceMethod(self, @selector(swizzled_forwardInvocation:));
        method_exchangeImplementations(originalMethod, swizzledMethod);
    });
}

+ (void)enableMethodLogging {
    // 空方法,仅用于触发load逻辑(实际load会在类加载时自动执行)
}

- (void)swizzled_forwardInvocation:(NSInvocation *)invocation {
    SEL methodSelector = invocation.selector;
    NSString *className = NSStringFromClass([self class]);
    NSString *methodName = NSStringFromSelector(methodSelector);
    // 拼接成你想要的格式:-[ClassName methodName:]
    NSString *fullMethodSignature = [NSString stringWithFormat:@"-%@ %@", className, methodName];
    
    // 检查是否已打印过该方法,仅首次调用时打印
    if (![_loggedMethods containsObject:fullMethodSignature]) {
        [_loggedMethods addObject:fullMethodSignature];
        // 直接用NSLog自动生成时间、进程等系统信息
        NSLog(@"%@", fullMethodSignature);
    }
    
    // 调用原方法逻辑,不影响原有功能
    [self swizzled_forwardInvocation:invocation];
}

@end

使用方式

在AppDelegate.m的application:didFinishLaunchingWithOptions:方法里添加一行代码即可启用:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [NSObject enableMethodLogging];
    // 其他初始化代码
    return YES;
}

方案二:手动封装日志宏(仅记录指定方法)

如果你只需要调试特定类的方法,不想全局拦截,那可以封装一个宏,手动添加到需要记录的方法中,同样实现去重和指定格式。

宏定义代码

在你的PCH文件或者公共头文件里添加以下代码:

// 存储已打印方法的集合
static NSMutableSet *_loggedMethods;

#define LogUniqueMethod() \
do { \
    static dispatch_once_t onceToken; \
    dispatch_once(&onceToken, ^{ \
        _loggedMethods = [NSMutableSet set]; \
    }); \
    NSString *className = NSStringFromClass([self class]); \
    NSString *methodName = NSStringFromSelector(_cmd); \
    NSString *fullMethod = [NSString stringWithFormat:@"-%@ %@", className, methodName]; \
    if (![_loggedMethods containsObject:fullMethod]) { \
        [_loggedMethods addObject:fullMethod]; \
        NSLog(@"%@", fullMethod); \
    } \
} while (0)

使用方式

在需要记录的方法里直接调用宏即可:

- (void)viewDidLoad {
    [super viewDidLoad];
    LogUniqueMethod();
    // 你的业务代码
}

- (void)prepareTableView {
    LogUniqueMethod();
    // 你的业务代码
}

注意事项

  1. 方案一的风险:Method Swizzling是全局生效的,建议只在Debug模式下启用,避免影响线上代码;同时要注意和其他第三方库的方法交换冲突,尽量在分类的load方法里用dispatch_once保证只执行一次。
  2. 日志格式:两种方案都利用系统NSLog自动生成时间、进程ID、线程ID等信息,完全符合你给出的示例格式。
  3. 去重逻辑:通过NSMutableSet存储已打印的方法签名,确保每个方法仅在首次执行时被记录。

内容的提问来源于stack exchange,提问作者user9067376

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:02:24