普通视图

发现新文章,点击刷新页面。
昨天以前掘金专栏-Jony唐

YYCache(二)

作者 Jony唐
2023年3月24日 18:08

YYCache(二)

代码测试:

初始化YYCache实例:

#import <YYCache/YYCache.h>
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) YYCache *contactsCache;
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.contactsCache = [YYCache cacheWithName:@"Contacts”];
}

添加一个通讯录模型:

@interface ContactsModel : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *phoneNumber;
@end

添加10条数据:

for (int i = 0; i < 10; i ++) {
     ContactsModel *model = [ContactsModel new];
     model.name = [NSString stringWithFormat:@"张%d",i];
     model.phoneNumber = [NSString stringWithFormat:@"1588889999%d",i];
     [self.contactsCache setObject:model forKey:
     [NSString stringWithFormat:@"kContacts_%d",i]];
 }

提示Sending 'ContactsModel *__strong' to parameter of incompatible type 'id<NSCoding> _Nullable: 给ContactsModel添加<NSCoding>协议:

@interface ContactsModel : NSObject<NSCoding>

//通讯类内部的两个属性变量分别转码
- (void)encodeWithCoder:(nonnull NSCoder *)coder {
    [coder encodeObject:_name forKey:@"name"];
    [coder encodeObject:_phoneNumber forKey:@"phoneNumber"];
}
//分别把两个属性变量根据关键字进行逆转码,最后返回一个Contacts类的对象
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
    if (self = [super init]) {
        if (coder) {
            _name = [coder decodeObjectOfClass:[NSString class] 
            forKey:@"name"];
            _phoneNumber = [coder decodeObjectOfClass:[NSString class] 
            forKey:@"phoneNumber"];
        }
    }
    return self;
}

提示消失,用循环把值取出来:

for (int i = 0; i < 10; i++) {
    ContactsModel *model = (ContactsModel *)[self.contactsCache
    objectForKey:[NSString stringWithFormat:@"kContacts_%d",i]];
    NSLog(@"name = %@ phoneNumber = %@",model.name, model.phoneNumber);
}

运行打印:

YYCacheDemo[3304:73220] name = 张0 phoneNumber = 15888899990
YYCacheDemo[3304:73220] name = 张1 phoneNumber = 15888899991
YYCacheDemo[3304:73220] name = 张2 phoneNumber = 15888899992
YYCacheDemo[3304:73220] name = 张3 phoneNumber = 15888899993
YYCacheDemo[3304:73220] name = 张4 phoneNumber = 15888899994
YYCacheDemo[3304:73220] name = 张5 phoneNumber = 15888899995
YYCacheDemo[3304:73220] name = 张6 phoneNumber = 15888899996
YYCacheDemo[3304:73220] name = 张7 phoneNumber = 15888899997
YYCacheDemo[3304:73220] name = 张8 phoneNumber = 15888899998
YYCacheDemo[3304:73220] name = 张9 phoneNumber = 15888899999

打印self.contactsCache.memoryCache和self.contactsCache.diskCache,发现数据一样

主要功能:

一、添加限制
二、数据修剪

YYMemoryCachePasted Graphic.pngYYDiskCach

Pasted Graphic 1.png

测试给内存添加数量限制:

self.contactsCache.memoryCache.countLimit = 5;
YYCacheDemo[4272:102918] name = (null) phoneNumber = (null)
YYCacheDemo[4272:102918] name = (null) phoneNumber = (null)
YYCacheDemo[4272:102918] name = (null) phoneNumber = (null)
YYCacheDemo[4272:102918] name = (null) phoneNumber = (null)
YYCacheDemo[4272:102918] name = (null) phoneNumber = (null)
YYCacheDemo[4272:102918] name = 张5 phoneNumber = 15888899995
YYCacheDemo[4272:102918] name = 张6 phoneNumber = 15888899996
YYCacheDemo[4272:102918] name = 张7 phoneNumber = 15888899997
YYCacheDemo[4272:102918] name = 张8 phoneNumber = 15888899998
YYCacheDemo[4272:102918] name = 张9 phoneNumber = 15888899999

发现前面的5个数据都被移除了

修剪最大个数为8个:

[self.contactsCache.memoryCache trimToCount:8];

运行:

