OC底层原理探索—类的加载(2)

OC底层原理探索—类的加载(2),第1张

上篇文章我们探索了 read_images 里面的几个比较重要的流程,这篇我们接着上篇文章剩下的 realizeClassWithoutSwift 方法来讲解

1首先进行判断,判断类是否存在,如果不存在直接返回nil,然后在判断这个类是否已经验证实现,如果实现则返回当前类,只要时因为这个地方也存在递归实现 元类与父类 , 根类的父类为nil 、 元类的isa指向自己 ,所以这样可以保证类只会被初始化一次

2 rw的初始化

3对类的父类和元类进行递归处理,是为了设置 superClass 的继承链和 isa 的走位图

方法列表 添加至rwe逻辑

方法排序 - prepareMethodLists 方法

在 prepareMethodLists 方法中 进行排序的主要方法 fixupMethodList 为这个方法

接下来我们来验证一下这里是否是在进行方法的排序

这里在排序之前实际上已经排好序了,主要是跟编译器有关,实在是整不出没有排好序的了。这里看下排序后的地址是由小到大进行排列的

_read_images -> relizeClassWithoutSwift(对于ro、rw的 *** 作) -> methodizeClass -> prepareMethodLists -> fixupMethodList (主要是方法排序)

回到 _read_images 方法,中的 类的加载处理部分

非懒加载类 即程序启动时便会加载,效率很低,所以苹果采用了 按需加载 ,也就是 懒加载类 ,等需要时再加载。

此时在 mainm 中初始化我们的类

在 realizeClassWithoutSwift 方法中添加该类的断点

1以下图为例,让我们继续探索

从这里我们可以看出0x0000000100008338的类型是元类

通过上图我们可以看出JTeacher的元类对象的父类地址等于JPerson的元类对象地址,JPerson的元类对象的父类地址等于NSObject的元类对象的地址,而NSObject的元类对象的父类地址则等于NSObject本身。

instance 的 isa 指向 class

class 的 isa 指向 meta-class

meta-class 的 isa 指向基类的meta-class

class 的 superClass 指向父类的class

如果没有父类,superClass 为nil

meta-class 的superClass 指向父类的 meta-class

基类的元类对象的superClass 指向基类的class

一、基础数据类型

2数据类型转化

基础数据类型转换:容量从大到小转换为强制转换,需要加强制转换符,double –> float–> long–> int–> char/ short/Byte

二、OC运算符

逻辑运算符

自加自减运算符++ / --

三、if条件语句、swtch语句、循环语句

if (0 == count){

}else if (1 == count) {

}else{

}

switch ( expression )

{

case value1 :

break; // 跳出整个循环语句

case value2 :

break;

case value3 :

break;

default:

break;

}

for (int x = 0; x<10; x++){

}

int a = 0;

while (a < 5) {

nslog(@”%d”,a);

if (a == 1) {

continue;//跳出当次循环,输入结果0134

}

a++;

}

动态类型:OC在运行时才确定对象的实际类型,id。

动态绑定:程序在执行时才确定对象调用的实际方法。

多态:一种事物的多种形态。不同类的对象可以定义共享相同的方法,

多态的条件:有继承关系、有方法的重写、父类声明变量指向子类对象。

NSNumber

NSFileHandle

复制对象

归档

NSUserDefault

KVC

KVO

NSNotification

NSPredicate谓词

类目(Category):添加新的方法,会成为原始类的一部分,类目不能添加实例变量,类目可以重写原始类的方法。

类目命名方式:类名+扩展方法。

延展(Extension):

协议:

委托设计模式:

这章其实只讲 src/core/instance/initjs 里的 resolveConstructorOptions 函数。

这个函数水有点深,网上的很多文章都没说全,所以单独拎出来

首先来个栗子

当我们计算 Child 的 options 的时候我们不能简单的取 Childoptions ,因为后面其父父类 Vue 混入了 options ,其本身也混入了 options 。这时候我们取得 Childoptions 会漏了 Vuemixin 混入的 options 。

这也是很好理解,因为 Childoptions 在 Child = Parentextend() 之后除了 Childmixin() 就没改过,但是 Vuemixin() 导致 Vueoptions 改变了,所以本该继承下来的没继承

就像标题所述,这个是解析传入的构造函数的 options ,然后返回新的 options (内部会修正一些变量)

首先我们假设下面的 Ctor 是在 Child 的时候的情况对于上文的栗子而言

首先根据是否有 super 属性来判断是否是子类

若是是子类的话看看分支之内

