了解了Runtime函数含义,我们就可以直接使用Runtime的API了,那接下来继续探究Runtime的源码,经过源码分析来更加深刻的了解Runtime原理。
开发应用都知道Runtime很重要,但是有很多小伙伴根本不了解,或者只是知道可以替换方法啊、可以交换两个方法的调用,项目中也用不到,
从进入iOS开始,写了无数个[[objc alloc] init]
,这个到底在干嘛?初始化和init?alloc和init到底做了什么?
Person *person = [Person alloc]; Person *person1 = [person init]; Person *person2 = [person init]; NSLog(@"%p-----%p------%p", person, person1, person2);
这里会输出什么呢?
0x10102e1a0-----0x10102e1a0------0x10102e1a0
来,让我们断点看下,alloc
和init
是怎么调用的
我们看到调用alloc
和init
都调起了objc_msgSend
,接下来跟着符号断点走
进入libobjc
库的dylib之后走+[NSObject alloc]
方法,指针调起_objc_rootAlloc
,进入_objc(会者定离一期一祈是啥意思?会者定离,一期一祈是指经常会面的人必有离散之时。意思是说世事无常,没有不散的宴席。这两句话总的来说,就是说世事无常,当珍惜遇见的人,把和每个人相遇,当作一生只有一次的缘分。)_rootAlloc
方法,继续调起callAlloc
,通过寄存器,可以看到alloc已经通过类创建实例对象
init
按照同样方法 依然可以通过汇编看出方法调用顺序,可以用真机进行测试并打印
当新的对象被创建时,其内存同时被分配,实例变量也同时被初始化。对象的第一个实例变量是一个指向该对象的类结构的指针,叫做 isa。通过该指针,对象可以访问它对应的类以及相应的父类。在 Objective-C 运行时系统中对象需要有 isa 指针,我们一般创建的从 NSObject 或者 NSProxy 继承的对象都自动包括 isa 变量。接下来看下对象被创建的过程
首先,我们通过clang命令
$$ xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o testMain.c++
也可以用clang -rewrite-objc main.m -o test.c++
命令,只不过会有很多警告、代码会更长(大概9万多行)。
编译main函数中的OC代码为C++代码
int main(int argc, const char * argv[]) { @autoreleasepool { Person *p = [[Person alloc] init]; [p run]; } return 0;}
编译后多一个testMain.c++文件,打开后在代码最后面会发现我们的main函数
int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init")); ((void (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("run")); } return 0;}
可以看出,我们的方法调用会编译成objc_msgSend,
person对象由此还会发现对象的本质其实就是一个结构体
下层通讯(通过源码查看objc_msgSend内部实现)首先我们到苹果open source官网下载最新源码
在方法调用的时候,会发送objc_msgSend
消息,objc_msgSend
会根据sel找到函数实现的指针imp,进而执行函数,那sel是如何找到imp的呢?objc_msgSend
在发送消息时候根据sel查找imp有两种方式
bits中包含各种数据,cache(每个类都有一个)用来存储方法select和imp,select和imp会以哈希表形式存在objc_msgSend
在快速查找的时候,就是通过汇编查找objc_class中的cache,如果找到则直接返回,否则通过C的lookup,找到后再存入cache
首先调用objc_msgSend
会走到ENTRY
先判断p0检查是否为空和tagged pointer(特殊类型)判断,调用LNilOrTagged
进行isa处理,通过isa找到相应类class,最后调用LGetIsaDone
来执行CacheLookup
在缓存中查找imp,如果查找到直接调起imp否则调起objc_msgSend_uncached,objc_msgSend_uncached有两种情况
首先,第一个是CacheHit,直接调起imp,第二个是CheckMiss,之后调用objc_msgSend_uncached,第三个就是add,下面是CacheHit和CheckMiss的宏
CacheLookup macro那如果在缓存中没有查找到imp,调起objc_msgSend_uncached
,在方法列表中找到imp之后再TailCallFunctionPointer
调起imp
STATIC_ENTRY __objc_msgSend_uncached UNWIND __objc_msgSend_uncached, FrameWithNoSaves // THIS IS NOT A CALLABLE C FUNCTION // Out-of-band p16 is the class to search MethodTableLookup // 方法列表中找到imp TailCallFunctionPointer x17
重点:MethodTableLookup是怎么操作的
小知识点:通过method list查找method,下面是method_t的结构,method其实是一个哈希表,sel和imp是键值对
struct method_t { SEL name; const char *types; // 参数类型 MethodListIMP imp; struct SortBySELAddress : public std::binary_function<const method_t&, const method_t&, bool> { bool operator() (const method_t& lhs, const method_t& rhs) { return lhs.name <
扫码加微信详细咨询太和智慧养老产品和平台服务!
版权声明:
---------------------------------------------------------------
所有信息来源于互联网,本文的版权归原作者所有,不代表本网观点和立场。
本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请发送邮件至 81480447@qq.com 举报,一经查实,本站将立刻删除。
扫码加微信详细咨询太和智慧养老产品和平台服务!
养老资讯
- 肾病综合征患者在饮食上要
- 肾病综合征患者不能吃什么
- 提醒肾病患者应当注意的饮
- 饮食上面肾病患者要注意什
- 为肾病患者介绍几种膳食帮
- 您了解肾病患者在饮食上要
- 作为一名肾病患者在饮食上
- 年轻的肾病患者需要注意哪
- 介绍一下肾病患者的饮食禁
- 为你盘点一下肾病患者的饮
- 不是所有肌肤都适合去角质
- 快速消除眼袋我有绝招
- 23岁用什么护肤品好,教
- 5种保湿小妙招摆脱冬季干
- 怎么才能紧致松垮下垂脸
- 如何用简单护理打造完美面
- 怎么才能让黑头见光死
- 告别干燥打造水润美唇
- 房事后男人为什么倒头就睡
- 教你怎么正确使用避孕套
- 辨别男性保健品优劣的妙招
- 压力男喜欢丰满女
- 恋爱中谁会付出多一些
- 男人你就是要让她爽
- 切记房事不宜过早
- 男人千万不要做女人讨厌的
- 你知道女人乳房里的七大秘
- 不当的性生活会引起哪些性
- 专家揭秘每周两次性爱并不
- 夫妻间的激情碰撞听听男人
- 容易影响性爱成功的几点误
- 不可不防的女性“性爱焦虑
- 注意维护男性健康保持性爱
- 性爱安全女性常用避孕方式
- 丰满女人的那些特点吸引男
- 淡彩润唇膏选择大揭秘春天
- 用简单彩妆打造无辜萌妹子
- 如何化出明艳彩妆
- 几招教你玩转唇膏颜色选择
- 夫妻情色让生活更有情趣
助老机构介绍
姓名:
年龄:
电话: