zoukankan      html  css  js  c++  java
  • runtime使用总结

    runtime这个东西,项目是很少用到的,但面试又避不可少,了解其内部的机制对底层的理解还是很有必要的。

    1.动态添加属性

    拓展类别属性的简单实现

    a.定义字面量指针  static char dynamicAttributes;

    b.设置属性  objc_setAssociatedObject(self,&dynamicAttributes,(id)value,OBJC_ASSOCIATION_COPY_NONATOMIC|OBJC_ASSCOCIATION_RETAIN)

    c.获取属性值  objc_getAsscociatedObject(self,&dynamicAttributes)

    2.model转字典

    每个类中都有一个属性列表,我们要做的就是遍历该列表,利用KVC取值,装入字典

    unsigned int count = 0;
    objc_property_t *ptyList = class_copyPropertyList(self.class,&count);
    NSMutableDictonary *dic = [NSMutableDictionary dictionary];
    for (int i=0;i<count;i++) {
    	objc_property pty = ptyList[i];
    	const char *cname = property_getName(pty);
    	NSString *name = [NSString stringWithUTF8String:cname];
    	if (name.length > 0) {
    		id value = [self valueForKey:name];
    		[dic setObject:value forKey:name];
    	}
    }

    3.方法交换

    C中方法是编译时就得实现。OC中方法是编译时无需实现,可在运行是动态插入方法及实现。利用这一点,可以轻松实现运行时方法的动态交换。

    因为load方法早于main方法,并且不会覆盖父类实现,为了提高代码的可读性,一半是在分类中实现相关方法的交换。

    + load {
    	SEL original = @selector(willMoveToSuperView:);
    	SEL exchange = @selector(gl_willMoveToSuperView:);
    	Method originalMethod = class_getInstanceMethod(self.class,original);
    	Method exchangeMethod = class_getInstanceMethod(self.class,exchange);
    		// 动态添加方法,若选择器存在方法实现,会失败
    	BOOL isAdd = class_addMethod(self,original,method_getImplementation(exchangeMethod),method_getTypeEncoding(exchangeMethod));
    	if (isAdd) {
    		// 添加成功,将新的选择器替换为旧实现
    		class_replaceMethod(self,exchange,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));
    	}else {
    		// 直接交换两方法实现
    		method_changeImplementations(originalMethod,exchangeMethod);
    	}
    }
    
    - (void)gl_villMoveToSuperView:(UIView *)superView {
    	// do something ...
    }
    

    isa指针

      是一个指向所属类的指针。OC中消息机制是依靠objc_msgSend(receiver,selector)这个函数发送消息的。

      objc_msgSend会根据实例对象的isa指针查找对象的类,然后查找该类的dispatch_table中的selector,找不到就依次查找父类,直到NSObject类。实在找不到方法就抛出异常。找到selector后,会根据dispatch_table中的内存地址该selector。为了提高转发效率,系统会将所有的selector地址和已使用的selector地址缓存起来,通过类的形式划分不同的缓冲区域。obj_msgSend去查找dispatch_table前,会先检查该类的缓存,如果缓存命中,就直接调用selector。

    消息转发机制(前提是dispatch_table找不到对应的selector)

      1.对象收到无法解读的消息后,首先会调用resolveInstanceMethod:询问是否有动态添加方法来处理

    void speak(id self, SEL _cmd){
        NSLog(@"Now I can speak.");
    }
    + (BOOL)resolveInstanceMethod:(SEL)sel {
        
        NSLog(@"resolveInstanceMethod:  %@", NSStringFromSelector(sel));
        if (sel == @selector(speak)) {
            class_addMethod([self class], sel, (IMP)speak, "V@:");
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    

       2.第一步若没有添加新方法,那就询问有没别人帮忙处理,调用的是forwardingTargetForSelector:

    - (id)forwardingTargetForSelector:(SEL)aSelector {
        NSLog(@"forwardingTargetForSelector:  %@", NSStringFromSelector(aSelector));
        Bird *bird = [[Bird alloc] init];
        if ([bird respondsToSelector: aSelector]) {
            return bird;
        }
        return [super forwardingTargetForSelector: aSelector];
    }
    // Bird.m
    - (void)fly {
        NSLog(@"I am a bird, I can fly.");
    }
    

       3.当前两步均没有响应时,走到第三部,调用forwardInvocation:,该方法会首先调用methodSignatureForSelector:方法获取选择子的方法签名

    - (void)forwardInvocation:(NSInvocation *)anInvocation {
        NSLog(@"forwardInvocation: %@", NSStringFromSelector([anInvocation selector]));
        if ([anInvocation selector] == @selector(speak)) {
            Monkey *monkey = [[Monkey alloc] init];
            [anInvocation invokeWithTarget:monkey];
        }   
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
        NSLog(@"method signature for selector: %@", NSStringFromSelector(aSelector));
        if (aSelector == @selector(code)) {
            return [NSMethodSignature signatureWithObjCTypes:"V@:@"];
        }
        return [super methodSignatureForSelector:aSelector];
    }
    

       4.若前三步均没有处理,则抛出异常doesNotRecognizeSelector:

    NSInvocation

      对方法的另一种封装。相对于performSelector:withObject:只能传2参数的弊端,invocation类可以设置封装多参数

      a.对选择子签名  

        NSMethodSignature *instanceSignature = [self instanceMethodSignatureWithSelector:@selector(run:)];//实例方法签名

        NSMethodSignature *classSignature = [self methodSignatureWithSelector:@selector(run:)];//类方法签名

      b.实例化invocation,并设置参数

    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    //设置方法调用者
    invocation.target = self;
    //注意:这里的方法名一定要与方法签名类中的方法一致
    invocation.selector = @selector(run:);
    NSString *way = @"byCar";
    //这里的Index要从2开始,以为0跟1已经被占据了,分别是self(target),selector(_cmd)
    [invocation setArgument:&way atIndex:2];
    //3、调用invoke方法
    [invocation invoke];
    

       c.获取invocation返回值

    id res = nil;
    if (signature.methodReturnLength != 0) {//有返回值
        //将返回值赋值给res
        [invocation getReturnValue:&res];
    }
    return res;

    IMP指针

      指向选择子方法实现的指针,直接调用它相对于调用方法,会提高程序运行效率。

      相对于Method的方法交换,method_exchangeImplementations(method1,method2),使用IMP指针可以简化其对方法的额外实现,显得更加优雅。

      如果想IMP指针带参数或者返回值,需要将proprecessing:enable strict checking of objc_msgSend calls 配置为NO,默认是YES,表示IMP无参无返回值。

    typedef id (*_IMP)(id,SEL,...);
    typedef void (*_VIMP)(id,SEL,...);
    
    + (void)load {
    	static dispatch_once onceToken;
    	dispatch_once(&onceToken,^{
    		Method viewDidLoad = class_getInstanceMethod(self,@selector(viewDidLoad));
    		_IMP viewDidLoad_IMP = (_IMP)method_getImplementation(viewDidLoad);
    		method_setImplementation(viewDidLoad,imp_implementationWithBlock(^(id target,SEL action){
    			viewDidLoad_IMP(target,@selector(viewDidLoad));
    			// 新增代码 do extra things
    		}));
    	});
    }
    

    最后贴一张class的内部结构图,容以后细细研究

  • 相关阅读:
    【BZOJ5306】染色(HAOI2018)-容斥原理+NTT
    【BZOJ3129】方程(SDOI2013)-容斥原理+扩展Lucas定理
    【BZOJ3876】支线剧情(AHOI&JSOI2014)-有上下界费用流
    【POJ1149】PIGS-最大流+优化建模
    【BZOJ1941】Hide and Seek(SDOI2010)-KD树
    【BZOJ1834】网络扩容(ZJOI2010)-最大流+费用流+拆边
    【BZOJ1927】星际竞速(SCOI2010)-费用流+拆点
    【BZOJ4872】分手是祝愿(六省联考2017)-期望DP
    【BZOJ2879】美食节(NOI2012)-费用流+拆点+动态加边
    JQ简单图片轮播
  • 原文地址:https://www.cnblogs.com/xiaoerheiwatu/p/10028762.html
Copyright © 2011-2022 走看看