NSObject 分类
为了提供更多特性,ReactiveCocoa 框架为 NSObject 类扩展了多个分类。所以,所有 NSObject 的子类都可以调用这些分类中的方法。
RACDeallocating
RACDeallocating 分类中提供了一个 rac_willDeallocSignal
方法,该方法可以返回一个信号流,当该类的实例被释放时,这个信号流会立即传递一个结束信号量。所以,可以订阅这个信号流,从而在实例对象销毁时,执行一些操作。
@property (atomic, readonly, strong) RACCompoundDisposable *rac_deallocDisposable;
- (RACSignal *)rac_willDeallocSignal;
该特性实现的主要方法如下:
static void swizzleDeallocIfNeeded(Class classToSwizzle) {
@synchronized (swizzledClasses()) {
NSString *className = NSStringFromClass(classToSwizzle);
if ([swizzledClasses() containsObject:className]) return;
SEL deallocSelector = sel_registerName("dealloc");
__block void (*originalDealloc)(__unsafe_unretained id, SEL) = NULL;
id newDealloc = ^(__unsafe_unretained id self) {
RACCompoundDisposable *compoundDisposable = objc_getAssociatedObject(self, RACObjectCompoundDisposable);
[compoundDisposable dispose];
if (originalDealloc == NULL) {
struct objc_super superInfo = {
.receiver = self,
.super_class = class_getSuperclass(classToSwizzle)
};
void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper;
msgSend(&superInfo, deallocSelector);
} else {
originalDealloc(self, deallocSelector);
}
};
IMP newDeallocIMP = imp_implementationWithBlock(newDealloc);
if (!class_addMethod(classToSwizzle, deallocSelector, newDeallocIMP, "v@:")) {
Method deallocMethod = class_getInstanceMethod(classToSwizzle, deallocSelector);
originalDealloc = (__typeof__(originalDealloc))method_getImplementation(deallocMethod);
originalDealloc = (__typeof__(originalDealloc))method_setImplementation(deallocMethod, newDeallocIMP);
}
[swizzledClasses() addObject:className];
}
}
原理是利用 OC 语言的运行时特性,为指定的类定义 dealloc 方法。当然,若 dealloc 方法已经实现了,则要将原方法保存替换,而后执行。
- swizzledClasses() 返回一个集合,如果该集合已经包含了指定的 classToSwizzle 类,那么说明 dealloc 方法已经经过相应的调整,直接返回。
- 接着定义一个 originalDealloc 变量用来保存可能存在的 dealloc 实现方法。
- 定义一个 newDealloc 代码块,即是后面要设置的 dealloc 实现方法,该方法会先调用 compoundDisposable 的 dispose 方法,实际会传递一个结束信号量。然后,如果存在原 dealloc 方法,就执行,否则执行父类的 dealloc 方法。
- 使用
imp_implementationWithBlock
函数和参数 newDealloc 代码块创建一个实现方法的变量 newDeallocIMP 。 - 使用
class_addMethod
函数为指定的 classToSwizzle 类添加 dealloc 实现方法,如果添加失败,那么说明已经存在该实现方法,则先对其进行保存,而后用method_setImplementation
函数重写设置。
另外,也可以将相关的操作放在
rac_deallocDisposable
属性中。
RACDescription
在 RACDescription 分类中,主要是声明了一个 RACDescription 函数,该函数会根据参数的类型来返回相应的描述。
NSString *RACDescription(id object) {
if (getenv("RAC_DEBUG_SIGNAL_NAMES") != NULL) {
if ([object respondsToSelector:@selector(rac_description)]) {
return [object rac_description];
} else {
return [[NSString alloc] initWithFormat:@"<%@: %p>", [object class], object];
}
} else {
return @"(description skipped)";
}
}
不过,该方法只适用于调试模式。
RACKVOWrapper
RACKVOWrapper 中提供了一个方法,该方法对 KVO 模式进行了封装。并且,当监听的属性声明的是 weak 特征时,同样也会调用 block 代码块。并且,监听者和被监听者任意一个被销毁时,监听也会自动移除,而不用手动移除监听。
- (RACDisposable *)rac_observeKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
observer:(__weak NSObject *)weakObserver
block:(void (^)(id, NSDictionary *, BOOL, BOOL))block
{
//block 和 keyPath 不能为空
NSCParameterAssert(block != nil);
NSCParameterAssert(keyPath.rac_keyPathComponents.count > 0);
keyPath = [keyPath copy];
NSObject *strongObserver = weakObserver;
NSArray *keyPathComponents = keyPath.rac_keyPathComponents;
//keyPath 是否只包含一个部分
BOOL keyPathHasOneComponent = (keyPathComponents.count == 1);
//keyPath 的第一个部分
NSString *keyPathHead = keyPathComponents[0];
//keyPath 的剩余部分
NSString *keyPathTail = keyPath.rac_keyPathByDeletingFirstKeyPathComponent;
//清理对象
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
//第一个路径值的清理对象
RACSerialDisposable *firstComponentSerialDisposable = [RACSerialDisposable serialDisposableWithDisposable:[RACCompoundDisposable compoundDisposable]];
//获取清理对象
RACCompoundDisposable * (^firstComponentDisposable)(void) = ^{
return (RACCompoundDisposable *)firstComponentSerialDisposable.disposable;
};
[disposable addDisposable:firstComponentSerialDisposable];
//是否因该添加销毁监听,默认不添加
BOOL shouldAddDeallocObserver = NO;
//获取当前类的指定属性 property
objc_property_t property = class_getProperty(object_getClass(self), keyPathHead.UTF8String);
if (property != NULL) {
//获取属性 property 的特性
rac_propertyAttributes *attributes = rac_copyPropertyAttributes(property);
if (attributes != NULL) {
@onExit {
free(attributes);
};
BOOL isObject = attributes->objectClass != nil || strstr(attributes->type, @encode(id)) == attributes->type;
BOOL isProtocol = attributes->objectClass == NSClassFromString(@"Protocol");
BOOL isBlock = strcmp(attributes->type, @encode(void(^)())) == 0;
BOOL isWeak = attributes->weak;
//属性是类,并且为弱引用,不是代码块,不是协议,才可以添加销毁监听
shouldAddDeallocObserver = isObject && isWeak && !isBlock && !isProtocol;
}
}
//为指定值添加销毁监听的代码块
void (^addDeallocObserverToPropertyValue)(NSObject *) = ^(NSObject *value) {
//先进行判断,是否可以添加销毁监听
if (!shouldAddDeallocObserver) return;
//如果监听者是监听路径上的一个值,那么直接返回,因为监听者本身被销毁并不在监听范围内
if (value == weakObserver) return;
NSDictionary *change = @{
NSKeyValueChangeKindKey: @(NSKeyValueChangeSetting),
NSKeyValueChangeNewKey: NSNull.null,
};
//获取指定值的清理对象
RACCompoundDisposable *valueDisposable = value.rac_deallocDisposable;
//将 block 回调封装为一个清理任务
RACDisposable *deallocDisposable = [RACDisposable disposableWithBlock:^{
block(nil, change, YES, keyPathHasOneComponent);
}];
//将清理任务添加到指定值的清理对象中
[valueDisposable addDisposable:deallocDisposable];
//向第一个路径值的清理对象中添加一个任务,该任务是移除 deallocDisposable 清理任务
[firstComponentDisposable() addDisposable:[RACDisposable disposableWithBlock:^{
[valueDisposable removeDisposable:deallocDisposable];
}]];
};
//为第一个路径值后面的路径添加 block 回调任务
void (^addObserverToValue)(NSObject *) = ^(NSObject *value) {
RACDisposable *observerDisposable = [value rac_observeKeyPath:keyPathTail
options:(options & ~NSKeyValueObservingOptionInitial)
observer:weakObserver
block:block];
[firstComponentDisposable() addDisposable:observerDisposable];
};
//监听第一个路径值的变化,但是不包括值的初始化情况
NSKeyValueObservingOptions trampolineOptions = (options | NSKeyValueObservingOptionPrior) & ~NSKeyValueObservingOptionInitial;
RACKVOTrampoline *trampoline = [[RACKVOTrampoline alloc] initWithTarget:self
observer:strongObserver
keyPath:keyPathHead
options:trampolineOptions
block:^(id trampolineTarget, id trampolineObserver, NSDictionary *change) {
//当接收到前一个通知时,清理第一个路径值的相关任务
if ([change[NSKeyValueChangeNotificationIsPriorKey] boolValue]) {
[firstComponentDisposable() dispose];
//如果设置的监听有 NSKeyValueObservingOptionPrior ,则执行 block
if ((options & NSKeyValueObservingOptionPrior) != 0) {
block([trampolineTarget valueForKeyPath:keyPath], change, NO, keyPathHasOneComponent);
}
return;
}
//获取变化的值
NSObject *value = [trampolineTarget valueForKey:keyPathHead];
//如果值变为 nil ,那么直接调用 block 即可
if (value == nil) {
block(nil, change, NO, keyPathHasOneComponent);
return;
}
//将原任务清理,添加新的任务
RACDisposable *oldFirstComponentDisposable = [firstComponentSerialDisposable swapInDisposable:[RACCompoundDisposable compoundDisposable]];
[oldFirstComponentDisposable dispose];
//为指定值添加销毁监听
addDeallocObserverToPropertyValue(value);
//如果没有其他路径值,直接调用 block 代码
if (keyPathHasOneComponent) {
block(value, change, NO, keyPathHasOneComponent);
return;
}
//为剩余的路径值添加监听
addObserverToValue(value);
//调用 block 代码
block([value valueForKeyPath:keyPathTail], change, NO, keyPathHasOneComponent);
}];
//添加停止监听的清理任务
[disposable addDisposable:trampoline];
//获取路径的第一个值
NSObject *value = [self valueForKey:keyPathHead];
if (value != nil) {
//添加销毁监听
addDeallocObserverToPropertyValue(value);
if (!keyPathHasOneComponent) {
//添加剩余路径的监听
addObserverToValue(value);
}
}
//监听选项包含初始化选项时,调用 block 代码并传递初始化值
if ((options & NSKeyValueObservingOptionInitial) != 0) {
id initialValue = [self valueForKeyPath:keyPath];
NSDictionary *initialChange = @{
NSKeyValueChangeKindKey: @(NSKeyValueChangeSetting),
NSKeyValueChangeNewKey: initialValue ?: NSNull.null,
};
block(initialValue, initialChange, NO, keyPathHasOneComponent);
}
//将监听清理任务加入监听者和当前对象的销毁清理任务中
RACCompoundDisposable *observerDisposable = strongObserver.rac_deallocDisposable;
RACCompoundDisposable *selfDisposable = self.rac_deallocDisposable;
[observerDisposable addDisposable:disposable];
[selfDisposable addDisposable:disposable];
//返回一个清理对象,其包含停止监听的任务
return [RACDisposable disposableWithBlock:^{
[disposable dispose];
[observerDisposable removeDisposable:disposable];
[selfDisposable removeDisposable:disposable];
}];
}
从上面的代码分析,可以看出,对于传递的 keyPath 中的每个路径值,都会进行监听(监听者 observer 除外),不过其向 block 中传递的参数都是全路径所对应的值。
RACLifting
RACLifting 分类中定义了三个方法用来实现当前类中方法的调用,只是方法的参数是通过信号流传递的。
- (RACSignal *)rac_liftSelector:(SEL)selector withSignals:(RACSignal *)firstSignal, ... NS_REQUIRES_NIL_TERMINATION;
- (RACSignal *)rac_liftSelector:(SEL)selector withSignalsFromArray:(NSArray<RACSignal *> *)signals;
这两个方法调用当前类的 selector 方法,而参数同 signal 相对应。当每个信号流都传递了信号量时,方法会被调用,而之后,每当任意一个信号流传递了信号量后,都会去执行一次 selecor 方法。只是,这两个方法,最终都是调用了下面的这个方法。
- (RACSignal *)rac_liftSelector:(SEL)selector withSignalOfArguments:(RACSignal *)arguments {
NSCParameterAssert(selector != NULL);
NSCParameterAssert(arguments != nil);
@unsafeify(self);
NSMethodSignature *methodSignature = [self methodSignatureForSelector:selector];
NSCAssert(methodSignature != nil, @"%@ does not respond to %@", self, NSStringFromSelector(selector));
return [[[[arguments
takeUntil:self.rac_willDeallocSignal]
map:^(RACTuple *arguments) {
@strongify(self);
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
invocation.selector = selector;
invocation.rac_argumentsTuple = arguments;
[invocation invokeWithTarget:self];
return invocation.rac_returnValue;
}]
replayLast]
setNameWithFormat:@"%@ -rac_liftSelector: %s withSignalsOfArguments: %@", RACDescription(self), sel_getName(selector), arguments];
}
这三个方法的返回值是一个信号流,该信号流传递的是方法被调用的返回值,如果没有返回值,那么传递 RACUnit.defaultUnit
信号量。
RACPropertySubscribing
RACPropertySubscribing 分类实际是对 RACKVOWrapper 分类的进一步封装,其中的方法,实际都是调用了 RACKVOWrapper 中的方法。
- (RACSignal *)rac_valuesForKeyPath:(NSString *)keyPath observer:(__weak NSObject *)observer;
- (RACSignal<RACTwoTuple<id, NSDictionary *> *> *)rac_valuesAndChangesForKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options observer:(__weak NSObject *)observer;
上面两个方法都是对当前对象指定路径下属性的变化监听,返回的信号流传递了变化的值或变化的信息。
RACSelectorSignal
RACSelectorSignal 分类提供了两个方法,用来监听指定方法的调用,返回的信号流传递的信号量是调用方法时使用的参数。
不同于 RACLifting 分类,其是参数传递引发方法的调用,而 RACSelectorSignal 分类的方法是方法的调用,导致参数在信号流中的传递。
- (RACSignal<RACTuple *> *)rac_signalForSelector:(SEL)selector;
- (RACSignal<RACTuple *> *)rac_signalForSelector:(SEL)selector fromProtocol:(Protocol *)protocol;
内部方法
上面两个方法都调用了内部创建的函数:
static RACSignal *NSObjectRACSignalForSelector(NSObject *self, SEL selector, Protocol *protocol)
在理解这个函数之前,先说明其它相关的函数。
static SEL RACAliasForSelector(SEL originalSelector);
RACAliasForSelector 函数为指定的选择器添加一个前缀 rac_alias_
后返回这个新的选择器。
static NSMutableSet *swizzledClasses();
swizzledClasses 函数返回一个集合,该集合保存所有已经进行交换了的类。
static BOOL RACForwardInvocation(id self, NSInvocation *invocation) {
SEL aliasSelector = RACAliasForSelector(invocation.selector);
RACSubject *subject = objc_getAssociatedObject(self, aliasSelector);
Class class = object_getClass(invocation.target);
BOOL respondsToAlias = [class instancesRespondToSelector:aliasSelector];
if (respondsToAlias) {
invocation.selector = aliasSelector;
[invocation invoke];
}
if (subject == nil) return respondsToAlias;
[subject sendNext:invocation.rac_argumentsTuple];
return YES;
}
- 根据参数 invocation 获取别名选择器 aliasSelector
- 根据选择器 aliasSelector 获取信号流 subject
- 根据参数 invocation 获取消息接收类 class
- 确认 class 类中是否能够处理 aliasSelector 消息,如果能,就处理。
- 如果 subject 为空,直接返回 respondsToAlias
- 如果 subject 存在,则发送相关参数,最后返回 YES
static void RACSwizzleForwardInvocation(Class class) {
SEL forwardInvocationSEL = @selector(forwardInvocation:);
Method forwardInvocationMethod = class_getInstanceMethod(class, forwardInvocationSEL);
void (*originalForwardInvocation)(id, SEL, NSInvocation *) = NULL;
if (forwardInvocationMethod != NULL) {
originalForwardInvocation = (__typeof__(originalForwardInvocation))method_getImplementation(forwardInvocationMethod);
}
id newForwardInvocation = ^(id self, NSInvocation *invocation) {
BOOL matched = RACForwardInvocation(self, invocation);
if (matched) return;
if (originalForwardInvocation == NULL) {
[self doesNotRecognizeSelector:invocation.selector];
} else {
originalForwardInvocation(self, forwardInvocationSEL, invocation);
}
};
class_replaceMethod(class, forwardInvocationSEL, imp_implementationWithBlock(newForwardInvocation), "v@:@");
}
为指定的 class 类创建新的 forwardInvocation: 实现方法,该方法当实际要调用的方法不存在时调用。
- 如果当前类已经实现了 forwardInvocation: 方法,那么就将该实现保存下来。
- 在新的实现方法中,首先会去尝试调用原来要调用的方法的别名方法,如果成功,则在信号流中传递方法中使用的参数,否则,就调用原 forwardInvocation: 实现方法,如果没实现,就发送一个未识别方法错误。
static void RACSwizzleRespondsToSelector(Class class) {
SEL respondsToSelectorSEL = @selector(respondsToSelector:);
Method respondsToSelectorMethod = class_getInstanceMethod(class, respondsToSelectorSEL);
BOOL (*originalRespondsToSelector)(id, SEL, SEL) = (__typeof__(originalRespondsToSelector))method_getImplementation(respondsToSelectorMethod);
id newRespondsToSelector = ^ BOOL (id self, SEL selector) {
Method method = rac_getImmediateInstanceMethod(class, selector);
if (method != NULL && method_getImplementation(method) == _objc_msgForward) {
SEL aliasSelector = RACAliasForSelector(selector);
if (objc_getAssociatedObject(self, aliasSelector) != nil) return YES;
}
return originalRespondsToSelector(self, respondsToSelectorSEL, selector);
};
class_replaceMethod(class, respondsToSelectorSEL, imp_implementationWithBlock(newRespondsToSelector), method_getTypeEncoding(respondsToSelectorMethod));
}
该方法会为指定的 class 类重新指定 respondsToSelector: 的实现方法,用来确定该类是否能够响应指定的消息。
- 先将原 respondsToSelector: 的实现方法保存下来
- 定义新的实现方法,先遍历指定类的所有方法查找指定的方法,如果能找到该方法,并且其实现方法为
_objc_msgForward
,那么则判断与指定方法相关的别名方法是否关联了信号流,如果有,就返回 YES 。而遍历结束后,没有找到指定的方法,就执行原 respondsToSelector: 的实现方法。
static void RACSwizzleGetClass(Class class, Class statedClass) {
SEL selector = @selector(class);
Method method = class_getInstanceMethod(class, selector);
IMP newIMP = imp_implementationWithBlock(^(id self) {
return statedClass;
});
class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(method));
}
该方法将修改指定的 class 类的 class 方法,使其返回 statedClass 类的类型。
static void RACSwizzleMethodSignatureForSelector(Class class) {
IMP newIMP = imp_implementationWithBlock(^(id self, SEL selector) {
Class actualClass = object_getClass(self);
Method method = class_getInstanceMethod(actualClass, selector);
if (method == NULL) {
struct objc_super target = {
.super_class = class_getSuperclass(class),
.receiver = self,
};
NSMethodSignature * (*messageSend)(struct objc_super *, SEL, SEL) = (__typeof__(messageSend))objc_msgSendSuper;
return messageSend(&target, @selector(methodSignatureForSelector:), selector);
}
char const *encoding = method_getTypeEncoding(method);
return [NSMethodSignature signatureWithObjCTypes:encoding];
});
SEL selector = @selector(methodSignatureForSelector:);
Method methodSignatureForSelectorMethod = class_getInstanceMethod(class, selector);
class_replaceMethod(class, selector, newIMP, method_getTypeEncoding(methodSignatureForSelectorMethod));
}
该方法为指定的 class 类重新定义 methodSignatureForSelector: 的实现方法,在新的实现方法中,会先查找指定类的指定方法,如果方法存在,那么就根据该方法的类型编码创建一个方法签名并返回,如果不存在,则调用父类的 methodSignatureForSelector: 方法。
static Class RACSwizzleClass(NSObject *self) {
Class statedClass = self.class;
Class baseClass = object_getClass(self);
Class knownDynamicSubclass = objc_getAssociatedObject(self, RACSubclassAssociationKey);
if (knownDynamicSubclass != Nil) return knownDynamicSubclass;
NSString *className = NSStringFromClass(baseClass);
if (statedClass != baseClass) {
@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;
}
const char *subclassName = [className stringByAppendingString:RACSubclassSuffix].UTF8String;
Class subclass = objc_getClass(subclassName);
if (subclass == nil) {
subclass = objc_allocateClassPair(baseClass, subclassName, 0);
if (subclass == nil) return nil;
RACSwizzleForwardInvocation(subclass);
RACSwizzleRespondsToSelector(subclass);
RACSwizzleGetClass(subclass, statedClass);
RACSwizzleGetClass(object_getClass(subclass), statedClass);
RACSwizzleMethodSignatureForSelector(subclass);
objc_registerClassPair(subclass);
}
object_setClass(self, subclass);
objc_setAssociatedObject(self, RACSubclassAssociationKey, subclass, OBJC_ASSOCIATION_ASSIGN);
return subclass;
}
RACSwizzleClass 函数会为指定的实例对象绑定一个动态类,这个类一般是由框架创建的子类。这个绑定的类的相关消息转发方法都是被处理的过的,参见上面的方法说明以及消息转发机制。
static void RACCheckTypeEncoding(const char *typeEncoding);
该函数可以用来检查方法类型编码是否合法。
static const char *RACSignatureForUndefinedSelector(SEL selector) {
const char *name = sel_getName(selector);
NSMutableString *signature = [NSMutableString stringWithString:@"v@:"];
while ((name = strchr(name, ':')) != NULL) {
[signature appendString:@"@"];
name++;
}
return signature.UTF8String;
}
该方法返回指定选择器的方法类型编码,并且默认参数都是 OC 对象。
RACSelectorSignal 分类中的两个方法实际都是调用的 NSObjectRACSignalForSelector 函数,其提供了三个参数:self,selector,protocol。
-
获取选择器 selector 的别名选择器。
-
如果已经绑定了相应的信号流,那么就直接返回该信号流。
-
为当前的实例对象 self 绑定一个类 class ,这个类的相关方法都经过了处理。
-
创建一个信号流绑定到当前的对象,如果该对象销毁,那么信号流结束传递信号量。
-
从绑定的 class 类中查找指定的 selector 选择器方法,如果没有找到相应的方法:
1)如果参数 protocol 为空,那么便从 selector 中获取方法类型编码,否则就从协议中查找到指定方法并获取他的类型编码。
2)检查编码是否合法。
3)指定接收到 selector 消息时,进行转发。如果指定失败,就直接返回一个错误信号流。
-
如果指定的 selector 方法查询到了,并且不是转发给其他的方法
_objc_msgForward
,那么:1)获取方法的编码并检验其合法性。
2)向 class 类中添加别名方法,并且其实现方法指向查询到的方法实现。
3)指定 class 接收到 selector 消息后,便进行转发。
-
最后返回创建的 subject 信号流。
参考下面的源码:
static RACSignal *NSObjectRACSignalForSelector(NSObject *self, SEL selector, Protocol *protocol) {
SEL aliasSelector = RACAliasForSelector(selector);
@synchronized (self) {
RACSubject *subject = objc_getAssociatedObject(self, aliasSelector);
if (subject != nil) return subject;
Class class = RACSwizzleClass(self);
NSCAssert(class != nil, @"Could not swizzle class of %@", self);
subject = [[RACSubject subject] setNameWithFormat:@"%@ -rac_signalForSelector: %s", RACDescription(self), sel_getName(selector)];
objc_setAssociatedObject(self, aliasSelector, subject, OBJC_ASSOCIATION_RETAIN);
[self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{
[subject sendCompleted];
}]];
Method targetMethod = class_getInstanceMethod(class, selector);
if (targetMethod == NULL) {
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]];
}
} else if (method_getImplementation(targetMethod) != _objc_msgForward) {
// Make a method alias for the existing method implementation.
const char *typeEncoding = method_getTypeEncoding(targetMethod);
RACCheckTypeEncoding(typeEncoding);
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:.
class_replaceMethod(class, selector, _objc_msgForward, method_getTypeEncoding(targetMethod));
}
return subject;
}
}