Objective-C 源码(三)Category的实现原理

Category的使用场景主要有3个:

给现有的类添加方法;

将一个类的实现拆分成多个独立的源文件;

声明私有的方法。

实现原理

我们知道,无论我们有没有主动引入 Category 的头文件,Category 中的方法都会被添加进主类中。我们可以通过 - performSelector: 等方式对 Category 中的相应方法进行调用,之所以需要在调用的地方引入 Category 的头文件,只是为了“照顾”编译器同学的感受。

打开 objc-runtime-new.mm 文件,可以找到 _read_images ()方法

if (cat->instanceMethods ||  cat->protocols  
                ||  cat->instanceProperties) 
            {
                addUnattachedCategoryForClass(cat, cls, hi);
                if (cls->isRealized()) {
                    remethodizeClass(cls);
                    classExists = YES;
                }
                if (PrintConnecting) {
                    _objc_inform("CLASS: found category -%s(%s) %s", 
                                 cls->nameForLogging(), cat->name, 
                                 classExists ? "on existing class" : "");
                }
            }

            if (cat->classMethods  ||  cat->protocols  
                /* ||  cat->classProperties */) 
            {
                addUnattachedCategoryForClass(cat, cls->ISA(), hi);
                if (cls->ISA()->isRealized()) {
                    remethodizeClass(cls->ISA());
                }
                if (PrintConnecting) {
                    _objc_inform("CLASS: found category +%s(%s)", 
                                 cls->nameForLogging(), cat->name);
                }
            }

可以看到这个函数对Category做了如下处理:

将 Category 和它的主类(或元类)注册到哈希表中;

如果主类(或元类)已实现,那么重建它的方法列表。

在这里分了两种情况进行处理:Category 中的实例方法和属性被整合到主类中;而类方法则被整合到元类中。另外,对协议的处理比较特殊,Category 中的协议被同时整合到了主类和元类中。

最终都是通过调用

remethodizeClass(Class cls)

这个函数的主要作用是将 Category 中的方法、属性和协议整合到类(主类或元类)中,更新类的数据字段 data() 中 method_lists(或 method_list)、properties 和 protocols 的值。进一步,我们通过 attachCategoryMethods 函数的源码可以找到真正处理 Category 方法的 attachMethodLists 函数。

它的主要作用就是将集中的久类方法和Category中新添加的方法整合成一个心的方法列表,并赋值给 method_lists 或者 method_list 。说明了:主类中的方法和Category中的方法在runtime看来并没有区别,它们是被同等对待的,都保存在主类的方法列表中。

runtime 对 Category 中方法的处理过程并没有对 +load 方法进行什么特殊地处理。因此,严格意义上讲 Category 中的 +load 方法跟普通方法一样也会对主类中的 +load 方法造成覆盖,只不过 runtime 在自动调用主类和 Category 中的 +load 方法时是直接使用各自方法的指针进行调用的。所以才会使我们觉得主类和 Category 中的 +load 方法好像互不影响一样。因此,当我们手动给主类发送 +load 消息时,调用的一直会是分类中的 +load 方法。

时间: 2024-08-11 05:34:24

Objective-C 源码(三)Category的实现原理的相关文章

yii源码三 -- db

CDbConnection:path:/framework/db/CDbConnection.phpoverview:CDbConnection represents a connection to a database. 工作原理:CDbConnection works together with CDbCommand, CDbDataReader and CDbTransaction to provide data access to various DBMS.且基于PDO扩展. 首先用$c

CAD ObjectARX扩展工具的源码(三)

CAD ObjectARX扩展工具的源码(三)//得到文本边界oid CDrawFunction::getTextBoundary(AcDbObjectId objectId,double offset,AcDbObjectId &textBoundaryId){AcDbExtents Ext;AcDbEntity *pEnt;acdbOpenObject(pEnt,objectId,AcDb::kForWrite);if(pEnt->isKindOf(AcDbText::desc())){

Python源码剖析笔记3-Python执行原理初探

Python源码剖析笔记3-Python执行原理初探 本文简书地址:http://www.jianshu.com/p/03af86845c95 之前写了几篇源码剖析笔记,然而慢慢觉得没有从一个宏观的角度理解python执行原理的话,从底向上分析未免太容易让人疑惑,不如先从宏观上对python执行原理有了一个基本了解,再慢慢探究细节,这样也许会好很多.这也是最近这么久没有更新了笔记了,一直在看源码剖析书籍和源码,希望能够从一个宏观层面理清python执行原理.人说读书从薄读厚,再从厚读薄方是理解了

