Object-c?反射技术
背景:
App?随着迭代的发展,需要把c++与一些算法进行解耦,这样oc runtime的 反射可以利用,这样当app如果集成了算法组件,通过查找是否有对应class,从而可以动态判断方法是否可用。
在正式代码之前,先看下几个方法对应的含义
objc_getClass
这个是反射的核心,通过一个字符串构查找?类对象
C++ Class objc_getClass(const?char?*aClassName) { ????if?(!aClassName)?return?Nil; ? ????// NO unconnected, YES class handler ????return?look_up_class(aClassName,?NO,?YES); } ? ?。。。。 ? ? /*********************************************************************** * look_up_class * Look up a class by name, and realize it. * Locking: acquires runtimeLock **********************************************************************/ static?BOOL?empty_getClass(const?char?*name, Class *outClass) { ????*outClass =?nil; ????return?NO; } ? static?ChainedHookFunction<objc_hook_getClass> GetClassHook{empty_getClass}; ? void?objc_setHook_getClass(objc_hook_getClass newValue, ???????????????????????????objc_hook_getClass *outOldValue) { ????GetClassHook.set(newValue, outOldValue); } ? Class? look_up_class(const?char?*name,? ??????????????bool?includeUnconnected?__attribute__((unused)),? ??????????????bool?includeClassHandler?__attribute__((unused))) { ????if?(!name)?return?nil; ? ????Class result; ????bool?unrealized; ????{ ????????runtimeLock.lock(); ????????result = getClassExceptSomeSwift(name); ????????unrealized = result ?&& ?!result->isRealized(); ????????if?(unrealized) { ????????????result = realizeClassMaybeSwiftAndUnlock(result, runtimeLock); ????????????// runtimeLock is now unlocked ????????}?else?{ ????????????runtimeLock.unlock(); ????????} ????} ? ????if?(!result) { ????????// Ask Swift about its un-instantiated classes. ? ????????// We use thread-local storage to prevent infinite recursion ????????// if the hook function provokes another lookup of the same name ????????// (for example, if the hook calls objc_allocateClassPair) ? ????????auto?*tls = _objc_fetch_pthread_data(true); ? ????????// Stop if this thread is already looking up this name. ????????for?(unsigned?i = 0; i < tls->classNameLookupsUsed; i++) { ????????????if?(0 == strcmp(name, tls->classNameLookups[i])) { ????????????????return?nil; ????????????} ????????} ? ????????// Save this lookup in tls. ????????if?(tls->classNameLookupsUsed == tls->classNameLookupsAllocated) { ????????????tls->classNameLookupsAllocated = ????????????????(tls->classNameLookupsAllocated * 2 ?: 1); ????????????size_t size = tls->classNameLookupsAllocated * ????????????????sizeof(tls->classNameLookups[0]); ????????????tls->classNameLookups = (const?char?**) ????????????????realloc(tls->classNameLookups, size); ????????} ????????tls->classNameLookups[tls->classNameLookupsUsed++] = name; ? ????????// Call the hook. ????????Class swiftcls =?nil; ????????if?(GetClassHook.get()(name, &swiftcls)) { ????????????ASSERT(swiftcls->isRealized()); ????????????result = swiftcls; ????????} ? ????????// Erase the name from tls. ????????unsigned?slot = --tls->classNameLookupsUsed; ????????ASSERT(slot >= 0 ?&& ?slot < tls->classNameLookupsAllocated); ????????ASSERT(name == tls->classNameLookups[slot]); ????????tls->classNameLookups[slot] =?nil; ????} ? ????return?result; } ? |
能看的出来?会从tls(Thread?Local?Storage),classNameLookups,查找对应的class
class_getClassMethod
C++ ?OBJC_EXPORT Method?_Nullable class_getClassMethod(Class?_Nullable?cls,?SEL?_Nonnull?name)
?。。。。。 ?NEVER_INLINE IMP?lookUpImpOrForward(id?inst,?SEL?sel, Class cls,?int?behavior) { ????const?IMP?forward_imp = (IMP)_objc_msgForward_impcache; ????IMP?imp =?nil; ????Class curClass; ? ????runtimeLock.assertUnlocked(); ? ????if?(slowpath(!cls->isInitialized())) { ????????// The first message sent to a class is often +new or +alloc, or +self ????????// which goes through objc_opt_* or various optimized entry points. ????????// ????????// However, the class isn't realized/initialized yet at this point, ????????// and the optimized entry points fall down through objc_msgSend, ????????// which ends up here. ????????// ????????// We really want to avoid caching these, as it can cause IMP caches ????????// to be made with a single entry forever. ????????// ????????// Note that this check is racy as several threads might try to ????????// message a given class for the first time at the same time, ????????// in which case we might cache anyway. ????????behavior |= LOOKUP_NOCACHE; ????} ? ????// runtimeLock is held during isRealized and isInitialized checking ????// to prevent races against concurrent realization. ? ????// runtimeLock is held during method search 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. ? ????runtimeLock.lock(); ? ????// We don't want people to be able to craft a binary blob that looks like ????// a class but really isn't one and do a CFI attack. ????// ????// To make these harder we want to make sure this is a class that was ????// either built into the binary or legitimately registered through ????// objc_duplicateClass, objc_initializeClassPair or objc_allocateClassPair. ????checkIsKnownClass(cls); ? ????cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE); ????// runtimeLock may have been dropped but is now locked again ????runtimeLock.assertLocked(); ????curClass = cls; ? ????// The code used to lookup the class's cache again right after ????// we take the lock but for the vast majority of the cases ????// evidence shows this is a miss most of the time, hence a time loss. ????// ????// The only codepath calling into this without having performed some ????// kind of cache lookup is class_getInstanceMethod(). ? ????for?(unsigned?attempts = unreasonableClassCount();;) { ????????if?(curClass->cache.isConstantOptimizedCache(/* strict */true)) { #if CONFIG_USE_PREOPT_CACHES ????????????imp = cache_getImp(curClass, sel); ????????????if?(imp)?goto?done_unlock; ????????????curClass = curClass->cache.preoptFallbackClass(); #endif ????????}?else?{ ????????????// curClass method list. ????????????Method meth = getMethodNoSuper_nolock(curClass, sel); ????????????if?(meth) { ????????????????imp = meth->imp(false); ????????????????goto?done; ????????????} ? ????????????if?(slowpath((curClass = curClass->getSuperclass()) ==?nil)) { ????????????????// No implementation found, and method resolver didn't help. ????????????????// Use forwarding. ????????????????imp = forward_imp; ????????????????break; ????????????} ????????} ? ????????// Halt if there is a cycle in the superclass chain. ????????if?(slowpath(--attempts == 0)) { ????????????_objc_fatal("Memory corruption in class list."); ????????} ? ????????// Superclass cache. ????????imp = cache_getImp(curClass, sel); ????????if?(slowpath(imp == forward_imp)) { ????????????// Found a forward:: entry in a superclass. ????????????// Stop searching, but don't cache yet; call method ????????????// resolver for this class first. ????????????break; ????????} ????????if?(fastpath(imp)) { ????????????// Found the method in a superclass. Cache it in this class. ????????????goto?done; ????????} ????} ? ????// No implementation found. Try method resolver once. ? ????if?(slowpath(behavior & LOOKUP_RESOLVER)) { ????????behavior ^= LOOKUP_RESOLVER; ????????return?resolveMethod_locked(inst, sel, cls, behavior); ????} ? ?done: ????if?(fastpath((behavior & LOOKUP_NOCACHE) == 0)) { #if CONFIG_USE_PREOPT_CACHES ????????while?(cls->cache.isConstantOptimizedCache(/* strict */true)) { ????????????cls = cls->cache.preoptFallbackClass(); ????????} #endif ????????log_and_fill_cache(cls, imp, sel, inst, curClass); ????} ?done_unlock: ????runtimeLock.unlock(); ????if?(slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) { ????????return?nil; ????} ????return?imp; } |
lookUpImpOrForward?网上有很多资料介绍这个方法,这个是整个反射的核心,通过Class找到对应的方法,具体过程可以参考其他的网上资料,核心无非先从cache找有无方法,没有在从class的方法列表找,还没有可以看下能否进行消息转发等等,最终就是找到这个methed
有了class method?就可以提取方法的函数指针,有了函数指针就可以反射调用了
实例方法反射
C++ ?{ ????????????Class cls = objc_getClass("NSString"); ????????????NSObject *pObject = [cls alloc]; ????????????Method thmod= ?class_getInstanceMethod(cls,?@selector(init)); ????????????IMP?funPtr = ??method_getImplementation(thmod); ????????????NSString *pS = ?((id(*)(id,?SEL))funPtr)(pObject,?@selector(init)); ???????????? ??} |
Oc?方法转成c?后默认前边都会id,?SEL,就像c++类方法,默认会有this指针
类方法反射
C++ ?Class metaCls = objc_getMetaClass("NSString"); if?(metaCls) { ???Method thmod = ?class_getClassMethod(cls,?@selector(string)); ???IMP?funPtr = ??method_getImplementation(thmod); ???NSString *pS = ?((id(*)(id,?SEL))funPtr)(cls,?@selector(string));
} |
class_getClassMethod
C++ Method class_getClassMethod(Class cls,?SEL?sel) { ????if?(!cls ?|| ?!sel)?return?nil; ? ????return?class_getInstanceMethod(cls->getMeta(), sel); } ?????Class getMeta() { ????????if?(isMetaClassMaybeUnrealized())?return?(Class)this; ????????else?return?this->ISA(); ????} |
可以看的出来?如果cls是类对象,则会直接去ISA找到类对象,元类对象直接返回
所以类方法反射的时候可以穿metaClass,也可以Class
|