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

相关推荐
missmisslulu10 小时前
电容笔值得买吗?2024精选盘点推荐五大惊艳平替电容笔!
学习·ios·电脑·平板
GEEKVIP11 小时前
手机使用技巧:8 个 Android 锁屏移除工具 [解锁 Android]
android·macos·ios·智能手机·电脑·手机·iphone
GEEKVIP11 小时前
如何在 Windows 10 上恢复未保存/删除的 Word 文档
macos·ios·智能手机·电脑·word·笔记本电脑·iphone
奇客软件12 小时前
iPhone使用技巧:如何恢复变砖的 iPhone 或 iPad
数码相机·macos·ios·电脑·笔记本电脑·iphone·ipad
奇客软件2 天前
如何从相机的记忆棒(存储卡)中恢复丢失照片
深度学习·数码相机·ios·智能手机·电脑·笔记本电脑·iphone
GEEKVIP2 天前
如何修复变砖的手机并恢复丢失的数据
macos·ios·智能手机·word·手机·笔记本电脑·iphone
一丝晨光2 天前
继承、Lambda、Objective-C和Swift
开发语言·macos·ios·objective-c·swift·继承·lambda
GEEKVIP2 天前
iPhone/iPad技巧:如何解锁锁定的 iPhone 或 iPad
windows·macos·ios·智能手机·笔记本电脑·iphone·ipad
KWMax2 天前
RxSwift系列(二)操作符
ios·swift·rxswift
Mamong3 天前
Swift并发笔记
开发语言·ios·swift