首先获取父类 (Parent) 正确 的 options ,然后获取执行 Child = Parentextend() 时缓存的父类 (Parent) 的 options 。因为前者是当前父类 (Parent) 真实的 options ,所以俩者可以通过比较来判断之后是否改变过。

很明显改过了( Vuemixin({}) 导致 Parent 继承的值变了,也就导致其真实的 options 变了)

若是有改动的话就先修正

然后到重要的点

就是它,解析新增的那部分 options

这个就是上面所说的获取通过 extend、mixin 新增的那部分 options

首先获取三个属性,我们单看 created

然后遍历 latest ,要是值有了变化

那么就得判断这个值是否有重复(数组情况,比如生命周期钩子), 这个所谓的重呢就是是否和父类继承过来的那部分重复了 。很明显,这里的 fn1 就重了,因为是 Parent 继承来的

去重函数

首先判断传入的当前值是不是数组,如果不是,那么就直接返回最新值,否则的话

我们可见首先规范化俩参数为数组,然后遍历最新值,这里这个 if 语句有点难理解

其实他走的俩策略

1继承Inherit

这个是面向对象语言都有的一个特性,子类会继承父类的方法和属性。

对于以下情况,无法使用类别,必须使用继承。

1)在子类中新扩展的方法与原方法同名,但是还需要使用父类的实现要用继承。因为使用类别,会覆盖原类的实现,无法访问到原来的方法。

2)扩展类的属性和实例变量,这个类别无法做到。

2类别category

这是Objective-C语言的一个特性,可以在不改变类名和原来类的实现的前提下,实现对类的方法扩展。

以下两种方式最后使用类别。

类别的特点:

类别在实际中的应用:

1)针对系统提供的一些类,例如:NSString,NSArray,NSNumber等类,系统本身不提倡使用继承去扩展方法,因为这些类内部实现对继承有所限制,所以最后使用类别来进行方法扩展。

2)类别支持开发人员针对自己构建的类,把相关的方法分组到多个单独的文件中,对于大型而复杂的类,这有助于提高可维护性,并简化单个源文件的管理。

3类的扩展

OC中的扩展只是在m文件中@implementation之上加一段@interface~@end,在其中声明变量属性和方法。在本身的@implementation中实现。

扩展就像是匿名的类别,扩展中声明的实例变量和属性、方法都是私有的。扩展中声明的方法可以不实现(但会有issues提示)。

(应当注意的是OC中的扩展和swift中的扩展完全是两回事。swift中的扩展基本上完全相当于OC的类别。)

思考:为什么要创建方法缓存列表,目的是什么

假如调用对象方法的时候,首先从对象的方法列表中查询方法,如果不存在,通过superclass查找父类的方法列表,直到找到方法。

按照这样的逻辑执行的话,每次调用方法都会去查询方法,这样会造成资源的浪费

加入存在一个方法的缓存列表,第一次调用方法的时候,都将这个方法缓存起来,那么下次再调用这个方法的时候,就可以直接从缓存列表中读取,不需要再按照流程去查询方法

再次调用使用过的方法的时候,直接从缓存列表中读取

通过源码分析发现,苹果不是单纯的通过遍历的方式查询方法的位置。 而是通过将 sel 与 mask进行 &预算计算出 方法存放的下标,如果下标内已经存放了方法,会继续往上一个位置存放

如果缓存列表满了,会清空缓存列表,扩容内存为原来的两倍,等待下次调用方法的时候重新进行缓存,因为 _mask 存放的数量 已经改变了,进行 &运算的时候,肯定是和之前的值不一样,所以需要清空,重新缓存

class

Program

{

static

void

Main(string[]

args)

{

string

path

=

@"D:\OpenSSL";

//

ConsoleWriteLine(isDir(path));

//ConsoleRead();

Man

man

=

new

Man();

Type

type

=

manGetType();

PropertyInfo[]

infos

=

typeGetProperties();

foreach

(PropertyInfo

info

in

infos)

{

ConsoleWriteLine(infoDeclaringTypeFullName

+

"

,

"

+

infoName);

}

ConsoleRead();

}

}

public

class

Person

{

public

string

name

{

get;

set;

}

public

string

password

{

get;

set;

}

}

public

class

Man

:

Person

{

public

string

height

{

get;

set;

}

public

string

weight

{

get;

set;

}

}

比较笨的办法。

肯定有更优解决方案。

以上就是关于OC底层原理探索—类的加载(2)全部的内容,包括:OC底层原理探索—类的加载(2)、OC底层原理之《isa - 类的底层原理结构01》、OC基础数据类型等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/9681784.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-30
下一篇 2023-04-30

发表评论

登录后才能评论

评论列表(0条)

保存