YYCacheDemo[4461:108978] name = (null) phoneNumber = (null)
YYCacheDemo[4461:108978] name = (null) phoneNumber = (null)
YYCacheDemo[4461:108978] name = 张2 phoneNumber = 15888899992
YYCacheDemo[4461:108978] name = 张3 phoneNumber = 15888899993
YYCacheDemo[4461:108978] name = 张4 phoneNumber = 15888899994
YYCacheDemo[4461:108978] name = 张5 phoneNumber = 15888899995
YYCacheDemo[4461:108978] name = 张6 phoneNumber = 15888899996
YYCacheDemo[4461:108978] name = 张7 phoneNumber = 15888899997
YYCacheDemo[4461:108978] name = 张8 phoneNumber = 15888899998
YYCacheDemo[4461:108978] name = 张9 phoneNumber = 15888899999

清空所有缓存:

 [self.contactsCache removeAllObjects];

内存缓存:

YYCacheDemo[6819:162854] memory name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] memory name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] memory name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] memory name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] memory name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] memory name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] memory name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] memory name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] memory name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] memory name = (null) phoneNumber = (null)

磁盘缓存:

YYCacheDemo[6819:162854] disk name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] disk name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] disk name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] disk name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] disk name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] disk name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] disk name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] disk name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] disk name = (null) phoneNumber = (null)
YYCacheDemo[6819:162854] disk name = (null) phoneNumber = (null)

源码:

YYMemoryCache的初始化:

- (instancetype)init {
    self = super.init;
    pthread_mutex_init(&_lock, NULL);
    _lru = [_YYLinkedMap new];
    _queue = dispatch_queue_create("com.ibireme.cache.memory", DISPATCH_QUEUE_SERIAL);
    _countLimit = NSUIntegerMax;
    _costLimit = NSUIntegerMax;
    _ageLimit = DBL_MAX;
    _autoTrimInterval = 5.0;
    _shouldRemoveAllObjectsOnMemoryWarning = YES;
    _shouldRemoveAllObjectsWhenEnteringBackground = YES;
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appDidReceiveMemoryWarningNotification) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_appDidEnterBackgroundNotification) name:UIApplicationDidEnterBackgroundNotification object:nil];
    [self _trimRecursively];
    return self;
}

一个_YYLinkedMap的实例,查看_YYLinkedMap

@interface _YYLinkedMap : NSObject {
    @package
    CFMutableDictionaryRef _dic; // do not set object directly
    NSUInteger _totalCost;
    NSUInteger _totalCount;
    _YYLinkedMapNode *_head; // MRU, do not change it directly
    _YYLinkedMapNode *_tail; // LRU, do not change it directly
    BOOL _releaseOnMainThread;
    BOOL _releaseAsynchronously;
}

发现_YYLinkedMap是一个双向链表,有两个_YYLinkedMapNode类型节点,还有一个CFMutableDictionaryRef的字典_dic_dic是真正存放数据的地方。

递归修剪:

- (void)_trimRecursively {
    __weak typeof(self) _self = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(_autoTrimInterval * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        __strong typeof(_self) self = _self;
        if (!self) return;
        [self _trimInBackground];
        [self _trimRecursively];
    });
}
- (void)_trimInBackground {
    dispatch_async(_queue, ^{
        [self _trimToCost:self->_costLimit];
        [self _trimToCount:self->_countLimit];
        [self _trimToAge:self->_ageLimit];
    });
}

每隔_autoTrimInterval秒就自动调用修整内存数据,_autoTrimInterval默认是5秒。

添加数据:

- (void)setObject:(id)object forKey:(id)key withCost:(NSUInteger)cost {
    ….
    _YYLinkedMapNode *node = CFDictionaryGetValue(_lru->_dic, (__bridge const void *)(key));
    NSTimeInterval now = CACurrentMediaTime();
    if (node) {
        _lru->_totalCost -= node->_cost;
        _lru->_totalCost += cost;
        node->_cost = cost;
        node->_time = now;
        node->_value = object;
        [_lru bringNodeToHead:node];
    } else {
        node = [_YYLinkedMapNode new];
        node->_cost = cost;
        node->_time = now;
        node->_key = key;
        node->_value = object;
        [_lru insertNodeAtHead:node];
    }
   …
    pthread_mutex_unlock(&_lock);
}

由于_lru = [_YYLinkedMap new]; ,可以看到就是操作_YYLinkedMap双链表,使用的是pthread_mutex锁。

启动优化clang插桩(三)

作者 Jony唐
2023年3月21日 20:32

一、获取符号

先把获取符号的代码写在touchBegan里面:

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //    NSLog(@"%s",__func__);
//    因为不知道有多少个,所有用while循环
    while(YES){
        //        将node取出来
        SYNode *node = OSAtomicDequeue(&symbolList, offsetof(SYNode, next));
        //        取到node为空退出当前循环
        if(node == NULL){
            break;
        }
//        打印拿到符号的信息
        Dl_info info;
        dladdr(node->pc,&info);
        printf("%s\n",info.dli_sname);
    }
} 

