Objective-C method及相关方法分析
## Objective-C method及相关方法分析
本篇文章将探究一下objc里的关于方法的函数是怎样实现的
首先看下方法的定义, Method 是一个objc_method结构体
objc_method
objc_method 是类的一个方法的描写叙述
定义例如以下
typedef struct objc_method *Method;struct objc_method {SEL method_name; // 方法名称char *method_typesE; // 參数和返回类型的描写叙述字串IMP method_imp; // 方法的详细的实现的指针
}
Method class_getInstanceMethod(Class aClass, SEL aSelector)
返回aClass的名为aSelector的方法
定义例如以下
Method class_getInstanceMethod(Class cls, SEL sel)
{if (!cls || !sel) return NULL;return look_up_method(cls, sel, YES/*cache*/, YES/*resolver*/);
}static Method look_up_method(Class cls, SEL sel, BOOL withCache, BOOL withResolver)
{Method meth = NULL;// 1. 找缓存,有过有就返回if (withCache) {meth = _cache_getMethod(cls, sel, &_objc_msgForward_internal);if (meth == (Method)1) {// Cache contains forward:: . Stop searching.return NULL;}}// 2. 找自身if (!meth) meth = _class_getMethod(cls, sel);// 3. 找转发if (!meth && withResolver) meth = _class_resolveMethod(cls, sel);return meth;
}
IMP class_getMethodImplementation(Class cls, SEL name)
返回cls的name方法的调用地址
定义例如以下
IMP class_getMethodImplementation(Class cls, SEL sel)
{IMP imp;if (!cls || !sel) return NULL;imp = lookUpMethod(cls, sel, YES/*initialize*/, YES/*cache*/);// Translate forwarding function to C-callable external versionif (imp == (IMP)&_objc_msgForward_internal) {return (IMP)&_objc_msgForward;}return imp;
}PRIVATE_EXTERN IMP lookUpMethod(Class cls, SEL sel, BOOL initialize, BOOL cache)
{Class curClass;IMP methodPC = NULL;Method meth;BOOL triedResolver = NO;// Optimistic cache lookup// 1. 先找下缓存if (cache) {methodPC = _cache_getImp(cls, sel);if (methodPC) return methodPC; }// realize, +initialize, and any special early exit// 2. 初始化下这个类,为接下来做准备methodPC = prepareForMethodLookup(cls, sel, initialize);if (methodPC) return methodPC;// The lock is held to make method-lookup + cache-fill atomic // with respect to method addition. Otherwise, a category could // be added but ignored indefinitely because the cache was re-filled // with the old value after the cache flush on behalf of the category.retry:lockForMethodLookup();// Ignore GC selectorsif (ignoreSelector(sel)) {methodPC = _cache_addIgnoredEntry(cls, sel);goto done;}// Try this class's cache.// 3. 先试着找缓存methodPC = _cache_getImp(cls, sel);if (methodPC) goto done;// Try this class's method lists.// 4. 找自己的method列表meth = _class_getMethodNoSuper_nolock(cls, sel);if (meth) {log_and_fill_cache(cls, cls, meth, sel);methodPC = method_getImplementation(meth);goto done;}// Try superclass caches and method lists.// 5. 找父类的缓存和method列表curClass = cls;while ((curClass = _class_getSuperclass(curClass))) {// Superclass cache.meth = _cache_getMethod(curClass, sel, &_objc_msgForward_internal);if (meth) {if (meth != (Method)1) {// Found the method in a superclass. Cache it in this class.log_and_fill_cache(cls, curClass, meth, sel);methodPC = method_getImplementation(meth);goto done;}else {// Found a forward:: entry in a superclass.// Stop searching, but don't cache yet; call method // resolver for this class first.break;}}// Superclass method list.meth = _class_getMethodNoSuper_nolock(curClass, sel);if (meth) {log_and_fill_cache(cls, curClass, meth, sel);methodPC = method_getImplementation(meth);goto done;}}// No implementation found. Try method resolver once.// 6. 假设还是找不到就转发if (!triedResolver) {unlockForMethodLookup();_class_resolveMethod(cls, sel);// Don't cache the result; we don't hold the lock so it may have // changed already. Re-do the search from scratch instead.triedResolver = YES;goto retry;}// No implementation found, and method resolver didn't help. // Use forwarding._cache_addForwardEntry(cls, sel);methodPC = &_objc_msgForward_internal;done:unlockForMethodLookup();// paranoia: look for ignored selectors with non-ignored implementationsassert(!(ignoreSelector(sel) && methodPC != (IMP)&_objc_ignored_method));return methodPC;
}
不同的类能够有同样的方法名,方法链表中依据方法名去查找详细的方法实现的.
IMP 是一个函数指针, 这个被指向的函数包括一个接收消息的对象id(self指针), 调用方法的选标SEL(方法名), 及不定个数的方法參数, 并返回一个id。
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
给cls加入一个新的方法,若干cls存在这种方法则返回失败
以下来看代码
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
{if (!cls) return NO;rwlock_write(&runtimeLock);IMP old = addMethod(newcls(cls), name, imp, types ?: "", NO);rwlock_unlock_write(&runtimeLock);return old ? NO : YES;
}static IMP addMethod(class_t *cls, SEL name, IMP imp, const char *types, BOOL replace)
{IMP result = NULL;rwlock_assert_writing(&runtimeLock);assert(types);assert(isRealized(cls));method_t *m;// 1. 在自己的类的方法列表里找这种方法if ((m = getMethodNoSuper_nolock(cls, name))) {// already existsif (!replace) {// 不代替, 返回 m->impresult = _method_getImplementation(m);} else {// 代替, 设置 cls 的 m 方法实现为 impresult = _method_setImplementation(cls, m, imp);}} else {// fixme optimize// 2. 建立一个method_list_t节点method_list_t *newlist;newlist = (method_list_t *)_calloc_internal(sizeof(*newlist), 1);newlist->entsize_NEVER_USE = (uint32_t)sizeof(method_t) | fixed_up_method_list;newlist->count = 1;newlist->first.name = name;newlist->first.types = strdup(types);if (!ignoreSelector(name)) {newlist->first.imp = imp;} else {newlist->first.imp = (IMP)&_objc_ignored_method;}// 3. 把newlist加到cls的方法列表里BOOL vtablesAffected = NO;attachMethodLists(cls, &newlist, 1, NO, &vtablesAffected);// 4. 刷新cls缓存flushCaches(cls);if (vtablesAffected) flushVtables(cls);result = NULL;}return result;
}
我们用class_addMethod时, replace == NO, 所以cls已经存在这种方法的时候加入是失败的
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
替换cls的name方法的指针
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
{if (!cls) return NULL;return _class_addMethod(cls, name, imp, types, YES);
}
泪目, 这里就是直接设置replace == YES.
void method_exchangeImplementations(Method m1_gen, Method m2_gen)
交换2个方法的实现指针
void method_exchangeImplementations(Method m1_gen, Method m2_gen)
{method_t *m1 = newmethod(m1_gen);method_t *m2 = newmethod(m2_gen);if (!m1 || !m2) return;rwlock_write(&runtimeLock);if (ignoreSelector(m1->name) || ignoreSelector(m2->name)) {// Ignored methods stay ignored. Now they're both ignored.m1->imp = (IMP)&_objc_ignored_method;m2->imp = (IMP)&_objc_ignored_method;rwlock_unlock_write(&runtimeLock);return;}// 交换2个方法的实现指针IMP m1_imp = m1->imp;m1->imp = m2->imp;m2->imp = m1_imp;if (vtable_containsSelector(m1->name) || vtable_containsSelector(m2->name)) {// Don't know the class - will be slow if vtables are affected// fixme build list of classes whose Methods are known externally?
flushVtables(NULL); } // fixme catch NSObject changing to custom RR // cls->setCustomRR(); // fixme update monomorphism if necessary rwlock_unlock_write(&runtimeLock); }
事实上这里有个坑, Method是怎么来的呢, 通过class_getInstanceMethod,假设子类没有的话,会返回父类的方法, 假设这个时候在用method_exchangeImplementations替换,会把父类替的方法替换掉,这显然不是我们想要的.所以呢,我们的method swizzle一般是这么写
static void XY_swizzleInstanceMethod(Class c, SEL original, SEL replacement)
{Method a = class_getInstanceMethod(c, original);Method b = class_getInstanceMethod(c, replacement);if (class_addMethod(c, original, method_getImplementation(b), method_getTypeEncoding(b))){class_replaceMethod(c, replacement, method_getImplementation(a), method_getTypeEncoding(a));}else{method_exchangeImplementations(a, b); }
}
IMP method_getImplementation(Method method)
返回method的实现指针
代码例如以下, 没什么好说的,事实上就是返回method->imp
IMP method_getImplementation(Method m)
{return _method_getImplementation(newmethod(m));
}static IMP _method_getImplementation(method_t *m)
{if (!m) return NULL;return m->imp;
}
IMP method_setImplementation(Method method, IMP imp)
设置方法的新的实现指针, 返回旧的实现指针
IMP method_setImplementation(Method m, IMP imp)
{// Don't know the class - will be slow if vtables are affected// fixme build list of classes whose Methods are known externally?IMP result;rwlock_write(&runtimeLock);result = _method_setImplementation(Nil, newmethod(m), imp);rwlock_unlock_write(&runtimeLock);return result;
}static IMP _method_setImplementation(class_t *cls, method_t *m, IMP imp)
{rwlock_assert_writing(&runtimeLock);if (!m) return NULL;if (!imp) return NULL;if (ignoreSelector(m->name)) {// Ignored methods stay ignoredreturn m->imp;}// 替换方法的实现指针IMP old = _method_getImplementation(m);m->imp = imp;// No cache flushing needed - cache contains Methods not IMPs.if (vtable_containsSelector(newmethod(m)->name)) {// Will be slow if cls is NULL (i.e. unknown)// fixme build list of classes whose Methods are known externally?
flushVtables(cls); } // fixme catch NSObject changing to custom RR // cls->setCustomRR(); // fixme update monomorphism if necessary return old; }
method_getTypeEncoding(Method m)
返回方法m的參数和返回值的描写叙述的字串
这个就是直接返回m->types
转载于:https://www.cnblogs.com/brucemengbm/p/7016320.html
Objective-C method及相关方法分析相关推荐
- Android-----View绘制流程以及invalidate()等相关方法分析 .
引自:http://blog.csdn.net/qinjuning/article/details/7110211 前言: 本文是我读<Android内核剖析>第13章----View工作 ...
- Android中View绘制流程以及invalidate()等相关方法分析
...
- Android面试,View绘制流程以及invalidate()等相关方法分析
整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为 根据之前设置的状态,判断是否需要重新计算视图大小(measu ...
- 历史文件备份,原文件已损失
zzf073的博客 - 第6页 - DevDiv开发者社区 - Powered by DEVDIV.COM! zzf073的博客 - 第4页 - DevDiv开发者社区 - Powered by DE ...
- 扩增子分析QIIME2(2018.6). 1简介和安装
扩增子分析QIIME2. 1简介和安装 QIIME2版本 2018.6 简介 优点 学习思路 什么是QIIME 2? 核心概念 安装 原生安装QIIME2 虚拟机安装 使用VirtualBox方式安装 ...
- Asterisk 1.8 sip 协议栈分析
引用自:http://blog.csdn.net/z1623866465/archive/2011/01/02/6113057.aspx 看了一下 asterisk 1.8 ,chan_sip 更新了 ...
- 【Android 逆向】Android 逆向通用工具开发 ( 静态库项目中的网络操作核心类 CNetwork 分析 )
文章目录 一.adabingo 静态库项目中的网络操作核心类 CNetwork 分析 一.adabingo 静态库项目中的网络操作核心类 CNetwork 分析 CNetwork 相关方法分析 : 等 ...
- Android中ActivityManagerService与应用程序(客户端)通信模型分析
今天主要分析下ActivityManagerService(服务端) 与应用程序(客户端)之间的通信模型,在介绍这个通信模型的基础上,再 简单介绍实现这个模型所需要数据类型. 本文所介绍内容基于and ...
- Android Retrofit实现原理分析
retrofit有几个关键的地方. 1.用户自定义的接口和接口方法.(由动态代理创建对象.) 2.converter转换器.(把response转换为一个具体的对象) 3.注解的使用. 让我们跟随Ap ...
最新文章
- Altium Protel PCB Layer
- 程序员面试金典 - 面试题 01.06. 字符串压缩(字符串)
- 其实在直播平台买东西的客户最愚蠢
- IE和FF获得键盘码
- word2016 图片去底灰_87平开门见厅,镜面扩容,将黑白灰用到极致,不奢华但精致...
- python绘图之散点图
- 程序员必读的30本书籍
- python 对象转json
- 软件工程-第2章复习总结
- explain是mysql的关键字吗_mysql 中的explain关键字
- mysql 统计请假天数_知道请假时间段、工作日和工作时长,计算请假天数
- 使用mysql进行身份证校验
- 80%中国男人不敢主动和女人搭讪
- 如何取消windows xp开机时的登录界面
- Android APP - GPS定位并获取地理位置
- 圣迭戈与哥大电子计算机,加州大学圣迭戈分校
- uniapp调用百度智能云身份证识别
- 西藏春运送服务 让旅客带着温暖出发
- ASP.NET MVC5 多语言国际化
- 微信小程序云开发之云数据库入门
热门文章
- java(3)——数据类型中的数值型的整数类型
- 小试用python搭建自己的web服务器
- POJ 2483 Cows(树状数组)
- Logrotate 对服务器日志按照小时切割并压缩
- gcc/g++ 编译器出现 undefined reference to ‘这里是函数名‘,往往意味这这个函数没有定义
- android连接usb后默认app,android – 记住USB连接,但不要启动应用程序
- 2vec需要归一化吗_LTSM模型预测数据如何归一化?(知乎回答)
- 计算机软件 教案,计算机软件系统教案
- 关于android的外文论文,关于android的外文文献.doc
- 数据仓库之 ETL漫谈