OC Runtime实践

导入开启外挂功能
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";
}

  1. 替换原生的方法实现(以防止数组指针越界为例)
     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];
}

你可能感兴趣的:(OC Runtime实践)