点击运行,会打印出一堆的touchesBegan

-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
-[ViewController touchesBegan:withEvent:]
…

回到Build setting,将原来标记那里添加一个参数func

Pasted Graphic.png

再次运行,点击屏幕打印:

-[ViewController touchesBegan:withEvent:]
-[SceneDelegate sceneDidBecomeActive:]
-[SceneDelegate sceneWillEnterForeground:]
-[ViewController viewDidLoad]
-[SceneDelegate window]
-[SceneDelegate scene:willConnectToSession:options:]
-[SceneDelegate window]
-[SceneDelegate setWindow:]
-[SceneDelegate window]
-[AppDelegate application:didFinishLaunchingWithOptions:]
main

这样就拿到了所有的符号。

二、处理符号

因为队列是先进后出,所以我们需要做一个取反的操作,而且还有一些是重复的符号,我们需要去掉,处理完这些步骤之后的这些符号就是程序启动时候的顺序。

先给函数添加下划线:

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //    NSLog(@"%s",__func__);
    //    初始化一个数组来装载有顺序的数据
    NSMutableArray *symbolNames = [NSMutableArray array];
    
    //因为不知道有多少个,所有用while循环
    while(YES){
        //将node取出来
        SYNode *node = OSAtomicDequeue(&symbolList, offsetof(SYNode, next));
        //        取到node为空退出当前循环
        if(node == NULL){
            break;
        }
        //打印拿到符号的信息
        Dl_info info;
        dladdr(node->pc,&info);
        printf("%s\n",info.dli_sname);
        //转为OC字符串
        NSString *name = @(info.dli_sname );
        //判断是否是方法
        BOOL isMethod = [name hasPrefix:@"+["] ||
        [name hasPrefix:@"-["];
        //拿到处理后的符号
        NSString * symbolName = isMethod? name : [@“_” stringByAppendingString:name];
//        添加进数组
        [symbolNames addObject:symbolName];
    }
    NSLog(@"%@",symbolNames);
}

运行打印,得到:

(
    "-[ViewController touchesBegan:withEvent:]",
    "-[SceneDelegate sceneDidBecomeActive:]",
    "-[SceneDelegate sceneWillEnterForeground:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate scene:willConnectToSession:options:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate setWindow:]",
    "-[SceneDelegate window]",
    "-[AppDelegate application:didFinishLaunchingWithOptions:]",
    "_main"
)

这样main函数就加上了下划线“_”

三、符号逆序

直接反向遍历:

NSEnumerator *em = [symbolNames reverseObjectEnumerator];
NSLog(@"%@",em.allObjects);

运行打印,得到:

 (
    "_main",
    "-[AppDelegate application:didFinishLaunchingWithOptions:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate setWindow:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate scene:willConnectToSession:options:]",
    "-[SceneDelegate window]",
    "-[ViewController viewDidLoad]",
    "-[SceneDelegate sceneWillEnterForeground:]",
    "-[SceneDelegate sceneDidBecomeActive:]",
    "-[ViewController touchesBegan:withEvent:]"
)

这就得到我们想要的顺序。

四、符号去重

//   初始化去重后的数组
NSMutableArray *funcs = [NSMutableArray arrayWithCapacity:symbolNames.count];
//    定义表示
NSString *name;
//    判断是否数组里已经存在,不存在则添加
while (name = [em nextObject]) {
if(![funcs containsObject:name]){
[funcs addObject:name];
    }
}
NSLog(@"%@",funcs);

运行打印,得到:

(
    "_main",
    "-[AppDelegate application:didFinishLaunchingWithOptions:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate setWindow:]",
    "-[SceneDelegate scene:willConnectToSession:options:]",
    "-[ViewController viewDidLoad]",
    "-[SceneDelegate sceneWillEnterForeground:]",
    "-[SceneDelegate sceneDidBecomeActive:]",
    "-[ViewController touchesBegan:withEvent:]"
)

可以发现里面已经没有了重复的符号

五、生成.order文件

//    拼接成一个字符串
    NSString *funcsStr = [funcs componentsJoinedByString:@"\n"];
//    文件路径
    NSString *filePath = [NSTemporaryDirectory() stringByAppendingString:@"TraceDemo.order"];
//    文件的内容
    NSData *file = [funcsStr dataUsingEncoding:NSUTF8StringEncoding];
//    写入文件
    [[NSFileManager defaultManager] createFileAtPath:filePath contents:file attributes:nil];