【MyBatis源码分析】插件实现原理

MyBatis插件原理----从<plugins>解析开始 本文分析一下MyBatis的插件实现原理,在此之前,如果对MyBatis插件不是很熟悉的朋友,可参看此文MyBatis7:MyBatis插件及示例----打印每条SQL语句及其执行时间,本文我以一个例子说明了MyBatis插件是什么以及如何实现.由于MyBatis的插件已经深入到了MyBatis底层代码,因此要更好地使用插件,必须对插件实现原理及MyBatis底层代码有所熟悉才行,本文分析一下MyBatis的插件实现原理. 首先,我们

问答形式阅读jQuery源码(三)

通过艾伦的博客,我们能看出,jQuery的promise和其他回调都是通过jQuery.Callbacks实现的.所以我们一起简单看看jQuery.Deferred和jQuery.Callbacks.来看看关于他们的一些提问. 提问:jQuery.Callbacks的配置为什么是用字符串参数? jQuery.Callbacks有四种配置,分别是once.memory.unique.stopOnFalse.而jQuery.Callbacks的配置形式却和以往我们熟悉的不同,不是使用json,而是使

spring事务源码分析结合mybatis源码(三)

下面将结合mybatis源码来分析下,这种持久化框架是如何对connection使用,来达到spring事务的控制. 想要在把mybatis跟spring整合都需要这样一个jar包:mybatis-spring-x.x.x.jar,这里面定义了一些主要的整合信息. 在spring配置文件中需要配置如下两个bean: <!-- mybatis配置 --> <bean id="sqlSessionFactory" class="org.mybatis.sprin

Redis源码中探秘SHA-1算法原理及其编程实现

导读 SHA-1算法是第一代"安全散列算法"的缩写,其本质就是一个Hash算法.SHA系列标准主要用于数字签名,生成消息摘要,曾被认为是MD5算法的后继者.如今SHA家族已经出现了5个算法.Redis使用的是SHA-1,它能将一个最大2^64比特的消息,转换成一串160位的消息摘要,并能保证任何两组不同的消息产生的消息摘要是不同的.虽然SHA1于早年间也传出了破解之道,但作为SHA家族的第一代算法,对我们仍然很具有学习价值和指导意义. SHA-1算法的详细内容可以参考官方的RFC:ht

内核通信之Netlink源码分析-用户内核通信原理3

2017-07-06 上节主讲了用户层通过netlink和内核交互的详细过程,本节分析下用户层接收数据的过程-- 有了之前基础知识的介绍,用户层接收数据只涉及到一个核心调用readmsg(), 其他的就不多介绍了,不太明白的请参考之前的文章,我们还是重点看下内核究竟在背后做了什么!该函数在内核对应于read_msg系统调用 SYSCALL_DEFINE3(recvmsg, int, fd, struct msghdr __user *, msg, unsigned int, flags) { i

自主开发微信云控系统源码与群控系统源码区别,及技术原理

在微信云控系统中,主要由服务器端,手机客户端,模拟点击 滑动等部分组成.其系统关键的核心在于服务器端的命令推送到手机上,以及手机接收到命令后执行相应程序的模拟人工操作.在开发过程中,我们主要用到的开发语言有:PHP.Android.Java.C.C++.服务器的推送方式有很多种,在我们实现云控系统中选用的是个推来实现命令的推送.后台设备管理,以及命令参数的下发等等,都是大家都会经常遇到的各种管理系统的开发类似的.部分核心代码为: //服务端推送接口,支持三个接口推送 //1.PushMessag

老李推荐:第14章7节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-获取版本号 2

代码先是发送”LIST”命令到ViewServer列出所有的打开的窗口,然后把每个窗口都保存起来.342行起按照源码的注释解析就是说:从协议版本3以后开始加入了窗口自动更新的功能,但是在此之前,如果用户想要获得一个获得焦点的窗口的话,需要通过显式的创建一个特殊的哈希值为-1的Window实例来完成.怎么知道它的哈希值是-1呢?请看Window类的getfocusedWindow方法: return new Window(device, "<Focused Window>",