## Objective-C method及相关方法分析

转载请注名出处 [http://blog.csdn.net/uxyheaven](http://blog.csdn.net/uxyheaven/article/details/38120335)

本篇文章将探究一下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及相关方法分析相关推荐

  1. Android-----View绘制流程以及invalidate()等相关方法分析 .

    引自:http://blog.csdn.net/qinjuning/article/details/7110211 前言: 本文是我读<Android内核剖析>第13章----View工作 ...

  2. Android中View绘制流程以及invalidate()等相关方法分析

                                                                                                        ...

  3. Android面试,View绘制流程以及invalidate()等相关方法分析

    整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为 根据之前设置的状态,判断是否需要重新计算视图大小(measu ...

  4. 历史文件备份,原文件已损失

    zzf073的博客 - 第6页 - DevDiv开发者社区 - Powered by DEVDIV.COM! zzf073的博客 - 第4页 - DevDiv开发者社区 - Powered by DE ...

  5. 扩增子分析QIIME2(2018.6). 1简介和安装

    扩增子分析QIIME2. 1简介和安装 QIIME2版本 2018.6 简介 优点 学习思路 什么是QIIME 2? 核心概念 安装 原生安装QIIME2 虚拟机安装 使用VirtualBox方式安装 ...

  6. Asterisk 1.8 sip 协议栈分析

    引用自:http://blog.csdn.net/z1623866465/archive/2011/01/02/6113057.aspx 看了一下 asterisk 1.8 ,chan_sip 更新了 ...

  7. 【Android 逆向】Android 逆向通用工具开发 ( 静态库项目中的网络操作核心类 CNetwork 分析 )

    文章目录 一.adabingo 静态库项目中的网络操作核心类 CNetwork 分析 一.adabingo 静态库项目中的网络操作核心类 CNetwork 分析 CNetwork 相关方法分析 : 等 ...

  8. Android中ActivityManagerService与应用程序(客户端)通信模型分析

    今天主要分析下ActivityManagerService(服务端) 与应用程序(客户端)之间的通信模型,在介绍这个通信模型的基础上,再 简单介绍实现这个模型所需要数据类型. 本文所介绍内容基于and ...

  9. Android Retrofit实现原理分析

    retrofit有几个关键的地方. 1.用户自定义的接口和接口方法.(由动态代理创建对象.) 2.converter转换器.(把response转换为一个具体的对象) 3.注解的使用. 让我们跟随Ap ...

最新文章

  1. Altium Protel PCB Layer
  2. 程序员面试金典 - 面试题 01.06. 字符串压缩(字符串)
  3. 其实在直播平台买东西的客户最愚蠢
  4. IE和FF获得键盘码
  5. word2016 图片去底灰_87平开门见厅,镜面扩容,将黑白灰用到极致,不奢华但精致...
  6. python绘图之散点图
  7. 程序员必读的30本书籍
  8. python 对象转json
  9. 软件工程-第2章复习总结
  10. explain是mysql的关键字吗_mysql 中的explain关键字
  11. mysql 统计请假天数_知道请假时间段、工作日和工作时长,计算请假天数
  12. 使用mysql进行身份证校验
  13. 80%中国男人不敢主动和女人搭讪
  14. 如何取消windows xp开机时的登录界面
  15. Android APP - GPS定位并获取地理位置
  16. 圣迭戈与哥大电子计算机,加州大学圣迭戈分校
  17. uniapp调用百度智能云身份证识别
  18. 西藏春运送服务 让旅客带着温暖出发
  19. ASP.NET MVC5 多语言国际化
  20. 微信小程序云开发之云数据库入门

热门文章

  1. java(3)——数据类型中的数值型的整数类型
  2. 小试用python搭建自己的web服务器
  3. POJ 2483 Cows(树状数组)
  4. Logrotate 对服务器日志按照小时切割并压缩
  5. gcc/g++ 编译器出现 undefined reference to ‘这里是函数名‘,往往意味这这个函数没有定义
  6. android连接usb后默认app,android – 记住USB连接,但不要启动应用程序
  7. 2vec需要归一化吗_LTSM模型预测数据如何归一化?(知乎回答)
  8. 计算机软件 教案,计算机软件系统教案
  9. 关于android的外文论文,关于android的外文文献.doc
  10. 数据仓库之 ETL漫谈