Category
是Objective-C 2.0
以后添加的语言特性,它的主要做用是为已经存在的类添加方法,通常称为分类。 Category
在iOS
开发中使用很是的频繁,特别是在为系统类进行拓展的时候,咱们能够不用继承系统类,直接给系统类添加方法,最大程度的体现了Objective-C
的动态语言特性。数组
新建了一个工程,建立了一个实体类,和一个category分类 bash
cmd+B
编译以后打开终端,cd到工程的目录,执行
clang -rewrite-objc Test+categroy.m
命令
Test+categroy.cpp
文件
_category_t
,看到编译出来的category结构体
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 (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
复制代码
能够看出,在源码中Category
的底层结构category_t
和上文中咱们获得的_category_t
结构体基本一致,在编译以后,每一个Category
确实会生成一个category_t
类型的结构体。并且category_t
类型的结构体中是没有ivars
这项,这就是分类是不能添加成员变量的缘由。数据结构
dyld
是苹果的动态加载器,用来加载image
(image
指的是Mach-O
格式的二进制文件,不是图片)dyld
,而dyld
会将咱们APP所依赖的各类库加载到内存中,其中就包括libobjc
库(OC
和runtime
),这些工做,是在APP
的main
函数执行以前完成的_objc_init
是Object-C runtime
的入口函数,在这里主要是读取Mach-O
文件OC
对应的Segment section
,并根据其中的数据代码信息,完成为OC
的内存布局,以及初始化runtime
相关的数据结构。_objc_init
函数void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
复制代码
_dyld_objc_notify_register
这一句代码,这里注册了三个函数&map_images
将image
加载进内存load_imagesdyld
初始化加载image
方法unmap_images
移除内存 下面探究map_images
这个函数void map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
复制代码
map_images_nolock
函数是用来执行全部类注册和修复操做,并调用+load方法
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
...
//前面的代码省略,主要代码是_read_images这个函数
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
}
复制代码
_read_images
函数中有多个段落,咱们研究的重点在于分类// Discover categories.
for (EACH_HEADER) {
// 获取项目中全部的Category,获得一个二维数组
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
// 遍历二维数组,获得每个category_t类型的结构体变量
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
// 经过cat->cls拿到当前Category所属的类
Class cls = remapClass(cat->cls);
if (!cls) {
catlist[i] = nil;
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
"missing weak-linked target class",
cat->name, cat);
}
continue;
}
// 判断class是否存在
bool classExists = NO;
// 若是Category中存在实例方法,协议或者是实例属性
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
// 将cls的未合并的全部Category存放到以cls为key的一个映射表中去
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
// 从新组织实例方法,将Category中的方法、属性、协议等等附加到cls的实例方法列表、实例属性列表和协议列表中去
remethodizeClass(cls);
classExists = YES;
}
if (PrintConnecting) {
_objc_inform("CLASS: found category -%s(%s) %s",
cls->nameForLogging(), cat->name,
classExists ? "on existing class" : "");
}
}
// 若是Category中存在类方法,协议或者是类属性
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
// 从新组织元类方法,将Category中的类方法、类属性、协议等等附加到cls的元类的类方法列表、类属性列表和协议列表中去
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
if (PrintConnecting) {
_objc_inform("CLASS: found category +%s(%s)",
cls->nameForLogging(), cat->name);
}
}
}
}
复制代码
remethodizeClass
函数,重点函数在attachCategories
static void remethodizeClass(Class cls)
{
category_list *cats;
bool isMeta;
runtimeLock.assertWriting();
isMeta = cls->isMetaClass();
// Re-methodizing: check for more categories
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
if (PrintConnecting) {
_objc_inform("CLASS: attaching categories to class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// 把分类信息附加到类/元类中
attachCategories(cls, cats, true /*flush caches*/);
free(cats);
}
}
复制代码
attachCategories
函数static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);
// 判断传入的类是否为元类
bool isMeta = cls->isMetaClass();
// fixme rearrange to remove these intermediate allocations
// 建立一个存放方法的二维数组
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));
// 建立一个存放属性的二维数组
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));
// 建立一个存放协议的二维数组
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists));
// Count backwards through cats to get newest categories first
int mcount = 0;
int propcount = 0;
int protocount = 0;
int i = cats->count;
bool fromBundle = NO;
while (i--) {
// 取出其中一个分类
auto& entry = cats->list[i];
// 取出分类中的方法存放到二维数组 mlists 中,isMeta决定是类方法仍是实例方法
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
// 取出分类中的属性存放到二维数组 proplists 中
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
// 取出分类中的协议存放到二维数组 protolists 中
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
// 获取到类对象中的数据
auto rw = cls->data();
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
// 将全部分类的对象方法附加到类对象方法列表中
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
// 将全部分类的属性附加到类对象属性列表中
rw->properties.attachLists(proplists, propcount);
free(proplists);
// 将全部分类的协议附加到类对象协议列表中
rw->protocols.attachLists(protolists, protocount);
free(protolists);
}
复制代码
attachLists
这个函数主要是关联原类方法的void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
// 内存移动
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
// 内存拷贝
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
复制代码