OC Runtime(一)一些底层实现原理

runtime是一个由C语言和汇编编写的运行时库。runtime为OC提供 了面向对象的能力。他的作用体现在,加载类的信息、调度方法、方法的转发等。使得  OC面向对象编程成为可能。
Selector 底层实现为C语言结构体,selector可以唯一标识一个你想要调用的OC方法。
在runtime底层它是这样定义的
typedef struct objc_selector  *SEL;
并这样使用。。。
SEL aSel = @selector(movieTitle);

消息
OC中方法的调度,是通过消息传递的方式实现的。OC的消息和C函数的调度不同。事实上,当你向一个对象发送一个消息并不意味着它会执行该消息。消息接受者可以去check消息发送至,并根据该决定执行不同的方法或将消息转发到不同的目标对象上。如果你在运行时库查看一个类,你会看到这个
typedef struct objc_class *Class;
typedef struct objc_object {   
     Class isa;
} *id;
OC中类的结构体,以及OC中对象的结构体。
OC中每一个类对象都含有一个指向类的isa结构体指针。isa指针,是每一个类对象运行时需要考察的对象,通过isa确定它的类。这样在该对象接收到某个消息时,可以去检索是否可以响应该消息。id指针,标识这该类是一个OC对象。如果我们有一个id类型的对象。我们可以获取它的类对象,查看它是否响应某一个方法,等等。从而在对该对象做具体操作的时候,我们可以确定该对象的具体类型。

在block中,我们可以同样看到isa指针
struct Block_literal_1 {   
     void *isa; // initialized to &_NSConcreteStackBlock or           &_NSConcreteGlobalBlock   
     int flags;   
     int reserved;   
     void (*invoke)(void *, ...);   
     struct Block_descriptor_1 {
          unsigned long int reserved; // NULL    
          unsigned long int size;  // sizeof(struct Block_literal_1) // optional helper functions    
          void (*copy_helper)(void *dst, void *src);    
          void (*dispose_helper)(void *src);   
     } *descriptor;   
     // imported variables
};
block本身被设计为兼容Objective-c运行时,他们被视为对象。所有他们才可以被声明为 -retain,-release,-copy,等等.

IMP
typedef id (*IMP)(id self,SEL _cmd,...);
IMP是OC中方法实现的指针,编译器会自动生成。OC运行时就是通过IMP来调用方法的。那么OC Class的底层实现又是怎么样的呢
一个普通的类实现
@interface MyClass : NSObject {
     //vars
     NSInteger counter;
}
//methods
-(void)doFoo;
@end
但是,runtime有更多的追踪,就像这样
#if !__OBJC2__
     Class super_class OBJC2_UNAVAILABLE;
     const char *name OBJC2_UNAVAILABLE;
     long version OBJC2_UNAVAILABLE;
     l ong info OBJC2_UNAVAILABLE;
     long instance_size OBJC2_UNAVAILABLE;
     struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
     struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
     struct objc_cache *cache OBJC2_UNAVAILABLE;
     struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
我们看到一个类中包含了,父类的引用,名字,实例变量,缓存,协议,方法等等。这些都是运行时库或他的实例在响应消息时需要的信息。

我们知道Class定义了类实例对象,但是Class本身也是一个对象。那么他们是如何工作的呢
答案是meta Class
当你发送一个消息[ NSObject alloc ]你实际上是发送一个消息给类的对象,这类对象即为该类meta Class的实例。类似于,当你说从NSObject继承的时候,所有类都指向NSObject做为它的父类。所有的meta Class 都指向root meta Class作为他们的父类。所有meta Class在他们的method_List中都只是包含了该类的类方法。用于类方法的相应。

为什么我们写代码时,继承自苹果提供的类。
在OC中当我们分配一个实例时
MyObject *object = [[MyObject alloc] init];
最初消息是指向alloc来分配的。如果你点进去看一下文档
"The isa instance variable of the new instance is initialized to a data structure that describes the class; memory for all other instance variables is set to 0."
从苹果类继承,我们不仅继承了一些属性,而且,这让我们很容易地分配和创建我们的对象在内存中的结构。(符合运行时预期的结构,(包含一个isa指针指向我们的类)以及合理的大小)

