问题一:load办法在什么时分调用?

答:在load_images内部调用load办法。

  • 收集类的load办法 到loadable_classes实例化目标是什么意思
  • 收集分类的load办法增加到loadable_categories表,
  • 调用call_load_methods,内部调用两张load表里的load办法。

(参阅文章:[iOS底层剖析之应用程序加载流程])

问题二:load_images在什么时分调用?

答:在dyly检测到新的镜像文件的时分,dyly会调用load_images。

问题三:load办法和initalize办法哪个先调用?

答:l实例化目标oad办法更早。

  • load办法是在dyly链接镜像文件的时分就会去调用load_images,继而load_images会调用全部类和分类的load办法。

  • initalize是在榜首次给政策发送消息的时分进行初始化调用。

问题四:load、initalize、C++这三个自起函数调用的三种办法(主动调用)办法哪个先调用?

答:看C++界说在哪里。

  • C++办法假定写在objc工程里,那么实施次序:C++办法 –变量提高> load办法 -> initalize办法

  • c++办法假定写在自己工程里,那么实施次序:load办法 -> C++办法 -&ios手游下载渠道gt; initalize办法

变量与函数偿:关于objc工程里的c++函数调用,在objc源码查找变量提高_o指针bjc_init,能够看到内部调用了static_init函数。stati函数调用句子c_init函数是对C++函数静态函数的初始化

问题五:runtime是什么?它是底层吗?

答:runtime它是变量min表明什么类型的变量一种作业时机制,是由C或C++汇编写成的一套API,为OC面向政策作业时的一种功能,它不是底层ios模拟器。场景的运用场景:

  • Category分类特征、办法的加载;

  • addMethod(动态增加办法);

runtime的存在,让类的特征、办法在编译之后,还能够动态的增加绑定到类上。

问题六:能否向编译后的得到的类中增加实例变量?能否向作业时创立的类中增加实例变量?

答:不能向编译后的得到的类中增加实例变量;只需没注册到内存的类就还变量类型有哪些能够增加。

  • 变量提高译好的实例变量存在于roios14.4.1更新了什么中,一函数调用能够作为一个函数的形参旦编译结束,内存结构就完全供认,无法更实例化一个类改。
  • 关于向作业时创立的类中增加实例变量,这边有个案例和推导能够看下:

咱们作业时创立类,代码是这样的:

Class Person = objc_allocateClassPair(NSObject.class, "Person", 0);
class_addIvar(Person, "_name", sizeof(函数调用中的参数太少NSString *), log2(sizeof(NSString *)), "@");
obj实例化需求c_registerC函数调用lassPair(Person);

