理解Runtime特性

来源:互联网 发布:常用网络端口 编辑:程序博客网 时间:2024/06/16 12:04

Objective-C 使用运行时机制,即在程序运行时才决定执行的方法对方法进行调用,其中最主要的是一套消息发送机制。使用C语言实现,方法包含在头文件#import<objc/objc-runtime.h>中,里面包含很多运行时方法。

1.如我们常规的一条方法调用:[object   addDanmicMethod],会在编译时被翻译成objc_msgSend(object@selector(addDanmicMethod));每一个class包含一个is指针,还有一个dispatch_table(分发表)存放所有成员方法的入口。

这个方法先去查找 self这个对象或者其父类是否响应 @selector(addDanmicMethod),如果从这个类的方法分发表或者 cache里面找到了,就调用它对应的函数指针。如果找不到,那就会执行一些其他的东西。步骤如下:

  1. 检测这个 selector是不是要忽略的。比如 Mac OS X 开发,有了垃圾回收就不理会 retain, release这些函数了。
  2. 检测这个 target是不是 nil 对象。ObjC的特性是允许对一个 nil 对象执行任何一个方法不会 Crash,因为会被忽略掉。
  3. 如果上面两个都过了,那就开始查找这个类的 IMP,先从 cache里面找,完了找得到就跳到对应的函数去执行。
  4. 如果 cache找不到就找一下方法分发表。
  5. 如果还找不到就要开始消息转发逻辑了。

这种消息转发机制是 Runtime非常重要的一个特性,大概的步骤如下:

    1.查找该类及其父类的 cahce 和方法分发表,在找不到的情况下执行2

    2.执行 + (BOOL) resolveInstanceMethod:(SEL)aSEL 方法。这就给了程序员一次机会,可以告诉 runtime 在找不到改方法的情况下执行什么方法。举个例子:

-(NSString *)setaddFunction:(NSString *)string1 withValue:(NSString *)string2

{

    return [string1stringByAppendingString:string2];

}


NSString* myDaynamicIMP(id self,SEL _cmd)

{

    NSLog(@"result:%@",[self setaddFunction:@"this is an" withValue:@"daynamic method"]);

    return [self setaddFunction:@"this is an" withValue:@" daynamic method"];

}


NSString* myNewDaynamicIMP(id self,SEL _cmd)

{

    NSLog(@"result:%@",[self setaddFunction:@"this is an" withValue:@"daynamic replace method"]);

    return [self setaddFunction:@"this is an" withValue:@" daynamic replace method"];

}


-(IBAction)clickChangeBehavior:(id)sender

{

    SEL  oriFunction = @selector(myDaynamicIMP);

    IMP orginIMP = [NSObject instanceMethodForSelector:oriFunction];

    IMP imp2 = class_replaceMethod([self class],oriFunction,(IMP)myNewDaynamicIMP,NULL);

    _textView.text = [self performSelector:@selector(myDaynamicIMP)];

}


+(BOOL)resolveInstanceMethod:(SEL)sel

{

    if(sel == @selector(addDanmicMethod))

    {

        class_addMethod([self class], sel, (IMP)myDaynamicIMP, "N@:");//动态添加一个实现方法

        return YES;

    }

    return [super resolveInstanceMethod:sel];

}


3.接下来 Runtime 会调用 – (id)forwardingTargetForSelector:(SEL)aSelector 方法。这就给了程序员第二次机会,如果你没办法在自己的类里面找到替代方法,你就重载这个方法,然后把消息转给其他的Object

            -(id)forwardingTargetForSelector:(SEL)aSelector

{

    if(aSelector == @selector(addDanmicMethod))

  {

  return [self setaddFunction:@"this is an" withValue:@" daynamic forward method"]

  }

 return [self forwardingTargetForSelector:aSelector];

}

4.最后,Runtime 会调用 – (void)forwardInvocation:(NSInvocation *)anInvocation 这个方法。NSInvocation 其实就是一条消息的封装。如果你能拿到 NSInvocation,那你就能修改这条消息的 target, selector  arguments。举个例子:

-(void)forwardInvocation:(NSInvocation *)invocation{

SEL invSEL = invocation.selector;

 if([altObject respondsToSelector:invSEL]) {

[invocation invokeWithTarget:altObject];

} else {

 [self doesNotRecognizeSelector:invSEL];

 }

}

 默认情况下 NSObject  forwardInvocation 的实现就是简单地执行 -doesNotRecognizeSelector: 这个方法,所以如果你想真正的在最后关头去转发消息你可以重载这个方法。


0 0
原创粉丝点击