在同時(shí)使用RAC和Aspects的時(shí)候,遇到了一個(gè)Crash,棧溢出了。
看了一下,是之前在項(xiàng)目中使用了RAC的rac_singalForSelector
@weakify(self);
[[viewController rac_signalForSelector:@selector(viewDidAppear:)] subscribeNext
:^(id x) {
@strongify(self);
self.shouldIgnorePushingViewControllers = NO;
}];
后來(lái)他又使用了Aspects庫(kù)中的aspect_hookSelector
[UIViewController aspect_hookSelector:@selector(viewDidAppear:) withOptions
:AspectPositionAfter usingBlock:^(id<AspectInfo> info, BOOL animated) {
NSLog(@"AOP: %@ - %d", [info.instance class], animated);
} error:NULL];
后來(lái)就仔細(xì)看了一下兩者的實(shí)現(xiàn),從接口上來(lái)說(shuō),RAC的rac_singalForSelector的實(shí)例對(duì)象方法Aspects的aspect_hookSelector類(lèi)對(duì)象方法。
第一感覺(jué)是前者針對(duì)是實(shí)例對(duì)象,后者針對(duì)的是類(lèi)對(duì)象。主要涉及的其實(shí)還是對(duì)象模型里的一些知識(shí),操作了類(lèi)對(duì)象和實(shí)例對(duì)象中的數(shù)據(jù)如果對(duì)對(duì)象模型不太熟悉,可以看一下我之前寫(xiě)的一篇Blog:對(duì)象模型
現(xiàn)在來(lái)分析下原因,從源碼入手,先來(lái)大致看RAC相關(guān)的源碼:
static RACSignal *NSObjectRACSignalForSelector(NSObject *self, SEL selector,
Protocol *protocol) {
//aliasSelector是為了區(qū)分原Selector,RAC加了自己的前綴
SEL aliasSelector = RACAliasForSelector(selector);
@synchronized (self) {
//這里是根據(jù)aliasSelector獲得相應(yīng)的熱信號(hào),這個(gè)熱信號(hào)主要是用于通知業(yè)務(wù)層的回調(diào)
//也就是subscribeNext后面那一段block
RACSubject *subject = objc_getAssociatedObject(self
, aliasSelector);
if (subject != nil) return subject;
//這個(gè)地方是基于當(dāng)前self類(lèi)創(chuàng)建了一個(gè)新類(lèi)型,有很多hook的邏輯都放在這個(gè)類(lèi)型中
//具體邏輯在RACSwizzleClass函數(shù)中再分析
Class class = RACSwizzleClass(self);
NSCAssert(class != nil, @"Could not swizzle class of %@", self);
//創(chuàng)建一個(gè)用于通知的熱信號(hào)
subject = [[RACSubject subject] setNameWithFormat:@"%@
-rac_signalForSelector: %s",
self.rac_description,
sel_getName(selector)];
//通過(guò)關(guān)聯(lián)對(duì)象的方式存儲(chǔ)熱信號(hào)
objc_setAssociatedObject(self,
aliasSelector,
subject,
OBJC_ASSOCIATION_RETAIN);
[self.rac_deallocDisposable addDisposable:
[RACDisposable disposableWithBlock:^{
[subject sendCompleted];
}]];
//targetMethod是"viewDidApear"這個(gè)Selector的方法對(duì)象
Method targetMethod = class_getInstanceMethod(class, selector);
if (targetMethod == NULL) {
//按照現(xiàn)有邏輯,這里應(yīng)該是走不到的,先不去看它
const char *typeEncoding;
if (protocol == NULL) {
typeEncoding = RACSignatureForUndefinedSelector(selector);
} else {
// Look for the selector as an optional instance method.
struct objc_method_description methodDescription =
protocol_getMethodDescription(protocol, selector, NO, YES);
if (methodDescription.name == NULL) {
// Then fall back to looking for a required
//instance method.
methodDescription =
protocol_getMethodDescription(protocol
, selector
, YES
, YES);
NSCAssert(methodDescription.name != NULL
, @"Selector %@ does not exist in <%s>"
,NSStringFromSelector(selector)
, protocol_getName(protocol));
}
typeEncoding = methodDescription.types;
}
RACCheckTypeEncoding(typeEncoding);
// Define the selector to call -forwardInvocation:.
if (!class_addMethod(class,
selector,
_objc_msgForward,
typeEncoding)) {
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey:
[NSString stringWithFormat:
NSLocalizedString(
@"A race condition occurred
implementing %@ on class %@"
, nil)
,NSStringFromSelector(selector)
, class],
NSLocalizedRecoverySuggestionErrorKey:
NSLocalizedString(@"Invoke -rac_signalForSelector:
again to override the implementation.", nil)
};
return [RACSignal error:[NSError errorWithDomain:
RACSelectorSignalErrorDomain
code:RACSelectorSignalErrorMethodSwizzlingRace
userInfo:userInfo]];
}
//_objc_msgForward是消息進(jìn)行轉(zhuǎn)發(fā)的開(kāi)始,這里主要是判斷原始的
//方法對(duì)象有沒(méi)有被hook成_objc_msgForward,因?yàn)橄旅嬉_(kāi)始進(jìn)行方法實(shí)現(xiàn)的替換了
} else if (method_getImplementation(targetMethod) !=
_objc_msgForward) {
// Make a method alias for the existing
//method implementation.
const char *typeEncoding =
method_getTypeEncoding(targetMethod);
RACCheckTypeEncoding(typeEncoding);
//class_addMethod只是在當(dāng)前類(lèi)型對(duì)象(不會(huì)去基類(lèi)中查找)中
//去找有沒(méi)有aliasSelector的方法,如果有就直接返回原來(lái)的方法實(shí)現(xiàn)
//如果沒(méi)有就添加一個(gè)
BOOL addedAlias __attribute__((unused)) =
class_addMethod(class, aliasSelector,
method_getImplementation(targetMethod),
typeEncoding);
NSCAssert(addedAlias, @"Original implementation
for %@ is already copied to %@ on %@",
NSStringFromSelector(selector),
NSStringFromSelector(aliasSelector), class);
// Redefine the selector to call -forwardInvocation:.
// 這里相當(dāng)于對(duì)原始方法進(jìn)行方法實(shí)現(xiàn)的替換,
//只要調(diào)用原始方法,直接會(huì)開(kāi)始方法轉(zhuǎn)發(fā)的過(guò)程
class_replaceMethod(class
, selector
, _objc_msgForward,
method_getTypeEncoding(targetMethod));
}
return subject;
}
}
通過(guò)原始英文注釋加上后來(lái)的中文注釋?xiě)?yīng)該能比較好的理解大致的過(guò)程了
分為2部分
1是創(chuàng)建用于通知業(yè)務(wù)層回調(diào)的熱信號(hào)
2是Hook了當(dāng)前類(lèi),創(chuàng)建了新的子類(lèi)型,并將這個(gè)子類(lèi)型的原始selector進(jìn)行了方法實(shí)現(xiàn)的替換
既將原始selector實(shí)現(xiàn)替換成_objc_msgForward,增加了一個(gè)新的selector prefix_selector,
并將其實(shí)現(xiàn)為原始selector。另外個(gè)人覺(jué)得命名上最好是不要叫class了,可以改為subClass或者
swizzleClass,容易跟原始class搞混
接著就是RACSwizzleCalss函數(shù)了:
static Class RACSwizzleClass(NSObject *self) {
//self.class和object_getClass返回的都是當(dāng)前實(shí)例的類(lèi)對(duì)象,好像沒(méi)什么區(qū)別???
Class statedClass = self.class;
Class baseClass = object_getClass(self);
// The "known dynamic subclass" is the subclass generated by RAC.
// It's stored as an associated object on every instance that's already
// been swizzled, so that even if something else swizzles the class of
// this instance, we can still access the RAC generated subclass.
//這里官方說(shuō)明了子類(lèi)的用途,如果有其他地方swizzle了當(dāng)前類(lèi)對(duì)象,那也不太影響RAC自己的邏輯
//因?yàn)樗械男畔⒍急4嬖谧约簞?chuàng)建的子類(lèi)中。
Class knownDynamicSubclass =
objc_getAssociatedObject(self, RACSubclassAssociationKey);
if (knownDynamicSubclass != Nil) return knownDynamicSubclass;
NSString *className = NSStringFromClass(baseClass);
if (statedClass != baseClass) {
//這部分邏輯沒(méi)走到,先不看
@synchronized (swizzledClasses()) {
if (![swizzledClasses() containsObject:className]) {
RACSwizzleForwardInvocation(baseClass);
RACSwizzleRespondsToSelector(baseClass);
RACSwizzleGetClass(baseClass, statedClass);
RACSwizzleGetClass(object_getClass(baseClass),
statedClass);
RACSwizzleMethodSignatureForSelector(baseClass);
[swizzledClasses() addObject:className];
}
}
return baseClass;
}
//加上RAC自己的前綴,給新的子類(lèi)命名
const char *subclassName = [className stringByAppendingString:
RACSubclassSuffix].UTF8String;
Class subclass = objc_getClass(subclassName);
if (subclass == nil) {
//createClass里面調(diào)用的是objc_allocateClassPair函數(shù)用于創(chuàng)建新的類(lèi)型對(duì)象,
//它和下面的objc_registerClassPair函數(shù)是配對(duì)使用的
subclass = [RACObjCRuntime createClass:subclassName
inheritingFromClass:baseClass];
if (subclass == nil) return nil;
//這里是對(duì)新的子類(lèi)的forwardInvocation方法hook
RACSwizzleForwardInvocation(subclass);
//hookRespondsToSelector方法
RACSwizzleRespondsToSelector(subclass);
//hook了Class方法
RACSwizzleGetClass(subclass, statedClass);
//這里object_getClass得到的還是statedClass,既原始的類(lèi)。
RACSwizzleGetClass(object_getClass(subclass), statedClass);
//hook了方法簽名
RACSwizzleMethodSignatureForSelector(subclass);
//結(jié)束子類(lèi)的創(chuàng)建
objc_registerClassPair(subclass);
}
//這里是將當(dāng)前實(shí)例對(duì)象的類(lèi)型信息改寫(xiě)成新的子類(lèi)
object_setClass(self, subclass);
//保存子類(lèi)信息
objc_setAssociatedObject(self, RACSubclassAssociationKey, subclass,
OBJC_ASSOCIATION_ASSIGN);
return subclass;
}
RACSwizzleCalss函數(shù)里主要干的事情就是創(chuàng)建子類(lèi)型:
1.給新的子類(lèi)型命名
2.hook幾個(gè)重要的消息轉(zhuǎn)發(fā)方法,1是為了得到方法調(diào)用的入口 2是將子類(lèi)偽裝成原始的類(lèi)型
3.改變了實(shí)例對(duì)象的isa指針為新的子類(lèi),當(dāng)前實(shí)例對(duì)象的類(lèi)型信息為新的子類(lèi)信息
這里有必要看一下RACSwizzleGetClass函數(shù)的實(shí)現(xiàn):
static void RACSwizzleGetClass(Class class, Class statedClass) {
SEL selector = @selector(class);
Method method = class_getInstanceMethod(class, selector);
IMP newIMP = imp_implementationWithBlock(^(id self) {
//當(dāng)調(diào)用子類(lèi)的class方法的時(shí)候,返回的還是原始類(lèi)型對(duì)象
return statedClass;
});
class_replaceMethod(class
, selector
, newIMP
, method_getTypeEncoding(method));
}
這樣一來(lái),使用者感覺(jué)不到有什么異樣,不知道hook的存在了
最后重點(diǎn)看一下RACSwizzleForwardInvocation函數(shù):
static void RACSwizzleForwardInvocation(Class class) {
SEL forwardInvocationSEL = @selector(forwardInvocation:);
Method forwardInvocationMethod =
class_getInstanceMethod(class
, forwardInvocationSEL);
// Preserve any existing implementation of -forwardInvocation:.
void (*originalForwardInvocation)(id, SEL, NSInvocation *) = NULL;
if (forwardInvocationMethod != NULL) {
originalForwardInvocation =
(__typeof__(originalForwardInvocation))method_getImplementation(
forwardInvocationMethod);
}
// Set up a new version of -forwardInvocation:.
//
// If the selector has been passed to -rac_signalForSelector:, invoke
// the aliased method, and forward the arguments to any attached signals.
//
// If the selector has not been passed to -rac_signalForSelector:,
// invoke any existing implementation of -forwardInvocation:. If there
// was no existing implementation, throw an unrecognized selector
// exception.
id newForwardInvocation = ^(id self, NSInvocation *invocation) {
BOOL matched = RACForwardInvocation(self, invocation);
if (matched) return;
if (originalForwardInvocation == NULL) {
[self doesNotRecognizeSelector:invocation.selector];
} else {
//如果走到這里,基本上也要拋出異常了,一搬hook的都是必有的方法,已經(jīng)繞過(guò)了
//原來(lái)的方法查找的過(guò)程到了消息轉(zhuǎn)發(fā)這一步
originalForwardInvocation(self, forwardInvocationSEL,
invocation);
}
};
class_replaceMethod(class, forwardInvocationSEL,
imp_implementationWithBlock(newForwardInvocation)
, "v@:@");
}
用newForwardInvocation替換了原來(lái)的forwardInvocation實(shí)現(xiàn),里面又調(diào)用了RACForwardInvocation函數(shù):
static BOOL RACForwardInvocation(id self, NSInvocation *invocation) {
//這里的invocation還是原始的方法調(diào)用信息,手動(dòng)改成了帶前綴的selector
SEL aliasSelector = RACAliasForSelector(invocation.selector);
//獲取一下之前存儲(chǔ)的熱信號(hào)
RACSubject *subject = objc_getAssociatedObject(self, aliasSelector);
//這里的class得到的是RAC創(chuàng)建的子類(lèi),因?yàn)橹耙呀?jīng)改寫(xiě)了實(shí)例對(duì)象的ISA指針
Class class = object_getClass(invocation.target);
BOOL respondsToAlias = [class instancesRespondToSelector:aliasSelector];
if (respondsToAlias) {
//去改變selector為hook的新的selector,不然要死循環(huán)了
invocation.selector = aliasSelector;
//這一步就是真正原始的方法調(diào)用了,還記得之前已經(jīng)把a(bǔ)liasSelector的實(shí)現(xiàn)改為
//原始Selector的實(shí)現(xiàn)了吧
[invocation invoke];
}
if (subject == nil) return respondsToAlias;
//通知業(yè)務(wù)層,要監(jiān)聽(tīng)的selector已經(jīng)被調(diào)用了,這個(gè)發(fā)生在原始方法調(diào)用之后
[subject sendNext:invocation.rac_argumentsTuple];
return YES;
}
前面幾個(gè)函數(shù)都是為這一步打基礎(chǔ)的,到了真正方法調(diào)用的時(shí)候,原始方法的調(diào)用以及通知業(yè)務(wù)層回調(diào)都在這里完成。
通過(guò)以上分析,現(xiàn)在通過(guò)一張圖總結(jié)一下:

