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

相关推荐
二流小码农1 分钟前
鸿蒙开发:一个底部的曲线导航
android·ios·harmonyos
2501_915909069 分钟前
如何在 Windows 上上架 iOS App,分析上架流程哪些是不用mac的
android·macos·ios·小程序·uni-app·iphone·webview
2501_915921431 小时前
分析 iOS 描述文件创建与管理中常见的问题
android·ios·小程序·https·uni-app·iphone·webview
专业开发者18 小时前
调试 iOS 蓝牙应用的新方法
物联网·macos·ios·cocoa
tangbin5830851 天前
iOS Swift 可选值(Optional)详解
前端·ios
卷心菜加农炮1 天前
基于Python的FastAPI后端开发框架如何使用PyInstaller 进行打包与部署
ios
北极象2 天前
千问大模型接入示例
ios·iphone·qwen
ipad协议开发2 天前
企业微信 iPad 协议应用机器人开发
ios·企业微信·ipad
QuantumLeap丶3 天前
《Flutter全栈开发实战指南:从零到高级》- 26 -持续集成与部署
android·flutter·ios
2501_915918413 天前
TCP 抓包分析在复杂网络问题中的作用,从连接和数据流层面理解系统异常行为
网络·网络协议·tcp/ip·ios·小程序·uni-app·iphone