xml地图|网站地图|网站标签 [设为首页] [加入收藏]

iOS底层原理总结,category工作原理

来源:http://www.ccidsi.com 作者:呼叫中心培训课程 人气:132 发布时间:2020-04-21
摘要:Category的原形二load,initialize方法Category的面目三关联对象 面试题 Category的落实原理,以至Category为何只好加方法不能加属性。 Category中有load方法呢?load方法是什么样时候调用的?load方

Category的原形<二>load,initialize方法Category的面目<三>关联对象

面试题

  1. Category的落实原理,以至Category为何只好加方法不能加属性。
  2. Category中有load方法呢?load方法是什么样时候调用的?load 方法能三番五遍吗?
  3. load、initialize的区分,以致它们在category重写的时候的调用的次第。

摘要

在Objective-C 2.0中,提供了category这么些语言特色,能够动态地为本来就有类增加新行为。方今category已经遍及于Objective-C代码的顺序角落,从Apple官方的framework到种种开源框架,从功用繁复的特大型应用软件到概括的施用,catagory无处不在。本文对category做了相比康健的整合治理,希望对读者有所裨益。

一 写在最初

Category我们应该用过,它主假使用在为对象的不相同种类的功效中庸之道,譬喻说人那个目的,大家可以为其创造四个分类,分别对应学习,工作,苏息。上边创制了二个Person类和三个Person类的归类。分别是Person Test和Person Eat。这两个类中各有八个办法。

//Person类- run;- run{ NSLog;}

//Person Test分类- test;- test{ NSLog;}

//Person Eat分类- eat;- eat{ NSLog;}

当大家须要使用这么些分类的时候只须求引进那几个分类的头文件就能够:

#import "Person Test.h"#import "Person Eat.h"Person *person = [[Person alloc] init]; [person run]; [person test]; [person eat];

我们都通晓,函数调用的精气神儿是音信机制。[person run]的真相正是objc_mgs(person, @selector,那几个很好掌握,由于目的方法是寄存在类对象中的,所以向person对象发送音讯便是通过person对象的isa指针找到其类对象,然后在类对象中找到那么些目的方法。[person test][person run]都是调用分类的靶子方法,本质应该雷同。[person test]的庐山面目目就是objc_mgs(person, @selector,给实例对象发送音讯,person对象通过自身的isa指针找到类对象,然后在投机的类对象中搜寻那一个实例方法,那么难点来了,person类对象中有没有囤积分类中的那一个指标方法吧?Person Test那几个分类会不会有温馨的分类的类对象,将分类的目的方法囤积在这里个类对象中呢?

咱俩要明白的少数是种种类唯有一个类对象,不管这几个类有未有分类。所以分类中的对象方法也就存款和储蓄在Person类的类对象中。后边大家会通过源码证实这点。

Category的本质

首先大家写一段轻便的代码,之后的剖析都依照这段代码。

Presen类 // Presen.h#import <Foundation/Foundation.h>@interface Preson : NSObject{ int _age;}- run;@end// Presen.m#import "Preson.h"@implementation Preson- run{ NSLog(@"Person - run");}@endPresen扩展1// Presen Test.h#import "Preson.h"@interface Preson  <NSCopying>- test;  abc;@property (assign, nonatomic) int age;- setAge:age;- age;@end// Presen Test.m#import "Preson Test.h"@implementation Preson - test{}  abc{}- setAge:age{}- age{ return 10;}@endPresen分类2// Preson Test2.h#import "Preson.h"@interface Preson @end// Preson Test2.m#import "Preson Test2.h"@implementation Preson - run{ NSLog(@"Person  - run");}@end

咱俩前面讲到超过实际例对象的isa指针指向类对象,类对象的isa指针指向元类对象,当p调用run方法时,通超过实际例对象的isa指针找到类对象,然后在类对象中检索对象方法,若无找到,就通过类对象的superclass指针找到父类对象,接着去搜求run方法。

那就是说当调用分类的法猪时,步骤是或不是和调用对象方法一致啊?分拣中的对象方法仍然为积累在类对象中的,同本类对象方法在同二个地方,调用步骤也同调用对象方法相同。即使是类方式的话,也千人一面是积攒在元类对象中。那么分类方法是哪些存储在类对象中的,我们来由此源码看一下分类的平底构造。

目录布局

1、 category简要介绍2、 category布局重新整合3、 category加载进度4、category和 load

二 底层构造

大家在率先有的讲了,分类中的对象方法和类措施最后会归并到类中,分类中的对象方法统一到类的类对象中,分类中的类措施统一到类的元类对象中。那么这个统一是怎么着时候产生的吧?是在编译器编译器就帮大家联合好了吗?实际是在运维期,实行的联合。下边大家通过将Objective-c的代码转变为c 的源码窥伺者一下Category的底层结构。大家在命令行步向到存放Person Test.m这一个文件的公文夹中,然后在命令行输入clang -rewrite-objc Person Test.m,那样Person Test.m这一个文件就被转载为了c 的源码Person Test.cpp。大家张开那几个.cpp文件,由于这么些文件十分的短,所以大家一贯拖到最上边,找到_category_t本条构造体。这几个布局体就是每贰个分类的布局:

struct _category_t { const char *name; //类名 struct _class_t *cls; const struct _method_list_t *instance_methods; //对象方法列表 const struct _method_list_t *class_methods; //实例方法列表 const struct _protocol_list_t *protocols; //协议列表 const struct _prop_list_t *properties; //属性列表};

咱俩跟着往下找到这么些布局体的起始化:

static struct _category_t _OBJC_$_CATEGORY_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = { "Person", 0, // &OBJC_CLASS_$_Person, (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test, (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test, 0, 0,};

透过构造体名称_OBJC_$_CATEGORY_Person_$_Test我们得以精晓那是Person Test这几个分类的早先化。类名对应的是"Person",对象方法列表那一个构造体对应的是&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test,类方式列表这一个布局体对应的是&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test,其他的开始化都以空。然后大家找到&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test其一构造体:

static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[1];} _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 1, {{(struct objc_selector *)"test", "v16@0:8", _I_Person_Test_test}}};

能够见见那个构造体中带有二个指标方法test,那多亏Person Test这一个分类中的对象方法。然后大家再找到&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test以此布局体:

static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[1];} _OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 1, {{(struct objc_selector *)"test2", "v16@0:8", _C_Person_Test_test2}}};

