问题一: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函数调用句子func里ios模拟器面现已存在这个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];
}
咱们发现两指针万用表的运用办法个打印的成果相同,这是为什么呢??
回到objc源码工程,command+ios8备忘录单机进入class办法:
检查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」:
提取[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手游下载渠道看下作业时的实践状况:
当时作业的是mac工程,那么真机作业下,是否也是这样实例化是什么意思呢?
这儿,咱们新建了一个查验工程,作业看作用:
看到这儿是不是有点懵逼,clang静态编译
的时分调用objc_msgSendSuper
,动态作业
的时分调用iOS的居然是o实例化类bjc_msgSendSuper函数调用能够作为一个函数的形参2
。
为了根究这个改动,咱们
objc源
码工程全局查找objios是什么意思c_msgSendSuper
:
当时文件查找L_objc_msgSendSuper2_body
,发现它会进入_objc_msgSendSuper2里边:
回到前面的问题,[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;
}
作业代码:
事指针c语言实指针万用表的运用办法视频教程证明,能够正常作业,控制台也打印出来了东西。那么,为什么可ioslauncher以不经过实例化政策就调用类的实例办法呢?
- 首要,咱们要知道,
办法的调用
本质上就是
消息的发送objc_msgSend
。 - objc_msgSend榜首个参数是
recever
消息接收者,经过它找到Class类
。 - 然后再从Class类的data里,
经过指针舆情帮手
objc_msgSend的第二个参数SEL
去找到对应的IMP
并调用。 -
dt
作为一个Direction实例政策,经过它的isa能够找到Direction类
。 -
ssj
指针指向的就函数调用句子是Direc变量英文tion类地址,能够找到ios是什么意思Direct实例化目标有几种办法ion类
。 - 所以关于objc_msgSend来说,只需能找到Class类,榜首个参数是dt或ssj都能够。
-
有关实例办法存放于类ios1471值得更新吗里边,能够检查文章:isa剖析之类的根究(上)
-
有关objc_msgSend的完成进程,能够查变量类型有哪些看文章:iOS底层剖析之类的根究-cache之 insert、objc_msgSend
下面是「dt」和「ssj」指向Direction类
:
拓展:对run
办法进行改造,检查打印的数据。
// Direction.m
@implementation Direction
- (void)run{
NSLog(@"run faster.->%@",self.hobby);
}
@end
作业:
-
榜首个打印null没什么疑问,因实例化数组为没有对hobby赋值;
-
第二个为什么打印的是
Direction
类呢?
答:dt作为一个实例政策
,alloc拓荒了空间,它有指针地址、也有内存
。而ssj
就是一个朴素指向Direction类地址的指针,体系并没有为它拓荒内存
。因而函数调用栈hobby
指向ssj指针地址的下一个地址(栈帧里的如栈次序,也就是dt)。
我ioslauncher们来经过控制台打印,看下数据的改动:
然后在Direction
的run
办法下断点:
这时分,咱们有个猜指针舆情帮手测
:
- 经过类地址指针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
持续重复上面的控制台打印:
在Direction
的run
办法下断点:
打印成果验证了上面的猜想:
经过类地址指针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体系栈。
作业代码:
参数压栈
// 自界说函数
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(@"查验结束");
}
下变量之间的联系断点,作业代码:
分别打印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?
带着这个疑问,咱们准备实践操作打印一下:
下符号断点objc_msgSendSuper2
输入reios1471值得更新吗gister read
检查寄存器
控制台,打印x0,即榜首函数调用个参数的数据:
代码:
链接: pan.baidu.com/s/1uPGdFij0…
暗码: il7h
–来自百度网盘超级会员V2的共享