导入<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出来的都是带下划线的, 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.消息转发处理
Person *p = [Person new];
id obj = [p performSelector:@selector(drive) withObject:@"fuck" withObject:@"fuck"];
//在Person类或者类目中,用resolveInstanceMethod处理未实现的消息
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
class_addMethod([self class], sel, class_getMethodImplementation(self, @selector(eat:param:)), "v@:@"); //最后一个参数的含义 v:void @:对象->self :表示SEL->_cmd
return [super resolveInstanceMethod:sel];
}
- (id)eat:(NSString *)str param:(NSString *)str2{
NSLog(@"%@-%@", str, str2);
return @"123";
}
- 替换原生的方法实现(以防止数组指针越界为例)
NSArray *arr = [NSArray arrayWithObjects:@"123", @"456", nil];
id obj = [arr objectAtIndex:3];
//在NSArray的类目中实现
+ (void)load { //load方法必执行
/*
由于 NSArray是一个类簇,需要把所有的入口都封住才算完美 . 方法还是很常见的,用了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; //将原生函数与自定义函数实现对换
}
- (instancetype)safe_objectAtIndex:(NSUInteger)index
{
if (self.count < (index + 1)) {
NSLog(@"指针越界,下标:%ld",index);
return nil;
}
return [self safe_objectAtIndex:index];
}
网友评论