## 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 version
if (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 selectors
if (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 implementations
assert(!(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 exists
if (!replace) {
// 不代替, 返回 m->imp
result = _method_getImplementation(m);
} else {
// 代替, 设置 cls 的 m 方法实现为 imp
result = _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 ignored
return 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

Objective-C method及相关方法分析的更多相关文章

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

    [原文]http://blog.csdn.net/qinjuning 整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简 ...

  2. Android中View绘制流程以及invalidate()等相关方法分析(转载的文章,出处在正文已表明)

    转载请注明出处:http://blog.csdn.net/qinjuning 前言: 本文是我读<Android内核剖析>第13章----View工作原理总结而成的,在此膜拜下作者 .同时 ...

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

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

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

    转自:http://blog.csdn.net/qinjuning 前言: 本文是我读<Android内核剖析>第13章----View工作原理总结而成的,在此膜拜下作者 .同时真挚地向渴 ...

  5. Dalvik虚拟机java方法执行流程和Method结构体分析

    Method结构体是啥? 在Dalvik虚拟机内部,每个Java方法都有一个对应的Method结构体,虚拟机根据此结构体获取方法的所有信息. Method结构体是怎样定义的? 此结构体在不同的andr ...

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

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

  7. 平摊分析 Amortized Analysis ------geeksforgeeks翻译

    当偶尔一切操作很花的时间很慢,而大多数操作的时间都很快的时候,平摊分析的方法就很很好用了.在平摊分析中,我们分析一串操作并且可以得到最坏情况下的平均时间复杂度.例如hash table, disjoi ...

  8. Android Retrofit实现原理分析

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

  9. 【spring源码分析】IOC容器初始化(九)

    前言:上篇文章末尾提到createBeanInstance方法中使用工厂方法实例化Bean对象,本文将对该方法进行分析. AbstractAutowireCapableBeanFactory#inst ...

随机推荐

  1. Find or Query Data with the mongo Shell

    https://docs.mongodb.com/getting-started/shell/query/ Overview You can use the find() method to issu ...

  2. m_Orchestrate learning system---二十三、如何搜索概念图插件

    m_Orchestrate learning system---二十三.如何搜索概念图插件 一.总结 一句话总结:要在百度你们搜索前端组件,前端组件  概念图工具,js概念图工具等等这些 用的话用go ...

  3. ★★★【卡法 常用js库】: js汇合 表单验证 cookie设置 日期格式 电话手机号码 email 整数 小数 金额 检查参数长度

    [卡法 常用js库]: js汇合 表单验证  cookie设置  日期格式  电话手机号码  email  整数  小数  金额   检查参数长度 // +---------------------- ...

  4. 系统丢失的DLL文件问题根源解决(纯净官网下载放心)(图文详解)(博主推荐)

    导言 最近,身边的朋友们,问我,他电脑的win10系统里 mfc110.dll 丢失. 其他的系统文件丢失修复,是一样的步骤. 现象 大家也许,都会有这么一个习惯,动不动则就去百度上搜索. 其实啊,这 ...

  5. OpenCV问题集锦,图片显示不出来的问题,cvWaitKey(0),不能读图片,未经处理的异常,等问题集合

    昨天根据uc伯克利的人工图像分割文件.seg,显示图像的时候调用了OpenCV的库函数,图片都能用imwrite写好,但是imshow死活显示不出来. 今天早上发现原来是imshow()后面应该加上: ...

  6. AS3 常见问题

    SharedObject 不起作用(exe, air中) var so:SharedObject = SharedObject.getLocal("aa", "/&quo ...

  7. BootStrap--from 表单

    1 垂直表单(默认) 2 内联表单 3 水平表单 使用 class .sr-only,您可以隐藏内联表单的标签. 垂直或基本表单 基本的表单结构是 Bootstrap 自带的,个别的表单控件自动接收一 ...

  8. [译] 我最终是怎么玩转了 Vue 的作用域插槽

    原文链接:https://juejin.im/post/5c8856e6e51d456b30397f31#comment 原文地址:How I finally got my head around S ...

  9. [NOIP2016普及组]魔法阵

    题目:洛谷P2119.Vijos P2012.codevs5624. 题目大意:有n件物品,每件物品有个魔法值.要求组成魔法阵(Xa,Xb,Xc,Xd),该魔法阵要满足Xa<Xb<Xc&l ...

  10. python3之对本地TXT文件进行增加,删除,修改,查看功能。

    由于是初学,代码如有不足,欢迎指出! 本博客记录我的编程之路,记录所学到的知识,分享所学心得! 这是我的一个作业. 首先分析要求: 创建一个TXT文件用于存储账号与密码 实现对文件进行增加,删除,修改 ...