OC 是一門動(dòng)態(tài)的語(yǔ)言, runtime 的機(jī)制給開(kāi)發(fā)者提供了許多新的可能, 在運(yùn)行時(shí), 可以動(dòng)態(tài)為一個(gè)類添加方法和屬性.
首先說(shuō)一下 objc_msgSend 執(zhí)行方法
現(xiàn)在我們有一個(gè) Doctor 類:
// Doctor.m
@implementation Doctor
- (void)sayhello {
NSLog(@"hello");
}
@end
我們都知道不在.h 里面申明, .只在 .m 中實(shí)現(xiàn)的方法, 就相當(dāng)于私有方法. 但并非只可以在.m 中調(diào)用.
Doctor *doctor = [[Doctor alloc] init];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[doctor performSelector:NSSelectorFromString(@"sayhello") withObject:nil];
#pragma clang diagnostic pop
使用 perfromSelector 就可以直接掉用知道名字的方法.
perfromSelector相當(dāng)于:
Doctor *doctor = [[Doctor alloc] init];
SEL sel = NSSelectorFromString(@"sayhello");
void (*sendMsg)(id, SEL) = (void (*)(id, SEL))objc_msgSend;
sendMsg(doctor, sel);
runtime 中使用 objc_msgSend 來(lái)執(zhí)行所有的方法.
class_addMethod 動(dòng)態(tài)添加方法
SEL sel = NSSelectorFromString(@"sayhello");
// 獲取當(dāng)前類 method
Method method = class_getInstanceMethod([self class], NSSelectorFromString(@"sayhello"));
// 讓 Doctor 指向當(dāng)前類的 method
class_addMethod([doctor class], sel, method_getImplementation(method), method_getTypeEncoding(method));
//當(dāng)前類聲明一個(gè)方法
- (void)sayhello {
NSLog(@"hello");
}
Method Swizzling
@implementation UIViewController (Tracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// 將 originalMethod 指向 xxx_viewWillAppear: 的實(shí)現(xiàn).
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
// 將 swizzledMethod 指向 viewWillAppear: 的實(shí)現(xiàn).
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
#pragma mark - Method Swizzling
- (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated]; // 這里實(shí)際執(zhí)行的是 viewWillAppear:
}
通過(guò)調(diào)換兩個(gè)方法的實(shí)現(xiàn), 來(lái)實(shí)現(xiàn), 捕捉原始方法的執(zhí)行. 當(dāng)需要捕捉某個(gè)方法執(zhí)行, 一種方法是通過(guò)繼承來(lái)實(shí)現(xiàn), 另一種方法通過(guò) Method Swizzling, 當(dāng)有很多不同形態(tài)的子類存在的時(shí)候, 繼承還是需要寫很多重復(fù)代碼.
首先說(shuō)一下, 當(dāng)通過(guò) Method Swizzling 交換方法之后, 所有這個(gè)類的子類走這個(gè)交換過(guò)的方法都會(huì), 在當(dāng)前的實(shí)現(xiàn)中捕捉到, 所以, 在這個(gè)方法中的 self 是會(huì)發(fā)生變化的, 可能是任意一個(gè) UIViewController 的子類.
即:
// 所有 UIViewController 的子類和本身, 在走 viewWillAppear 之前都會(huì)走下面的方法
- (void)xxx_viewWillAppear:(BOOL)animated {
[self xxx_viewWillAppear:animated]; // 這里實(shí)際執(zhí)行的是 viewWillAppear:
}
在 @sunny
的 xib的動(dòng)態(tài)橋接
中, 他捕捉了所有的 UIView 子類的 awakeAfterUsingCoder
方法, 并通過(guò)判斷是否遵守協(xié)議來(lái)判斷是否要進(jìn)行處理, 所以只有在 UIView 子類種, 遵守 XXNibBridge
協(xié)議的子類才會(huì)被動(dòng)態(tài)加載, 這是 Method Swizzling 一個(gè)很好的案例.
// 交換后的方法
- (id)hackedAwakeAfterUsingCoder:(NSCoder *)decoder {
if ([self.class conformsToProtocol:@protocol(XXNibBridge)] && ((UIView *)self).subviews.count == 0) {
// "self" is placeholder view for this moment, replace it.
return [XXNibBridgeImplementation instantiateRealViewFromPlaceholder:(UIView *)self];
}
return self;
}