//    打印路径
    NSLog(@"%@",NSHomeDirectory());

运行打印:

TraceDemo[31577:752540] /Users/xxxx/Library/Developer/CoreSimulator/Devices/876D0DEB-7AC9-4B67-A877-DB2BC4B5BD10/data/Containers/Data/Application/702BBFFB-D619-4B19-814C-0C9CXXXXX
Tmp文件下可以看到一个.order文件

Pasted Graphic 3.png

打开文件:

Pasted Graphic 4.png 写入的内容就是我们想要的内容,这样就可以把.order文件复制进项目里。

Pasted Graphic 2.png

Order File添加文件位置:

Pasted Graphic 1.png

Link Map File打开:

1__#$!@%!#__Pasted Graphic 3.png

运行,然后找到这个LinkMap文件:

1__#$!@%!#__Pasted Graphic 4.png

打开和.order文件对比:

Pasted Graphic 5.png

发现完全一致。

重排之后减少多少时间,就需要用Instruments工具的System Trace去做具体对比。

六、使用swift情况

如果项目使用swift的话,跟重排和使用OC类似。创建一个swift文件:

import Foundation
class SwiftPage: NSObject{
@objc class public func swiftFunc(){
print("我是swift")
    }
}

导入头文件:

#import "TraceDemo-Swift.h"

添加方法:

- (void)viewDidLoad {
    [super viewDidLoad];
    [SwiftPage swiftFunc];
}

运行:

我是swift

点击屏幕打印:

(
    "_main",
    "-[AppDelegate application:didFinishLaunchingWithOptions:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate setWindow:]",
    "-[SceneDelegate scene:willConnectToSession:options:]",
    "-[ViewController viewDidLoad]",
    "-[SceneDelegate sceneWillEnterForeground:]",
    "-[SceneDelegate sceneDidBecomeActive:]",
    "-[ViewController touchesBegan:withEvent:]"
)

发现并没有打印swift方法,因为swift并不是clang编译的,clang插桩只能编译CC++OC,这里就需要用在Other Swift Flags添加两个标记:-sanitize-coverage=func-sanitize=undefined

1__#$!@%!#__Pasted Graphic.png

再次运行:

(
    "_main",
    "-[AppDelegate application:didFinishLaunchingWithOptions:]",
    "-[SceneDelegate window]",
    "-[SceneDelegate setWindow:]",
    "-[SceneDelegate scene:willConnectToSession:options:]",
    "-[ViewController viewDidLoad]",
    "_$s9TraceDemo9SwiftPageC9swiftFuncyyFZTo",
    "_$s9TraceDemo9SwiftPageC9swiftFuncyyFZ",
    "_$ss27_finalizeUninitializedArrayySayxGABnlF",
    "_$sSa12_endMutationyyF",
    "_$ss5print_9separator10terminatoryypd_S2StFfA0_",
    "_$ss5print_9separator10terminatoryypd_S2StFfA1_",
    "-[SceneDelegate sceneWillEnterForeground:]",
    "-[SceneDelegate sceneDidBecomeActive:]",
    "-[ViewController touchesBegan:withEvent:]"
)

可以看到swift方法,因为swift方法自带混淆,这里swift也捕获到了,这里就完成了OCswift的二进制重排。在项目需要上线的时候,删除一开始的标记-fsanitize-coverage=func,trace-pc-guard和其他测试代码。

启动优化clang插桩(一)

作者 Jony唐
2023年3月21日 20:31

启动优化clang插桩(一)

一、了解Clang

首先到Clang地址:Clang Documentation Pasted Graphic.pngPCs指的是CPU的寄存器,用来存储将要执行的下一条指令的地址,Tracing PCs就是跟踪CPU将要执行的代码。

二、如何使用

网页下拉有个Example Pasted Graphic 1.png 使用之前要在工程添加标记: Pasted Graphic 2.png

编译器就会在每一行代码的边缘插入这一段函数:__sanitizer_cov_trace_pc_guard(&guard_variable)

打开实例demo,在Build Settings 搜索 Other c Flag 填入 -fsanitize-coverage=trace-pc-guard

1__#$!@%!#__Pasted Graphic 1.png

项目会报未定义符号的错:

Pasted Graphic 7.png

这就需要去定义这两个符号,先把这两个函数复制过来:

Pasted Graphic 5.png 先把代码复制进ViewController

extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,
                                                    uint32_t *stop) {
  static uint64_t N;  // Counter for the guards.
  if (start == stop || *start) return;  // Initialize only once.
  printf("INIT: %p %p\n", start, stop);
  for (uint32_t *x = start; x < stop; x++)
    *x = ++N;  // Guards should start from 1.
}
// This callback is inserted by the compiler on every edge in the
// control flow (some optimizations apply).
// Typically, the compiler will emit the code like this:
//    if(*guard)
//      __sanitizer_cov_trace_pc_guard(guard);
// But for large functions it will emit a simple call:
//    __sanitizer_cov_trace_pc_guard(guard);
extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
  if (!*guard) return;  // Duplicate the guard check.
  // If you set *guard to 0 this code will not be called again for this edge.
  // Now you can get the PC and do whatever you want:
  //   store it somewhere or symbolize it and print right away.
  // The values of `*guard` are as you set them in
  // __sanitizer_cov_trace_pc_guard_init and so you can make them consecutive
  // and use them to dereference an array or a bit vector.
  void *PC = __builtin_return_address(0);
  char PcDescr[1024];
  // This function is a part of the sanitizer run-time.
  // To use it, link with AddressSanitizer or other sanitizer.
  __sanitizer_symbolize_pc(PC, "%p %F %L", PcDescr, sizeof(PcDescr));
  printf("guard: %p %x PC %s\n", guard, *guard, PcDescr);
}

把头文件也粘贴进来:

#include <stdint.h>
#include <stdio.h>
#include <sanitizer/coverage_interface.h>

两个方法里面都有extern “C”extern “C”的主要作用是为了能够正确实现C++去调用其他C语言的代码,加上extern “C”就会指示作用域内的代码按照C语言区编译,而不是C++,这个extern “C”在OC项目里没什么用,直接删除

此时还会包一个错误:

Pasted Graphic 8.png

这个__sanitizer_symbolize_pc(PC, "%p %F %L", PcDescr, sizeof(PcDescr));函数没有什么作用,直接删除即可。

三、代码调试

cmd + r运行,此时终端会打印一些信息:

Pasted Graphic 9.png

删除两个函数里面的注释,先注释第二个的内容,然后运行

INIT: 0x1025c5478 0x1025c54f0

这是运行打印得到的地址,就是函数(uint32_t *start, uint32_t *stop)startstop两个指针的地址

stop存储的就是我们工程里面符号的个数

for (uint32_t *x = start; x < stop; x++)
        *x = ++N;

看一下这个for循环,start会先复制给*xx++就是内存平移,按照uint32_t的大小去平移,而uint32_t的定义是typedef unsigned int uint32_t; 是无符号整型,占4个字节,所以每次按4个字节平移。

startstop里面存的是什么,打断点调试:

Pasted Graphic 10.png

先看start:

INIT: 0x1042a5278 0x1042a52e0
(lldb) x 0x1042a5278
0x1042a5278: 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00  ................
0x1042a5288: 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00  ................
(lldb)

由于uint32_t4个字节来存储发现start就是 0 1 2 3 4…,再看stop,由于stop的已经是结束位置,读取的数据是在startstop之间的数据,所以需要向前平移4个字节得到其真实数据。

(lldb) x (0x1042a52e0-4)
0x1042a52dc: 1a 00 00 00 00 00 00 00 00 00 00 00 fe f1 29 04  ..............).
0x1042a52ec: 01 00 00 00 00 00 00 00 00 00 00 00 90 40 2a 04  .............@*.
(lldb)

可以得到1a 就是26,也可以循环外面打印结果: Pasted Graphic 11.png 可以得到:

TraceDemo[16814:301325] 26

也是26个符号。

四、测试验证方法

可以验证一下,添加一个函数:

void test(void) {
    NSLog(@"%s",__func__);
}

符号变成27

TraceDemo[16911:304537] 27

再添加一个block

void (^block) (void) = ^{
    NSLog(@"%s",__func__);
};

符号变成28

TraceDemo[16933:305465] 28

添加一个数据类型属性:

@property (nonatomic ,assign) int age;

由于系统自动生成getter、setter方法,符号变成30

TraceDemo[16975:306816] 30

添加一个对象属性:

@property (nonatomic ,copy) NSString *str;

符号变成33

TraceDemo[17041:308780] 33

对象属性由于ARC,系统自动除了生成getter、setter方法外还生成了cxx_destruct()析构函数

添加一个方法:

- (void)test{
}

符号变成34

TraceDemo[17114:311256] 34

在其他类AppDelegate类中添加一个属性:

@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) NSString *name;
@end

符号变成37:

TraceDemo[17266:316294] 37

符号变成37

结论

这就说明了通过这个方法整个项目里的符号,它都能捕获到。

❌
❌