那么 struct objc_cache *cache 是什么呢

OC运行时可以通过ISA指针找到对应的类对象,从而检索到类对象中声明的Method_List 。然而我们可能只会调用其中的一小部分方法,每次在方法调度的时候都去遍历这些方法是没有意义的。所以类声明了cache,每一次当年调度方法在类中搜索得到正确的反馈后,就会将该方法加入到cache中。所以当 objc_msgSend()搜索在class中搜索方法时,它首先会搜索class cache。

alloc与init
对于NSObject 并没有什么重要的事情在 initialization中发生。但是并不总是这样。

#import < Foundation/Foundation.h> @interface MyObject : NSObject { NSString *aString; } @property(retain) NSString *aString; @end @implementation MyObject -(id)init { if (self = [super init]) {   [self setAString:nil]; } return self; } @synthesize aString; @end int main (int argc, const char * argv[]) {     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; id obj1 = [NSMutableArray alloc]; id obj2 = [[NSMutableArray alloc] init];   id obj3 = [NSArray alloc]; id obj4 = [[NSArray alloc] initWithObjects:@"Hello",nil];   NSLog(@"obj1 class is %@",NSStringFromClass([obj1 class])); NSLog(@"obj2 class is %@",NSStringFromClass([obj2 class]));   NSLog(@"obj3 class is %@",NSStringFromClass([obj3 class])); NSLog(@"obj4 class is %@",NSStringFromClass([obj4 class]));   id obj5 = [MyObject alloc]; id obj6 = [[MyObject alloc] init];   NSLog(@"obj5 class is %@",NSStringFromClass([obj5 class])); NSLog(@"obj6 class is

%@",NSStringFromClass([obj6 class]));   [pool drain];     return 0; }

obj1 class is __NSPlaceholderArray obj2 class is NSCFArray obj3 class is __NSPlaceholderArray obj4 class is NSCFArray obj5 class is MyObject obj6 class is MyObject


这是因为在OC 中在alloc中返回一个对象,init返回另一个对象是有可能的。

objc_msgSend做了什么
假设我们有这样的OC代码
[self printMessageWithString:@"Hello World!"];
在runtime中显然它会被转换成这样
objc_msgSend(self,@selector(printMessageWithString:),@"Hello World!");

其实objc_msgSend做了很多的事情,总结来说如下
1、查看是否可以忽略该请求。例如,如果我们是在垃圾回收机制下,我们可以忽略 -retain,-release等的调用。
2、检测target是否为nil。不像其他语言,向nil发送消息在OC中是完全合法的
3、现在我们需要找到方法的指针IMP,所以,我们首先从类的cache中寻找,如果找到,执行。
4、没有在cache中找到对应的IMP,从方法列表中寻找对应的IMP,如果找到,执行。
5、IMP在cache和方法列表中都没有找到,进入消息转发流程。这意味着最后你所写的方法会被编译器转换为C函数。所以当你写了这样一个方法
-(int)doComputeWithNum:(int)aNum 
会被翻译成这样
int aClass_doComputeWithNum(aClass *self,SEL _cmd,int aNum)
OC运行时通过函数指针调用函数。这些由编译器转换成的C语言函数是无法被直接调用的,然后Cocoa框架提供了获得对于函数函数指针的方式
//declare C function pointer
int (computeNum *)(id,SEL,int);
 
//methodForSelector is COCOA & not ObjC Runtime
//gets the same function pointer objc_msgSend gets
computeNum = (int (*)(id,SEL,int))[target methodForSelector:@selector(doComputeWithNum:)];
 
//execute the C function pointer returned by the runtime
computeNum(obj,@selector(doComputeWithNum:),aNum); 











你可能感兴趣的:(OC Runtime(一)一些底层实现原理)