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: 系统违反规定,发出的一个 信号

相关推荐
坏小虎11 小时前
Expo 快速创建 Android/iOS 应用开发指南
android·ios·rn·expo
光影少年12 小时前
Android和iOS原生开发的基础知识对RN开发的重要性,RN打包发布时原生端需要做哪些配置?
android·前端·react native·react.js·ios
北京自在科技12 小时前
Find My 修复定位 BUG,AirTag 安全再升级
ios·findmy·airtag
Digitally13 小时前
如何不用 USB 线将 iPhone 照片传到电脑?
ios·电脑·iphone
Sim14801 天前
iPhone将内置本地大模型,手机端AI实现0 token成本时代来临?
人工智能·ios·智能手机·iphone
Digitally1 天前
如何将 iPad 上的照片传输到 U 盘(4 种解决方案)
ios·ipad
报错小能手1 天前
ios开发方向——swift并发进阶核心 @MainActor 与 DispatchQueue.main 解析
开发语言·ios·swift
LcGero1 天前
Cocos Creator 业务与原生通信详解
android·ios·cocos creator·游戏开发·jsb
ii_best1 天前
lua语言开发脚本基础、mql命令库开发、安卓/ios基础开发教程,按键精灵新手工具
android·ios·自动化·编辑器
用户223586218202 天前
WebKit WebPage API 的引入尝试与自研实现
ios