一直以来能够见到这几个构造体,它饱含一个类措施test2,那个相近是Person Test中的类情势。

分类的最底层构造

怎样验证上述难题?通过翻看分类的源码大家能够找到category_t 结构体。

struct category_t { const char *name; classref_t cls; struct method_list_t *instanceMethods; // 对象方法 struct method_list_t *classMethods; // 类方法 struct protocol_list_t *protocols; // 协议 struct property_list_t *instanceProperties; // 属性 // Fields below this point are not always present on disk. struct property_list_t *_classProperties; method_list_t *methodsForMeta(bool isMeta) { if  return classMethods; else return instanceMethods; } property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);};

从源码基本能够看出我们平日利用categroy的办法,对象方法,类措施,公约,和质量都足以找到对应的存款和储蓄格局。并且大家发掘分类布局体中是不真实成员变量的,因而分类中是不一致敬增添成员变量的。分类中加上的属性并不会支持大家自动生成成员变量,只会生成get set方法的宣示,必要我们自身去得以落成。

通过源码我们发掘,分类的措施,公约,属性等相通真的是寄放在categroy布局体里面的,那么他又是什么存款和储蓄在类对象中的呢?我们来看一下尾巴部分的中间方法找寻个中的法则。首先我们由此命令行将Preson Test.m文件转载为c 文件,查看里面包车型大巴编写翻译过程。

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Preson Test.m

在分拣转变为c 文件中得以见到_category_t布局体中,寄存着类名,对象方法列表,类格局列表,公约列表,以致质量列表。

图片 1c 文件中category_t结构体

接着,大家得以看来_method_list_t类型的布局体,如下图所示

图片 2目的方法列表布局体

上海体育场合中我们开掘那一个构造体_OBJC_$_CATEGORY_INSTANCE_METHODS_Preson_$_Test从名称能够观看是INSTANCE_METHODS对象方法,况兼逐条对应为上边构造体内赋值。大家得以见见布局体中存款和储蓄了法子占用的内部存款和储蓄器,方法数量,甚至艺术列表。并且从上海教室中找到分类中我们完毕对应的对象方法,test , setAge, age多个艺术

接下去大家开采一律的_method_list_t类型的类格局构造体,如下图所示

图片 3类对象方法列表

同地点对象方法列表同样,那个大家得以见到是类措施列表布局体 _OBJC_$_CATEGORY_CLASS_METHODS_Preson_$_Test,同对象方法布局体相近,相似能够看到我们兑现的类格局,abc。

接下去是说道章程列表

图片 4合计章程列表

经过上述源码能够看见先将协商章程通过_method_list_t布局体存款和储蓄,之后经过_protocol_t构造体存款和储蓄在_OBJC_CATEGORY_PROTOCOLS_$_Preson_$_Test中同_protocol_list_t构造体一一对应,分别为protocol_count 交涉数量以至存款和储蓄了磋商格局的_protocol_t结构体。

最后大家得以看来属性列表

图片 5脾气列表构造体属性列表构造体_OBJC_$_PROP_LIST_Preson_$_Test同_prop_list_t布局体对应,存款和储蓄属性的挤占空间,属性属性数据,以致质量列表,从上图中能够看看大家分甘共苦写的age属性。

最终大家能够见到定义了_OBJC_$_CATEGORY_Preson_$_Test构造体,并且将大家地点根本深入分析的布局体一一赋值,大家经过两张图片比较一下。

图片 6_category_t图片 7_OBJC_$_CATEGORY_Preson_$_Test

上下两张图一一对应,而且我们看出定义_class_t类型的OBJC_CLASS_$_Preson结构体,最后将_OBJC_$_CATEGORY_Preson_$_Testcls指南针指向OBJC_CLASS_$_Preson构造体地址。大家这里能够看来,cls指南针指向的应有是分类的主类类对象的地点。

由此上述解析大家开掘。分类源码中真就是将大家定义的靶子方法,类方式,属性等都寄存在catagory_t布局体中。接下来大家在回来runtime源码查看catagory_t存款和储蓄的办法,属性,公约等是何许存款和储蓄在类对象中的。

首先来到runtime早先化函数

图片 8runtime发轫化函数

随之大家来到 &map_images读取模块(images这里表示模块),来到map_images_nolock函数中找到_read_images函数,在_read_images函数中大家找到分类相关代码

图片 9Discover categories代码

从上述代码中大家得以掌握这段代码是用来搜寻有未有分类的。通过_getObjc2CategoryList函数获取到分类列表之后,进行遍历,获取个中的艺术,左券,属性等。能够观望最终都调用了remethodizeClass;函数。大家来到remethodizeClass;函数内部查看。

图片 10remethodizeClass函数内部

经过上述代码大家开采attachCategories函数接受了类对象cls和归类数组cats,如笔者辈一初叶写的代码所示,三个类能够有七个分类。以前大家说起分类音讯囤积在category_t布局体中,那么多少个分类则保留在category_list中。

我们过来attachCategories函数内部。

图片 11attachCategories函数内部得以达成

上述源码中得以见到,首先依照办法列表,属性列表,协议列表,malloc分配内部存储器,根据多少个分类以致每一块方法要求多少内存来分配相应的内部存款和储蓄器地址。之后从分类数组里面往八个数组里面寄存分类数组里面寄存的分类方法,属性以至和煦归入对应mlist、proplists、protolosts数组中,这八个数组放着富有分类的办法,属性和商事。之后经过类对象的data(卡塔尔方法,得到类对象的class_rw_t结构体rw,在class布局中大家介绍过,class_rw_t中贮存着类对象的秘籍,属性和情商等数据,rw布局体通过类对象的data方法获得,所以rw里面寄放那类对象里面包车型客车多寡。之后分别通过rw调用艺术列表、属性列表、左券列表的attachList函数,将兼具的归类的情势、属性、合同列表数组传进去,我们大要能够推断到在attachList方法内部将分类和本类相应的指标方法,属性,和情商进行了联合。

我们来看一下attachLists函数内部。

图片 12attachLists函数内部贯彻

上述源代码中有八个主要的数组array(State of Qatar->lists: 类对象原本的秘诀列表,属性列表,契约列表。addedLists:传入全部分类的章程列表,属性列表,合同列表。

attachLists函数中最入眼的七个主意为memmove内部存款和储蓄器移动和memcpy内部存款和储蓄器拷贝。大家先来分别看一下那五个函数

// memmove :内存移动。/* __dst : 移动内存的目的地* __src : 被移动的内存首地址* __len : 被移动的内存长度* 将__src的内存移动__len块内存到__dst中*/void *memmove(void *__dst, const void *__src, size_t __len);// memcpy :内存拷贝。/* __dst : 拷贝内存的拷贝目的地* __src : 被拷贝的内存首地址* __n : 被移动的内存长度* 将__src的内存移动__n块内存到__dst中*/void *memcpy(void *__dst, const void *__src, size_t __n);

上边大家图示经过memmove和memcpy方法之后的内部存款和储蓄器变化。

图片 13未通过内部存款和储蓄器移动和拷贝时

通过memmove方法之后,内部存款和储蓄器变化为

// array()->lists 原来方法、属性、协议列表数组// addedCount 分类数组长度// oldCount * sizeof->lists[0]) 原来数组占据的空间memmove->lists   addedCount, array()->lists, oldCount * sizeof->lists[0]));

图片 14memmove方法之后内部存款和储蓄器变化

由此memmove方法之后,大家发掘,纵然本类的不二法门,属性,左券列表会独家后移,不过本类的对应数组的指针依旧指向原始地方。

memcpy方法之后,内部存款和储蓄器变化

// array()->lists 原来方法、属性、协议列表数组// addedLists 分类方法、属性、协议列表数组// addedCount * sizeof->lists[0]) 原来数组占据的空间memcpy->lists, addedLists, addedCount * sizeof->lists[0]));

图片 15memmove方法之后,内存变化

咱们发掘原本指针并不曾校订,至始至终指向早先的岗位。而且经过memmove和memcpy方法之后,分类的点子,属性,合同列表被放在了类对象中原本存款和储蓄的秘技,属性,公约列表前边。

那正是说为啥要将分类方法的列表追加到自然的指标方法后面吧,那样做的目标是为着有限援助分类方法优先调用,大家驾驭当分类重写本类的章程时,会覆盖本类的章程。其实通过地方的深入分析大家掌握真相上并非覆盖,而是优先调用。本类的不二等秘书技照旧在内部存款和储蓄器中的。大家能够透过打字与印刷全数类的具备办法名来查看

- printMethodNamesOfClass:cls{ unsigned int count; // 获得方法数组 Method *methodList = class_copyMethodList(cls, &count); // 存储方法名 NSMutableString *methodNames = [NSMutableString string]; // 遍历所有的方法 for (int i = 0; i < count; i  ) { // 获得方法 Method method = methodList[i]; // 获得方法名 NSString *methodName = NSStringFromSelector(method_getName; // 拼接方法名 [methodNames appendString:methodName]; [methodNames appendString:@", "]; } // 释放 free(methodList); // 打印方法名 NSLog(@"%@ - %@", cls, methodNames);}- viewDidLoad { [super viewDidLoad]; Preson *p = [[Preson alloc] init]; [p run]; [self printMethodNamesOfClass:[Preson class]];}

通过下图中打字与印刷内容能够窥见,调用的是Test第22中学的run方法,况兼Person类中存款和储蓄着三个run方法。

图片 16打字与印刷全体办法

问: Category的兑现原理,以至Category为何只可以加方法不能加属性?

答:分类的兑现原理是将category中的方法,属性,合同数据放在category_t布局体中,然后将组织体内的办法列表拷贝到类对象的办法列表中。Category能够增加属性,可是并不会自动生成成员变量及set/get方法。因为category_t构造体中并一纸空文成员变量。通过之前对目的的分析大家了然成员变量是存放在实例对象中的,而且编译的那一刻就曾经调控好了。而分类是在运作时才去加载的。那么我们就一点都不大概再程序运维时将分类的分子变量中增加到实例对象的结构体中。因而分类中不得以加多成员变量。

1、 category简介

category是Objective-C 2.0随后加上的语言特征,category的至关重要功效是为曾经存在的类加多方法。除外,apple还引入了category的别的多少个使用项景1

  • 能够把类的贯彻分开在多少个区别的文件之中。那样做有多少个明显的裨益,a卡塔尔国能够减去单个文件的体量b卡塔尔(قطر‎能够把分化的法力公司到区别的category里 c卡塔尔国可以由多少个开垦者合营完成二个类 d卡塔尔(قطر‎能够按需加载想要的category 等等。
  • 扬言私有方法

而是除了apple推荐的使用意况,广大开垦者脑洞大开,还衍生出了category的任何多少个使用意况:

  • 仿照多一连
  • 把framework的私人民居房方法公开

Objective-C的这几个语言特征对于纯动态语言来讲恐怕不算什么,譬如JavaScript,你能够天天为一个“类”可能指标增加率性方法和实例变量。然则对于不是那么“动态”的语言来讲,那实在是一个壮烈的特色。

本文由68399皇家赌场发布于呼叫中心培训课程,转载请注明出处:iOS底层原理总结,category工作原理

关键词: 工作 gt 原理 底层 本质

上一篇:CocoaPods使用总结,CocoaPods安装与使用

下一篇:没有了

最火资讯