現(xiàn)在看一下Aspectsaspect_hookSelector的實(shí)現(xiàn),主要是aspect_prepareClassAndHookSelector函數(shù)
static void aspect_prepareClassAndHookSelector(NSObject *self, SEL selector
, NSError **error) {
NSCParameterAssert(selector);
//這里等看aspect_hookClass具體分析其過(guò)程,暫時(shí)來(lái)看是生成了新子類(lèi)
Class klass = aspect_hookClass(self, error);
//獲得原始方法對(duì)象
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]) {
//添加alias方法,實(shí)現(xiàn)為原方法實(shí)現(xiàn)
__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);
}
// We use forwardInvocation to hook in.
//這里跟RAC一樣的做法,把原方法實(shí)現(xiàn)換成了_objc_msgForward
//調(diào)原方法會(huì)直接開(kāi)始消息轉(zhuǎn)發(fā)的過(guò)程
class_replaceMethod(klass
, selector
, aspect_getMsgForwardIMP(self, selector)
,typeEncoding);
AspectLog(@"Aspects: Installed hook for -[%@ %@].", klass,
NSStringFromSelector(selector));
}
}
大致上來(lái)看,在方法實(shí)現(xiàn)的層面的處理和RAC很類(lèi)似.實(shí)際調(diào)試過(guò)程中,aspect_hookClass最終只是調(diào)用了aspect_swizzleClassInPlace,然后aspect_swizzleClassInPlace其實(shí)調(diào)用了aspect_swizzleForwardInvocation現(xiàn)在來(lái)看一下aspect_swizzleForwardInvocation函數(shù):
static void aspect_swizzleForwardInvocation(Class klass) {
NSCParameterAssert(klass);
// If there is no method, replace will act like class_addMethod.
//將forwardInvocation方法實(shí)現(xiàn)替換成了__ASPECTS_ARE_BEING_CALLED__
IMP originalImplementation = class_replaceMethod(klass
,@selector(forwardInvocation:)
,(IMP)__ASPECTS_ARE_BEING_CALLED__
,"v@:@");
if (originalImplementation) {
//alias的forwardInvocation換成了原始實(shí)現(xiàn)
class_addMethod(klass,
NSSelectorFromString(AspectsForwardInvocationSelectorName),
originalImplementation, "v@:@");
}
AspectLog(@"Aspects: %@ is now aspect aware.", NSStringFromClass(klass));
}
在消息轉(zhuǎn)發(fā)的最后關(guān)頭,調(diào)用ASPECTS_ARE_BEING_CALLED
static void __ASPECTS_ARE_BEING_CALLED__(__unsafe_unretained NSObject *self,
SEL selector,
NSInvocation *invocation) {
NSCParameterAssert(self);
NSCParameterAssert(invocation);
SEL originalSelector = invocation.selector;
//得到相應(yīng)的aliasSelector
SEL aliasSelector = aspect_aliasForSelector(invocation.selector);
//替換selector,因?yàn)閕nvocation中的selector還是原始的selector
//不然遞歸死循環(huán)跟RAC一樣
invocation.selector = aliasSelector;
//通過(guò)aliasSelector得到hook的用戶回調(diào)
AspectsContainer *objectContainer = objc_getAssociatedObject(self,
aliasSelector);
AspectsContainer *classContainer = aspect_getContainerForClass(
object_getClass(self),
aliasSelector);
AspectInfo *info = [[AspectInfo alloc] initWithInstance:self
invocation:invocation];
NSArray *aspectsToRemove = nil;
// Before hooks.
//觸發(fā)那些在原始邏輯調(diào)用之前的回調(diào)
aspect_invoke(classContainer.beforeAspects, info);
aspect_invoke(objectContainer.beforeAspects, info);
// Instead hooks.
BOOL respondsToAlias = YES;
if (objectContainer.insteadAspects.count ||
classContainer.insteadAspects.count) {
//直接是回調(diào)替換原始實(shí)現(xiàn)了
aspect_invoke(classContainer.insteadAspects, info);
aspect_invoke(objectContainer.insteadAspects, info);
}else {
Class klass = object_getClass(invocation.target);
do {
if ((respondsToAlias = [klass instancesRespondToSelector:
aliasSelector])) {
//這里是觸發(fā)原始方法的實(shí)現(xiàn)
[invocation invoke];
break;
}
}while (!respondsToAlias && (klass = class_getSuperclass(klass)));
}
// After hooks.
//觸發(fā)那些在原始方法調(diào)用之后的用戶回調(diào)
aspect_invoke(classContainer.afterAspects, info);
aspect_invoke(objectContainer.afterAspects, info);
// If no hooks are installed, call original implementation (usually
//to throw an exception)
if (!respondsToAlias) {
invocation.selector = originalSelector;
SEL originalForwardInvocationSEL =
NSSelectorFromString(AspectsForwardInvocationSelectorName);
if ([self respondsToSelector:originalForwardInvocationSEL]) {
((void( *)(id, SEL, NSInvocation *))objc_msgSend)(self,
originalForwardInvocationSEL,
invocation);
}else {
[self doesNotRecognizeSelector:invocation.selector];
}
}
// Remove any hooks that are queued for deregistration.
[aspectsToRemove makeObjectsPerformSelector:@selector(remove)];
}
看樣子,在這種情況下A并沒(méi)有想RAC一樣創(chuàng)建新的子類(lèi)型,它是直接更改了原始類(lèi)型的方法信息。
用圖來(lái)表示一下它當(dāng)前的實(shí)現(xiàn):