首要用到了3个函数:

  • objc_allocateClassPair(获取成员变量Ivar
  • class_addIvar(动态增加成员变量
  • objc_regisios是什么意思te实例化需求rClassPai变量min表明什么类型的变量r(注册clas实例化需求s

并且「class_addIvar」的实施是在objc_allocateClas实例化目标的要害字sPair之后在objc_registerClassPair之前。至于为什么是这样,咱们能够经过源码来剖析一下:

首要看下oios1471值得更新吗bjc_alloc变量英文at实例化目标eClassPair干了什么?
Class objc_allocateClassPair(Class superclass, const char *name,变量是什么意思
              size_t extraB实例化目标有几种办法ytes)
{
...
objc_initializeClassPair_internal(superclassios1471值得更新吗, name, cls,函数调用句子func meta);
...
}

进入objc_initializeClassPair_internal指针数组

static void objc_initializeClas指针数学sPair_internal(Class superclass, const char *name, Class cls, Class meta)
{
...
cls_rw_w->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REAL函数调用句子IZED | RW_REALIZING;
meta_rw_w->flags = R变量W_CONSTRUC变量min表明什么类型的变量TING | RW_COPIED_RO | RW_REALIZED | RW_REALIZioslauncherING | RW_META;
...
}实例化目标的要害字

咱们能够看到,ob实例化需求jc_allocateClassPa函数调用句子ir对flag符号为RW_CONSTRUCTING。

看下class_addIvar干了什变量英文么?
BOOL
class_addIvar(Class cls, const char *name, size_t size,
ui指针nt8_t alignme指针和引证的差异nt, const char *type)
{
if (!cls) reios手游下载渠道turn NO; // 非空判别
...
if (cls->isMetaClass()) {实例化类// 是元类就不能增加
return NO;
}
// 假定类现已函数调用数组分配但未注册,就越过判别
if (!(cls->data()-&ios8备忘录gt;flags & RW_CONSTR实例化UCTING)) {
return NO;
}
// 假定ro函数调用句子funcios模拟器面现已存在这个tI函数调用数组var指针万用表的运用办法,就变量英文越过判别指针数学
if ((n函数调用句子ame  &&  getIvar(cls, name))  ||  size > UINT32_MAX) {
return NO;
}
...
}
/** 以上几个判别,说明满足以下几个条件才能够增加实例变量:
**
* 1、cls有值
* 2、cls不是元类
* 3、类现已分配但未注册(变量的界说要害指针舆情帮手)
* 4、指针数组ro里边不存在这个ioslaunchertIvar
*/

咱们要重视的点在这儿:c实例化ls->data()->flags 什么时分发生改动?

看下objc_registerClassPair干了什么?
void objc_registerClassPair(Cios手游下载渠道lass cls)
{
...
// cls已分配且已注册 或 cls的ISA指向已分配且已注册,就回来
if ((cls->data()->flags & RW_CONSTRUC实例化一个类TED)  ||
(cls->ISA()->data()->flags & R函数调用进程W_CONSTRUCTED))
{
_objc_inform("objc_registerClassPair: class '%s'变量名的命名规则 was alrea指针万用表测漏电dy "
"registered!", cls->data()->ro()->getName());
return;
}
//(非)cls已分配但未注册 或 (非)cls的ISA指向已分配但未注册,就回来
if (!(cls->data()->flags & RW_CONSTRUCTING)  ||
!(cls-变量>ISA()-&指针万用表的运用办法gt;data()->flags & RW_CONSTRUCTING))
{
_objc_infor指针数组m("objc_registerClassPair: class '%s' was not "
"allocated with objc_allocateClassPair!",
cls->data()->ro()->getName());
return;
}
/// 将状况改实例化数组为RW_CONSTRUCT指针式万用表ED,且铲除之前的符号
cls->ISA()->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
cls->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCT函数调用进程ING | RW_REALIZING);
...
}

所以,要实例化需求实施objc_registerClassPair,需求满足已分配但未注册

objc_registerClassPair函数一旦实施,flag符号就变成了RW_CONSTRUCTED,天然就不能再实施class_addIvar。

总结:
  • objc_allocateCios8备忘录lassPair(flag设置为RW_CONSTRUCTING
  • class_addIvar(实施的条件之一是flag为RWiOS_CONSTRUCTING
  • objc_regist变量与函数erClassPair(flag设置为RW_CONSTRUCTED

问题七: [self cla实例化目标ss] 和 [super class]的差异及原理是什么?

答:首要,咱们写个查验案例打实例化类印一下:

///实例化servlet类反常 DirectionChilios模拟器d.m内部代码:
- (in变量名的命名规则sta变量min表明什么类型的变量ncetype)init
{
self = [super init];
if (sel实例化一个类f)ios体系 {
NSLog(@"检查[self class] 和 [super class]差异---------%@->%@",[self class],[super class]);
}
return self;
}
/// main函数
int main(int argc, const char * argv[])ios模拟器 {
DirectionChildios手游下载渠道 *childClass = [[DirectionChild alloc] init];
}

OC底层面试题(一)附解题思路

咱们发现两指针万用表的运用办法个打印的成果相同,这是为什么呢??

OC底层面试题(一)附解题思路

回到objc源码工程,command+ios8备忘录单机进入class办法:

OC底层面试题(一)附解题思路

检查object_getClass

Class object_getClass(id obj)
{实例化servlet类反常
if (obj) return obj->getIsa();
else return Nil;
}

所以榜首个self打印DirectionChild就显而易见了。

接下来看[super class]函数调用的三种办法,

super不知道来自哪里,咱们经过clang看一下编译后的源码是什么样的。指令如下:

  • c函数调用不能够d到指定文件的上层文件夹目录
  • $xcrun -sdk i函数调用能够作为一个函数的形参phoneos clang -arch arm64 -rewrite实例化数组-objc DirectionChild实例化类.m

翻开得到的DirectionChil实例化servlet类反常d.mm,为了ios1471值得更新吗快速定位,查找「DirectionChild」:

OC底层面试题(一)附解题思路

提取[super class]部分代码:

((Class (*)(__rw_objc_super *, SEL))(void *)objc_msg函数调用栈SendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_ge指针式万用表tClass("DirectionChild"))}, sel_registerName("class"));

咱们发现[super class]实践实例化数组调用的是objc_msgSendSuper,

接下来,咱们经过Debug查ios手游下载渠道看下作业时的实践状况:
OC底层面试题(一)附解题思路

OC底层面试题(一)附解题思路

当时作业的是mac工程,那么真机作业下,是否也是这样实例化是什么意思呢?
这儿,咱们新建了一个查验工程,作业看作用:

OC底层面试题(一)附解题思路

OC底层面试题(一)附解题思路

看到这儿是不是有点懵逼,clang静态编译的时分调用objc_msgSendSuper动态作业的时分调用iOS的居然是o实例化类bjc_msgSendSuper函数调用能够作为一个函数的形参2

为了根究这个改动,咱们

objc源码工程全局查找objios是什么意思c_msgSendSuper

OC底层面试题(一)附解题思路

当时文件查找L_objc_msgSendSuper2_body,发现它会进入_objc_msgSendSuper2里边:

OC底层面试题(一)附解题思路

回到前面的问题,[super class]打印:

((Class (*)(__rw_objc实例化目标是什么意思_super *, SEL))(void *)objc_msgSendSuper)指针数学((__rw_objc_super){(id)self,
(id)class_getSuperclass(objc_getClass("DirectionChild"))},
sel_registerName("class"));

简化一下代码:

((void *)objc_msgSendSuper)((__rw_objc_super){
(id)self,
(id)class_getSuperclass(objc_getClass("DirectionChild"))
},sel_registerName("class指针数组"));

榜首个参数是se变量名的命名规则lf,咱们前面剖析过,cla变量英文ss终究回来的是classios体系函数榜首个ios8备忘录参数的isa指向,se实例化需求lf的isa指向是DirectionChild类,所以[self class][super class]都打印DirectionChild

问题七: 一般咱们在给一个函数调用数组类写init初始化办法的时分,都会写sel实例化目标有几种办法f=[super init],这是为什么?

答:这样写是为了更好的承继父类界说的一些公共特征、办法、协议等等。

问题八:请问下面main函数作业会报错吗?

// Direction.h
#import <Foundation/Foundation.h>
@interface Direction : NSObject
@property (nonatomic , stron变量与函数g) NSString *hobby;
@end
// Direction.m
@implementation Direction
- (void)ios1471值得更新吗run{
  NSLog(@变量的界说"run faster.");
}
@end
// main.m
int main(int aios15rgc, const char * argv[]) {
Dire指针数学ction *dt = [Directio变量与函数n变量与函数 alloc];
[dt run];
Class clsiOS = [Direction class];
void *ssj = &cls;
[(指针数学id)ssj run];
return 0;
}

作业代码:

OC底层面试题(一)附解题思路

指针c语言指针万用表的运用办法视频教程证明,能够正常作业,控制台也打印出来了东西。那么,为什么可ioslauncher以不经过实例化政策就调用类的实例办法呢?

  1. 首要,咱们要知道,办法的调用本质上就消息的发送objc_msgSend
  2. objc_msgSend榜首个参数是recever消息接收者,经过它找到Class类
  3. 然后再从Class类的data里,经过指针舆情帮手objc_msgSend的第二个参数SEL去找到对应的IMP并调用。
  4. dt作为一个Direction实例政策,经过它的isa能够找到Direction类
  5. ssj指针指向的就函数调用句子是Direc变量英文tion类地址,能够找到ios是什么意思Direct实例化目标有几种办法ion类
  6. 所以关于objc_msgSend来说,只需能找到Class类,榜首个参数是dt或ssj都能够。
  • 有关实例办法存放于类ios1471值得更新吗里边,能够检查文章:isa剖析之类的根究(上)

  • 有关objc_msgSend的完成进程,能够查变量类型有哪些看文章:iOS底层剖析之类的根究-cache之 insert、objc_msgSend

下面是「dt」和「ssj」指向Direction类

OC底层面试题(一)附解题思路

OC底层面试题(一)附解题思路

拓展:对run办法进行改造,检查打印的数据。

// Direction.m
@implementation Direction
- (void)run{
  NSLog(@"run faster.->%@",self.hobby);
}
@end

作业:

OC底层面试题(一)附解题思路

  • 榜首个打印null没什么疑问,因实例化数组为没有对hobby赋值;

  • 第二个为什么打印的是Direction类呢?

答:dt作为一个实例政策,alloc拓荒了空间,它有指针地址、也有内存。而ssj就是一个朴素指向Direction类地址的指针,体系并没有为它拓荒内存。因而函数调用栈hobby指向ssj指针地址的下一个地址(栈帧里的如栈次序,也就是dt)。

ioslauncher们来经过控制台打印,看下数据的改动:

OC底层面试题(一)附解题思路

然后在Directionrun办法下断点:

OC底层面试题(一)附解题思路

OC底层面试题(一)附解题思路

这时分,咱们有个指针舆情帮手

  • 经过类地址指针ssj拜访类的特征,得到的是数据或许跟ssj指针地址栈帧有联络。

为了验证这个猜想,咱们对代码进行了增加:

// Direction.h
#import变量提高 <Foundation/Foun函数调用的三种办法dation.h>
@interface Direction : NSObject
@pr指针和引证的差异operty (nonatomic , strong) NSString *firendNames;
@property (nonatomic , strong) NSString *firendNames2;
@p实例化目标有几种办法roperty (nonatomic , strong) NSString *hobby;
@end
//ios15 ViewController.m
- (void)viewDi函数调用句子funcdLoad {
[supios手游下载渠道er viewDidLoa变量是什么意思d];
Direction *dt = [Direction alloc];
[dt run];
/// 增加3个实例变量zo1、zo2、zo3指针万用表的详细运用
Zoon *zo1 = [Zoon new];
Zoon *zo2 = [Zoon new];
Zoon *zo3 = [Zoon new];
Class cls = [Direction c变量泵lass];
void *ssj = &cls;
[(__bridge id)ssj run];
}
@end

持续重复上面的控制台打印:

OC底层面试题(一)附解题思路

Directionrun办法下断点:

OC底层面试题(一)附解题思路

打印成果验证了上面的猜想:

  • 经过类地址指针ssj拜访类的特征,得到的是数据跟ssj指针地址栈帧有联络,依照入栈次序打印。

那么,dt实例政策是怎样拜访hobby特征的?实例化是什么意思

答:文章 iOS底层完成剖析之政策的本质讲到过,经过self首地址平移OBJC_IVAR_XXXX_X指针舆情帮手XXX_xxx找到特征的地址。

问题九:请问以下代码的压栈次函数调用第是怎样样的?

结构体压栈
- (vo函数调用不能够id)vi指针万用表的运用办法视频教程ewDidLoad {
[super viewDidLoad];
/// 查验刺进ios1471值得更新吗次序
Direction *dt实例化目标的要害字1 = [Direction alloc];
struct SSJStru指针数组ct sSJStruct = {@1,@22};
Direction *dt2 = [Direction alloc];
NSLog(@"查验结束"指针数学);
}

答:

  • dt1、sSJStruct、dt2,以此由高地址向低地址压栈。

  • sSJStruct内部,低地址向高地址压ios体系栈。

作业代码:

OC底层面试题(一)附解题思路

参数压栈
// 自界说函数
int ssj_fun(id obj1,ios体系id obj2){
return 0;
}
- (void)viewDidLoad {
[super viewDidLoad];
/// 查验参数压栈
Direction *dt1 = [Direction alloc];
Direction *dt2 = [Direction alloc];
ssj_fun(dt1, dt2);
NSLog(@"查验结束");
}

变量之间的联系断点,作业代码:

OC底层面试题(一)附解题思路

分别打印dt1、dt2、obj1、obj2,其间dt1、dt2是指针地址,obj1、obj2是值的地址。
根据栈的压栈实例化类从高到低次序,压函数调用的三种办法栈次序先obj1再obj2。

问题十:[super viewDidLoad] 里的super传的是当时Controllioslauncherer仍是当时Controller函数调用栈的父类?

// ViewController.m
- (void)viewDidLoad {
  [super viewDidLoios模拟器a实例化类d]
// 实践作业会调用函数  o函数调用中的参数太少bjc_msgSendSuper2
// objc_msgSendSuper指针数学2界说如下
objc_msgSendSuper2(struct objc_super * _Nonnull supe实例化目标是什么意思r, SEL _Nonnull op, ...)
}

那么,这儿的super传的是ViewController仍是UIViewControll实例化类er?
带着这个疑问,咱们准备实践操作打印一下:

OC底层面试题(一)附解题思路

下符号断点objc_msgSendSuper2

OC底层面试题(一)附解题思路

输入reios1471值得更新吗gister read检查寄存器

OC底层面试题(一)附解题思路

控制台,打印x0,即榜首函数调用个参数的数据:

OC底层面试题(一)附解题思路

代码:

链接: pan.baidu.com/s/1uPGdFij0…
暗码: il7h

–来自百度网盘超级会员V2的共享