導(dǎo)入<objc/runtime.h>開啟外掛功能
1.獲取屬性和方法列表
#define CLASS_NAME NSStringFromClass([self class])
@implementation NSObject (NSObject_extention)
//判斷有沒有屬性
- (BOOL)hasProperty:(NSString *)str
{
BOOL flag = NO;
u_int count = 0;
//class_copyIvarList出來(lái)的都是帶下劃線的娄周, class_copyPropertyList是不帶下劃線的(只能得到@property聲明的)
Ivar *ivars = class_copyIvarList(objc_getClass([CLASS_NAME UTF8String]), &count);
for (int i = 0; i < count; i++) {
NSString *name = [NSString stringWithUTF8String:ivar_getName(ivars[i])];
if ([str isEqualToString:name]) {
flag = YES;
break;
}
}
return flag;
}
//判斷有沒有方法
- (BOOL)hasMethod:(NSString *)str
{
BOOL flag = NO;
u_int count = 0;
Method *method = class_copyMethodList(objc_getClass([CLASS_NAME UTF8String]), &count);
for (int i = 0; i < count; i++) {
SEL sel = method_getName(method[i]);
NSString *name = [NSString stringWithUTF8String:sel_getName(sel)];
if ([name isEqualToString:str]) {
flag = YES;
break;
}
}
return flag;
}
@end
2.消息轉(zhuǎn)發(fā)處理
Person *p = [Person new];
id obj = [p performSelector:@selector(drive) withObject:@"fuck" withObject:@"fuck"];
//在Person類或者類目中,用resolveInstanceMethod處理未實(shí)現(xiàn)的消息
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
class_addMethod([self class], sel, class_getMethodImplementation(self, @selector(eat:param:)), "v@:@"); //最后一個(gè)參數(shù)的含義 v:void @:對(duì)象->self :表示SEL->_cmd
return [super resolveInstanceMethod:sel];
}
- (id)eat:(NSString *)str param:(NSString *)str2{
NSLog(@"%@-%@", str, str2);
return @"123";
}
- 替換原生的方法實(shí)現(xiàn)(以防止數(shù)組指針越界為例)
NSArray *arr = [NSArray arrayWithObjects:@"123", @"456", nil];
id obj = [arr objectAtIndex:3];
//在NSArray的類目中實(shí)現(xiàn)
+ (void)load { //load方法必執(zhí)行
/*
由于 NSArray是一個(gè)類簇,需要把所有的入口都封住才算完美 . 方法還是很常見的,用了runtime的方法替換 . 然后 給NSArray加類別
__NSSingleObjectArray __NSArray0 __NSArrayI __NSArrayM
*/
Method oldM = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
Method newM = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(safe_objectAtIndex:));
method_exchangeImplementations(oldM, newM);
static dispatch_once_t onceToken; //將原生函數(shù)與自定義函數(shù)實(shí)現(xiàn)對(duì)換
}
- (instancetype)safe_objectAtIndex:(NSUInteger)index
{
if (self.count < (index + 1)) {
NSLog(@"指針越界,下標(biāo):%ld",index);
return nil;
}
return [self safe_objectAtIndex:index];
}