• iOS之深入解析NSLog的调试技巧


    一、DEBUG 宏区分调试模式和发布模式进行特殊处理

    ① 利用 Configuration 配置不同的编译环境

    • 一个应用往往对应多个域名的情况,测试地址、生产地址、后台接口开发者的个人主机地址:
    1.开发人员环境(Other)连接写服务人的电脑,与服务器联调使用 
    2.开发环境(Debug)完成需求,代码上传,在外网开发服务器调试 
    3.测试环境(Testing)测试人员使用 
    4.预发布(PreRelease)测试人员使用,copy 的正式数据 
    5.正式环境(Release)上传 AppStore 使用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 新建不同的编译环境:

    在这里插入图片描述

    • 在 Build Setting 中搜索 preprocessor macro:

    在这里插入图片描述

    • 定义预编译宏 Preprocessor Macros:
    k_BUILD_VERSION = 0 -> 开发人员环境(Other) 
    k_BUILD_VERSION = 1 -> 开发环境(Debug) 
    k_BUILD_VERSION = 2 -> 测试环境(Testing) 
    k_BUILD_VERSION = 3 -> 预发布环境(PreRelease) 
    k_BUILD_VERSION = 4 -> 正式环境(Release)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    • 用预编译命令 Define 不同的 URL:
    #if  (k_BUILD_VERSION == 0)
     // 开发人员环境(Other)
      your code
    #elif  (k_BUILD_VERSION == 1)
    // 开发环境(Debug)
      your code
    #elif  (k_BUILD_VERSION == 2)
    // 测试环境(Testing)
      your code
    #elif  (k_BUILD_VERSION == 3)
    // 预发布(PreRelease)
       your code
    #elif  (k_BUILD_VERSION == 4)
    // 正式环境(Release)
        your code
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 切换 build Configuration:

    在这里插入图片描述

    • 配置不同环境,如果手机装了多个环境的项目,不好区分,可以在 Xcode 配置脚本,在编译时根据不同环境制作不同的 icon 图标。

    ② 发布模式关闭 NSLog

    // 调试模式
    #ifdef DEBUG
    #define NSLog(...)     NSLog(__VA_ARGS__)
    #define KisDebug 1
    
    //#define NSLog(fmt, ...) NSLog((@"[文件名:%s]\n" "[函数名:%s]\n" "[行号:%d] \n" fmt), __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__);
    
    #else // 发布模式
    #define NSLog(...)
    #define KisDebug 0
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    二、Objective-C’s boxing capability(“装箱”快速构造数字对象)

    ① NSLog 的输出

    • NSLog 将消息输出到 Apple System Log 工具或 Console 应用程序:

    在这里插入图片描述

    ② 改进 Objective-C 中的日志记录

    格式说明符描述
    func%s当前函数签名
    LINE%d在源代码文件的当前行号
    FILE%s源代码文件的完整路径
    PRETTY_FUNCTION%sprint the name of the function being called
    NSLog( @"calling: %s", __PRETTY_FUNCTION__ );
    
    • 1
    表达格式说明符描述
    NSStringFromSelector(_cmd)%@Name of the current selector
    NSStringFromClass([self class])%@Name of the current object’s class
    [[NSString stringWithUTF8String:FILE] lastPathComponent]%@Name of the source code file(源代码文件的名称)
    [NSThread callStackSymbols]%@NSArray of the current stack trace as programmer
     NSLog(@"%@", [NSThread callStackSymbols]);
    
    • 1
    • 数据类型的格式说明符:
    TypeFormat specifierConsiderations
    NSInteger%ld or %lxCast the value to long
    NSUInteger%lu or %lxCast the value to unsigned long
    CGFloat%f or %g%f works for floats and doubles when formatting; but note the technique described below for scanning
    CFIndex%ld or %lxThe same as NSInteger
    pointer%p or %zx%p adds 0x to the beginning of the output. If you don’t want that, use %zx and no typecast

    ③ 装箱快速构造数字对象

    • NSLog 函数提供许多用于打印数字的替代标记(例如 %d,%ld,%f,%d 等),为了方便起见,可以使用 Objective-C 的装箱功能来节省时间并避免编译器警告:
    double myNumber = 8.80;
    NSLog(@"number : %@", @(myNumber));
    NSLog(@"number class: %@", @(myNumber).class);
    
    // 调试结果
    number : 8.8
    number class: __NSCFNumber
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 平常也可以使用 @ 来快速包装数字类型以对象的形式进行存储和传参:
        [discountArray addObject:[[self class] mj_objectWithKeyValues:@{@"placeholder":QCTLocal(@"please_input_card_num"),@"btnContent":QCTLocal(@"member_see"),@"EnterModelType":@2,@"isEnabled":@1,@"isLast":@1,@"block":block}]];
    
    • 1

    ④ 打印调试(强化 NSLog)

    // A better version of NSLog
    #define NSLog(format, ...) do { \
    fprintf(stderr, "<%s : %d> %s\n", \
    [[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], \
    __LINE__, __func__); \
    (NSLog)((format), ##__VA_ARGS__); \
    fprintf(stderr, "-------\n"); \
    } while (0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 输出结果:
    <ViewController.m : 32> -[ViewController viewDidLoad]
    2022-06-26 17:33:31.022 DEUBG[12852:1238167] Hello World!
    -------
    
    • 1
    • 2
    • 3

    三、benchmarking 的时间测量

    ① benchmarking

    • benchmark 是程序明确地要测量并比较硬件以及软件上的运行效率。benchmarking 表示的则是测量效率的一段代码,可结合 Instruments 进行性能分析。
    • benchmark 代码不应该被加到终极提交的产品中,Benchmarking 应该被分离到单独的项目分支或独立的测试用例中,或者使用 DEBUG 宏的区分调试模式和发布模式进行特殊处理。

    ② CACurrentMediaTime

    • 推荐使用包装 mach_absolute_time 的 CACurrentMediaTime() 方法来以秒为单位测量时间:
    /* Returns the current CoreAnimation absolute time. This is the result of
     * calling mach_absolute_time () and converting the units to seconds. */
    
    CA_EXTERN CFTimeInterval CACurrentMediaTime (void)
        API_AVAILABLE (macos(10.5), ios(2.0));
    // uint64_t                        mach_absolute_time(void);
    // long timestamp = CFAbsoluteTimeGetCurrent()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 和 NSDate 或 CFAbsoluteTimeGetCurrent()偏移量不同的是,mach_absolute_time() 和 CACurrentMediaTime() 是基于内建时钟的,能够更精确更原子化地测量,并且不会因为外部时间变化而变化(例如时区变化、夏时制、秒突变)。
    static size_t const count = 1000;       // 有多少个元素需要添加到数组
    static size_t const iterations = 10000; // 测试运行的次数
    
    CFTimeInterval startTime = CACurrentMediaTime();
    {
        for (size_t i = 0; i < iterations; i++) {
            @autoreleasepool {              // 循环体都被 @autoreleasepool 包裹,用来降低内存占用
                NSMutableArray *mutableArray = [NSMutableArray array];
                for (size_t j = 0; j < count; j++) {
                    [mutableArray addObject:object];
                }
            }
        }
    }
    CFTimeInterval endTime = CACurrentMediaTime();
    NSLog(@"Total Runtime: %g s", endTime - startTime);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    ③ dispatch_benchmark

    • dispatch_benchmark 函数返回给定块执行所需的平均纳秒数:
    // 该方法并没有被公开声明,所以你必须要自己声明
    extern uint64_t dispatch_benchmark(size_t count, void (^block)(void));
    // 不要在 app 的提交代码中加入 benchmarking。dispatch_benchmark 可能会导致 app 被 App Store 拒绝
    
    • 1
    • 2
    • 3
    static size_t const count = 1000;  // 有多少个元素需要添加到数组
    
    uint64_t t = dispatch_benchmark(iterations, ^{
        @autoreleasepool {
            NSMutableArray *mutableArray = [NSMutableArray array];
            for (size_t i = 0; i < count; i++) { // 循环体都被 @autoreleasepool 包裹,用来降低内存占用
                [mutableArray addObject:object];
            }
        }
    });
    NSLog(@"[[NSMutableArray array] addObject:] Avg. Runtime: %llu ns", t);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    搭建网络yum仓库
    一款新的webshell管理工具
    NameNode的HA热备实现
    教资-中学《综合素质》(考前必背大题)
    【Vue】vscode格式刷插件Prettier以及配置项~~保姆级教程
    Android笔记(三)多Activity活动的切换中的简化处理
    Softing pnGate系列网关:将PROFIBUS总线集成到PROFINET网络
    深潜Kotlin协程(十六):Channel
    ubuntu20.04安装MySQL8、MySQL服务管理、mysql8卸载
    3D 生成重建005-NeRF席卷3D的表达形式
  • 原文地址:https://blog.csdn.net/Forever_wj/article/details/125490203