和RAC比要簡(jiǎn)單一些,那么當(dāng)同時(shí)使用兩個(gè)庫(kù)Hook的時(shí)候會(huì)出現(xiàn)什么情況呢?按照文章開(kāi)頭的場(chǎng)景,RAC相當(dāng)于Hook了某個(gè)繼承自UIViewController的子類(lèi),并基于該子類(lèi)創(chuàng)建了新的子類(lèi)型Aspects相當(dāng)于直接Hook了UIViewController,對(duì)UIViewController本身的方法實(shí)現(xiàn)進(jìn)行了更改替換現(xiàn)在假設(shè)繼承自UIViewController業(yè)務(wù)子類(lèi)型叫做BusinessVieweController
還是用一張簡(jiǎn)略圖來(lái)表示兩個(gè)庫(kù)Hook之后的場(chǎng)景吧:

在類(lèi)的繼承結(jié)構(gòu)下,Hook之后的結(jié)果,Aspects在UIViewController這一層,RAC在subClass這一層當(dāng)有外界調(diào)用A方法時(shí),subClass會(huì)最終首先調(diào)用原始A方法的實(shí)現(xiàn)。如果說(shuō)在BusisnessViewController類(lèi)型中定義了A方法的實(shí)現(xiàn),并且在A方法中調(diào)用了super的A方法,類(lèi)似:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
//業(yè)務(wù)代碼
//.....
}
那這個(gè)時(shí)候會(huì)繼續(xù)調(diào)用到UIViewController這一層,這個(gè)時(shí)候就會(huì)開(kāi)始走UIViewController這一層的Hook流程了,還是一樣最終會(huì)調(diào)用到forwardInvocation這個(gè)方法,但是這個(gè)時(shí)候subClass已經(jīng)實(shí)現(xiàn)了這個(gè)方法,所以調(diào)用流程會(huì)回到子類(lèi)subClass的Hook流程中,見(jiàn)上圖中右上角的紅色虛線部分。后面的邏輯就是無(wú)限循環(huán),在兩個(gè)類(lèi)對(duì)象中往返直到棧溢出。
通過(guò)上面的分析,如果說(shuō)在BusinessViewController中沒(méi)有調(diào)用super的方法,那么不會(huì)引起死循環(huán)的,因?yàn)椴粫?huì)觸發(fā)UIViewController類(lèi)對(duì)象的Hook流程。并且當(dāng)觸發(fā)了死循環(huán)之后,會(huì)發(fā)現(xiàn)RAC訂閱的業(yè)務(wù)層的回調(diào)和BusinessViewController的A方法中除了Super那一句,剩下的業(yè)務(wù)代碼也沒(méi)有被執(zhí)行。另外,RAC這種Hook相對(duì)來(lái)說(shuō)會(huì)更安全一些(并不是說(shuō)Aspects就沒(méi)有Hook Class 的情況,只是當(dāng)前是這樣):它完全子類(lèi)化了業(yè)務(wù)類(lèi),并且將Hook全都保留在了新建的子類(lèi)型中,對(duì)其他類(lèi)型包括原始業(yè)務(wù)類(lèi)型沒(méi)有干擾并且這種Hook只針對(duì)了某個(gè)具體的實(shí)例對(duì)象,其他勢(shì)力對(duì)象如果沒(méi)有通過(guò)調(diào)用rac_singalForSelector并不會(huì)受到任何影響。
如果是直接Hook原始類(lèi)型,那么影響的面將是非常廣的,包括所有的實(shí)例對(duì)象和所有的子類(lèi)對(duì)象。