一、alloc 实现原理(真机调试)
ini
// alloc 原理
MTObject *objc1 = [MTObject alloc];
MTObject *objc2 = [objc1 init];
MTObject *objc3 = [objc1 init];
NSLog(@"%@--%p", objc1, &objc1);
NSLog(@"%@--%p", objc2, &objc2);
NSLog(@"%@--%p", objc3, &objc3);
//shell
MainDemo[11549:304677] <MTObject: 0x600000280290>--0x7ffee5468138
MainDemo[11549:304677] <MTObject: 0x600000280290>--0x7ffee5468130
MainDemo[11549:304677] <MTObject: 0x600000280290>--0x7ffee5468128
调试方法 - libobjc.A.dylib`+[NSObject alloc]
- 下符号断点 - objc_alloc
- 对象alloc 直接 control + in 一步一步进入
- alloc 符号断点 objc_alloc
- 通过汇编
arduino
//lldb 读取寄存器的地址。
- register read x0
- x/4xg p
二、配置objc源码
- 下载objc源码 Apple open source
- 具体配置可以参考KC的博客 掘金
三、objc源码调试
- 源码分析
arduino
+(id)alloc
id _objc_rootAlloc(Class cls)
static id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
static id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
- 字节对齐:好处便于读取
-
- 影响因素:属性;
c
size_t size = instanceSize
alignedInstanceSize()
word_align() // 字节对齐
// 源码 - 8字节对齐
#define WORD_MASK 7UL
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
// 源码 - 16字节对齐
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
- size:
-
- 对象需要的内存空间是8的倍数 - 8字节对齐;
- 至少是16个字节
对象的本质
ini
MTPerson *person = [[MTPerson alloc] init];
person.name = @"MT";
person.age = 18;
person.height = 180;
person.hobby = @"男";
//lldb 输出
(lldb) x person
0x100945220: ed 82 00 00 01 80 1d 01 30 40 00 00 01 00 00 00 ........0@......
0x100945230: 00 00 00 00 00 00 00 00 12 00 00 00 00 00 00 00 ................
- x 【指针】: 输出对象以16进制输出
(lldb) po 0x100945220
<MTPerson: 0x100945220>
(lldb) po 0x011d8001000082ed
80361110145893101
(lldb) p 0x011d8001000082ed
- po 输出对象;栈顶指针指向isa,直接输出的是对象。
- iOS是小端需要从后往前读;
- 读的是地址的结构;获取isa还需mask
(lldb) x/4xg person
0x100945220: 0x011d8001000082ed 0x0000000100004030
0x100945230: 0x0000000000000000 0x0000000000000012
- x/4xg 【指针】4段、xg每字节读一次内存
(lldb) x/8xg person
0x100945220: 0x011d8001000082ed 0x0000000100004030
0x100945230: 0x0000000000000000 0x0000000000000012
0x100945240: 0x00000000000000b4 0x0000000000000000
(lldb) po 0x011d8001000082ed
80361110145893101
(lldb) po 0x0000000100004030
MT
(lldb) po 0x0000000000000012
18
(lldb) po 0x0000000000000000
<nil>
(lldb) p 0x0000000000000012
(int) $9 = 18
- init : 直接返回self;【工厂设计】
- new : [calloc init];不建议直接使用
四、alloc流程图

- NSObject - 直接走 objc_alloc
- MTPerson - 会走两次 alloc、任何一个继承NSObject的子类都会默认走一遍、完成SEL的处理
五、debug、release 优化

- fast smallest
六、结构体
- 指针是8个字节
- 结构体是根据成员决定的
- 结构体内存对齐,整数倍
- 内存优化;地址从前排到后的
- sizeof计算类型
- 结构体是可以伪继承
- union 联合体更省内存一些

七、lldb 调试
- p 直接打印指针
- po description 方法
- regitster read 读寄存器的地址
- x/4xg 读连续4段地址,从对象的栈顶开始
- x = memory read
- bt 查看调用堆栈信息
八、查看源码技巧
- 查看关键代码、忽略 if
- 优先判断主流程
九、clang编译cpp文件
clang -rewrite-objc main.m -o main.cpp
struct **MTObject_IMPL {
**struct **NSObject_IMPL NSObject_IVARS;
};
对象在底层都是结构体
- NSObject_IVARS = isa
- set方法 objc_setPro 工厂设计的适配器模式- retain新值,release旧值。
- char+位域 - 存储降低内存
**
十、isa结构
- nonpointer :表示isa 是否开启指针优化;0 - 纯isa指针;1 - 不止是类对象地址,isa汇总包含了类信息,对象信息;
- has_assoc : 关联对象标志,0没有,1存在
- has_cxx_dtor: 该对象是否有C++或者objc的析构器,如果有析构函数,则需要逻辑处理,如果没有就更快的释放对象。
- shiftcls: 存储类指针的值。开启指针优化的情况下,在arm64架构中有33位用来存储类指针。
- magic:用于调试器判断当前对象是真的对象还是没有初始化空间
- weakly_referenced:标志对象是否被只想或者曾只想一个arc的弱变量,没有弱引用的对象可以更快释放
- deallocting:标志对象是否正在释放内存
- has_sidetable_rc:当对象引用计数大于10时,则需要借用该变量存储进位
- extra_rc:当表示该对象的引用计数值,实际引用计数值减1.
当前的isa关联了指针和类
isa.bits & MASK_ISA 就是当前类的信息

- 实例对象 -> 对象 -> 元类 -> 根源类(NSObject)
- 实例之间没有继承关系;只有对象才有继承关系。
- NSObject根元类继承NSObject;NSObject继承为nil。
- 对象、类、元类都有isa
十一、objc_class(Class) VS objc_objcet(根类)
- struct **objc_class : objc_object;
- objc_objcet(C、C++)
- lldb调试
ini
(lldb) p MTPerson.class
(Class) $0 = MTPerson
(lldb) x/4xg $0
0x100008358: 0x0000000100008330 0x000000010036a140
0x100008368: 0x00000001006449f0 0x0001803c00000003
(lldb) p (class_data_bits_t *)0x100008378
(class_data_bits_t *) $1 = 0x0000000100008378
(lldb) p $1->data()
(class_rw_t *) $2 = 0x00000001006449a0
(lldb) p *$2
(class_rw_t) $3 = {
flags = 2148007936
witness = 1
ro_or_rw_ext = {
std::__1::atomic<unsigned long> = {
Value = 4295000184
}
}
firstSubclass = nil
nextSiblingClass = NSUUID
}
(lldb) p $3.methods()
(const method_array_t) $4 = {
list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
= {
list = {
ptr = 0x00000001000080c0
}
arrayAndFlag = 4295000256
}
}
}
(lldb) p $4.list
(const method_list_t_authed_ptr<method_list_t>) $5 = {
ptr = 0x00000001000080c0
}
(lldb)
- 成员变量和属性是有区别的。
-
- 属性自动生成setter、getter方法
- 属性,带_的成员变量
- 实例变量:特殊的成员变量
- typeEncding apple typeEncding
- 对象方法存在类里;类方法存在元类里。
class_getMethodImplementation
获取不到imp,转发_objc_msgForward
class_rw_t
运行时添加的方法将会存储在运行时生成的rw中。(read wirte)class_ro_t
主要存储编译期已经确定的属性、方法、协议,里面没有分类的方法。ro(read only)
lua
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
// 类方法的isKindOfClass 是找的类的ISA的superclass;类 vs 元类
// [[NSObject class] isKindOfClass:[NSObject class]]; // true 根元类的父类是NSObject
// [[MTPerson class] isKindOfClass:[MTPerson class]]; // false MTPerson 元类是根元类
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
// 实例方法的isKindOfClass 是找的类的class的superclass;类 vs 父类
// [[NSObject alloc] isKindOfClass:[NSObject alloc]]; // true NSObject的superclass为nil
// [[MTPerson alloc] isKindOfClass:[MTPerson alloc]]; // true MTPerson的superclass为NSObject
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
// [[NSObject class] isMemberOfClass:[NSObject class]]; // false
// [[MTPerson class] isMemberOfClass:[MTPerson class]]; // false
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
// [[NSObject alloc] isKindOfClass:[NSObject alloc]]; // true
// [[MTPerson alloc] isKindOfClass:[MTPerson alloc]]; // true
十二、cache_t: 主要用于存储属性、方法
cache_t
_maskAndBucket
_mask
_flags
_occupied: 相当于一个计数器
bucket_t:存储方法的值
_imp
_sel
ini
(lldb) p/x [MTPerson class]
(Class) $0 = 0x0000000100008368 MTPerson
// p/x 以16进制输出地址
(lldb) p (cache_t *)0x0000000100008378
(cache_t *) $1 = 0x0000000100008378
// 根据objc_class源码分析 $0 偏移0x10正好是 cache_t 的地址,可以直接打印
(lldb) p *$1
(cache_t) $2 = {
_bucketsAndMaybeMask = {
std::__1::atomic<unsigned long> = {
Value = 4310866528
}
}
= {
= {
_maybeMask = {
std::__1::atomic<unsigned int> = {
Value = 3
}
}
_flags = 32828
_occupied = 1
}
_originalPreoptCache = {
std::__1::atomic<preopt_cache_t *> = {
Value = 0x0001803c00000003
}
}
}
}
(lldb)
(lldb) p $1.buckets()
(bucket_t *) $4 = 0x0000000100f29a60
Fix-it applied, fixed expression was:
$1->buckets()
(lldb) p $1.buckets()[0]
(bucket_t) $5 = {
_sel = {
std::__1::atomic<objc_selector *> = "" {
Value = ""
}
}
_imp = {
std::__1::atomic<unsigned long> = {
Value = 3509320
}
}
}
Fix-it applied, fixed expression was:
$1->buckets()[0]
十三、objc_msgSend()
- 关闭严厉的检查机制

- objc_msgSend -> 二分查找自己 -> cache_fill -> objc_msgSend
- 实例方法对于元类是实力方法;类方法对于元类也是实例方法;
- CacheLoopUp 快速查找
- 慢速查找流程:loopUpImp
-
- 找自己的methodList
- 找父类的methodList
- imp: forward
- 消息处理机制
- 动态方法决议
-
-
- 或者用IDA、hopper反编译看源码coreFountion
extern void instrumentObjcMessageSends(BOOL flag);
-
十四、编译过程

dyld:
- 环境变量配置
- 共享缓存
- 主程序的初始化
- 加入动态库
- link主程序
- link动态库
- main()
runinit -> do init: libsys -> init -> libdispatch
调用流程:先load -> cxx -> main
查看环境变量的配置:export OBJC_HELP=1
crash: 系统违反规定,发出的一个 信号