Aspects是一個面向切面編程的庫。
若是想深刻了解iOS Runtime中的消息發送機制,Aspects的源碼是值得分析的。 git
項目主頁
Aspectsgithub
閱讀Aspects的源碼須要如下知識做爲基礎編程
閱讀本文以前,建議應該先斷點調試下Aspects的Demo,瞭解大體的過程。框架
Aspects的核心實現就是利用Runtime中的消息分發機制如圖:ide
Aspects經過把selector的方法替換爲msg_forward方法轉發 轉而調用 forwardInvocation(forwardInvocation的實現被Aspects替換,將原來的方法實現與添加的實現組合在了一塊兒)源碼分析
這是Aspects 面向切面編程的入口方法ui
- (id<AspectToken>)aspect_hookSelector:(SEL)selector
withOptions:(AspectOptions)options
usingBlock:(id)block
error:(NSError **)error {
return aspect_add(self, selector, options, block, error);
}複製代碼
這段代碼能夠分三部分來看spa
static id aspect_add(id self, SEL selector, AspectOptions options, id block, NSError **error) {
NSCParameterAssert(self);
NSCParameterAssert(selector);
NSCParameterAssert(block);
__block AspectIdentifier *identifier = nil;
aspect_performLocked(^{
if (aspect_isSelectorAllowedAndTrack(self, selector, options, error)) {
//一個實例 只有一個container
//這是區分實例對象和類對象的關鍵
//實例對象能夠有不少個,可是同一個類的類對象只能有一個
AspectsContainer *aspectContainer = aspect_getContainerForObject(self, selector);
//原來的selector block
identifier = [AspectIdentifier identifierWithSelector:selector object:self options:options block:block error:error];
if (identifier) {
//container 裏 存有 identifier (selector,block)
[aspectContainer addAspect:identifier withOptions:options];
// Modify the class to allow message interception.
aspect_prepareClassAndHookSelector(self, selector, error);
}
}
});
return identifier;
}複製代碼
aspect_prepareClassAndHookSelector這是核心的實現,涉及到動態生成子類,改變isa指針,改變方法的實現 一系列操做指針
static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector, NSError **error) {
NSCParameterAssert(selector);
//動態建立子類,改變forwardInvocation方法的實現
Class klass = aspect_hookClass(self, error);
Method targetMethod = class_getInstanceMethod(klass, selector);
IMP targetMethodIMP = method_getImplementation(targetMethod);
if (!aspect_isMsgForwardIMP(targetMethodIMP)) {
// Make a method alias for the existing method implementation, it not already copied.
const char *typeEncoding = method_getTypeEncoding(targetMethod);
SEL aliasSelector = aspect_aliasForSelector(selector);
if (![klass instancesRespondToSelector:aliasSelector]) {
//子類的aliasSelector的實現爲 當前類的selector
__unused BOOL addedAlias = class_addMethod(klass, aliasSelector, method_getImplementation(targetMethod), typeEncoding);
NSCAssert(addedAlias, @"Original implementation for %@ is already copied to %@ on %@", NSStringFromSelector(selector), NSStringFromSelector(aliasSelector), klass);
}
//selector方法替換爲_objc_msgForward
// We use forwardInvocation to hook in.
class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding);
AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass, NSStringFromSelector(selector));
}
}複製代碼
#pragma mark - Hook Class
static Class aspect_hookClass(NSObject *self, NSError **error) {
NSCParameterAssert(self);
//這裏能夠思考一下 class 方法 和 isa 的區別
//[self class] KVO可能改變了isa指針的指向
Class statedClass = self.class;
// object_getClass 能準確的找到isa指針
Class baseClass = object_getClass(self);
NSString *className = NSStringFromClass(baseClass);
// Already subclassed
//若是已經子類化了 就返回
if ([className hasSuffix:AspectsSubclassSuffix]) {
return baseClass;
//若是是類 就改掉類的forwardInvocation 而不是一個子類對象
// We swizzle a class object, not a single object.
}else if (class_isMetaClass(baseClass)) {
return aspect_swizzleClassInPlace((Class)self);
//考慮到KVO,KVO的底層實現,交換了isa指針
// Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place.
}else if (statedClass != baseClass) {
return aspect_swizzleClassInPlace(baseClass);
}
// Default case. Create dynamic subclass.
const char *subclassName = [className stringByAppendingString:AspectsSubclassSuffix].UTF8String;
Class subclass = objc_getClass(subclassName);
if (subclass == nil) {
// 經過建立新子類的方式
subclass = objc_allocateClassPair(baseClass, subclassName, 0);
if (subclass == nil) {
NSString *errrorDesc = [NSString stringWithFormat:@"objc_allocateClassPair failed to allocate class %s.", subclassName];
AspectError(AspectErrorFailedToAllocateClassPair, errrorDesc);
return nil;
}
// forwardInvocation 替換成 (IMP)_ASPECTS_ARE_BEING_CALLED__
aspect_swizzleForwardInvocation(subclass);
//子類的class方法返回當前被hook的對象的class
aspect_hookedGetClass(subclass, statedClass);
aspect_hookedGetClass(object_getClass(subclass), statedClass);
objc_registerClassPair(subclass);
}
//將當前self設置爲子類,這裏其實只是更改了self的isa指針而已, 這裏hook了子類的forwardInvocation方法,再次使用當前類時,實際上是使用了子類的forwardInvocation方法。
object_setClass(self, subclass);
return subclass;
}複製代碼
打個廣告嘿嘿
最近寫了個開源圖表框架
github.com/JunyiXie/XJ…
以爲不錯的話能夠給我個star
固然也歡迎issue!
代碼貢獻!調試