iOS-main 入口原理与实现方案

一、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源码调试

  • 源码分析
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:

  1. 环境变量配置
  2. 共享缓存
  3. 主程序的初始化
  4. 加入动态库
  5. link主程序
  6. link动态库
  7. main()

runinit -> do init: libsys -> init -> libdispatch

调用流程:先load -> cxx -> main

查看环境变量的配置:export OBJC_HELP=1

crash: 系统违反规定,发出的一个 信号

相关推荐
开心就好20252 天前
iOS App 安全加固流程记录,代码、资源与安装包保护
后端·ios
开心就好20252 天前
iOS App 性能测试工具怎么选?使用克魔助手(Keymob)结合 Instruments 完成
后端·ios
zhongjiahao3 天前
面试常问的 RunLoop,到底在Loop什么?
ios
wvy4 天前
iOS 26手势返回到根页面时TabBar的动效问题
ios
RickeyBoy4 天前
iOS 图片取色完全指南:从像素格式到工程实践
ios
aiopencode5 天前
使用 Ipa Guard 命令行版本将 IPA 混淆接入自动化流程
后端·ios
二流小码农5 天前
鸿蒙开发:路由组件升级,支持页面一键创建
android·ios·harmonyos
iceiceiceice6 天前
iOS PDF阅读器段评实现:如何从 PDFSelection 精准还原一个自然段
前端·人工智能·ios
ssshooter7 天前
Tauri 踩坑 appLink 修改后闪退
前端·ios·rust
二流小码农7 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos