收藏ios

isa指针

2018-10-18  本文已影响22人  高思阳

OC是一门面向对象的语言,每一个对象都是类的一个实例。在objective-c语言的内部,每一个对象都有一个isa指针,指向该指针的类。每一个类描述了一系例他的实例的特点,包括成员变量的列表,成员函数的列表。每一个对象都可以接收消息,而对象接收消息列表保存在他所对应的类中。

当我们初始化一个对象的时候,是怎么发送消息的

NSObject *obj=[[NSObject alloc] init];

调用方法,其实是给对象发送消息,在编译时这句话会翻译成一个C的函数调用,即:

objc_msgSend(objc_msgSend([NSObject class],@selector(alloc)),@selector(init));

使用这个函数的需要引入头文件:

#import <objc/message.h>

那不是把 OC代码转换成C。C语言函数在调用编译的时候就会决定调用哪个函数,而OC是一种动态语言,他会尽可能把代码的从编译链接是推迟到运行时,这就是OC运行时多态。 (给一个对象发送消息,并不会立即执行,而是在运行的时候在去寻找他对应的实现)

在Xcode中打开,NSObject.h和objc.h,我们可以看到,NSObject就是一个包含isa指针的结构体,按照面向对象的设计原则,所有的事物都应该是对象,所以严格的说OC并不是完全面向对象的(因为含有int double 类型的变量)。在OC语言中,每一个类实际上也是一个对象。每一个类也有一个isa指针。每一个类也可以接收消息,例如代码[NSObject alloc],就是向NSObject这个类发送名为 “alloc” 的消息。

在oc中,因为类也是一个对象,所以也必须是另外一个类的实例,这个类就是元类(metaclass)。

一个 Objective-C 方法会被编译成 objc_msgSend,这个函数有两个默认参数,id 类型的 self, SEL 类型的 op。我们先看看 id 的定义:

typedef struct objc_object *id;

struct objc_object {
  Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};

我们可以看到,在 objc_object 结构体中,只有一个指向 Class 类型的 isa 指针。

我们再看看 Class 的定义:

struct objc_class {
  Class _Nonnull isa OBJC_ISA_AVAILABILITY;

#if !__OBJC2__

  Class _Nullable super_class OBJC2_UNAVAILABLE;

  const char * _Nonnull name OBJC2_UNAVAILABLE;

  long version OBJC2_UNAVAILABLE;

  long info OBJC2_UNAVAILABLE;

  long instance_size OBJC2_UNAVAILABLE;

  struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;

  struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;

  struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;

  struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;

#endif
} OBJC2_UNAVAILABLE;

里面有很多参数,很显眼的能看到这一行:

struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;

看名字也容易理解,这个 methodLists 就是用来存放方法列表的。我们再看看 objc_method_list 这个结构体:

struct objc_method_list {

  struct objc_method_list * _Nullable obsolete OBJC2_UNAVAILABLE;

  int method_count OBJC2_UNAVAILABLE;

#ifdef __LP64__

  int space OBJC2_UNAVAILABLE;

#endif

  /* variable length structure */

  struct objc_method method_list[1] OBJC2_UNAVAILABLE;

}

里面的 objc_method ,也就是我们熟悉的 Method:

struct objc_method {

  SEL _Nonnull method_name OBJC2_UNAVAILABLE;

  char * _Nullable method_types OBJC2_UNAVAILABLE;

  IMP _Nonnull method_imp OBJC2_UNAVAILABLE;
}

Method 里面保存了三个参数:

经过层层挖掘,我们能明白实例对象调用方法的大致逻辑:

MyClass *myClass = [[MyClass alloc] init];
[myClass printLog];

类对象的类方法又是怎么找到并执行的?

由上文,我们已经知道,实例对象是通过 isa 指针,找到其类对象(Class)中保存的方法列表中的具体实现的。

比如:

MyClass *myClass = [[MyClass alloc] init];
[myClass printLog];

可以理解为:printLog 方法就是保存在 MyClass 中的。

那么如果是个类方法,又是保存在什么地方的呢?

我们回顾下 Class 的定义:

struct objc_class {

  Class _Nonnull isa OBJC_ISA_AVAILABILITY;

#if !__OBJC2__

  Class _Nullable super_class OBJC2_UNAVAILABLE;

  const char * _Nonnull name OBJC2_UNAVAILABLE;

  long version OBJC2_UNAVAILABLE;

  long info OBJC2_UNAVAILABLE;

  long instance_size OBJC2_UNAVAILABLE;

  struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;

  struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;

  struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;

  struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;

#endif

} OBJC2_UNAVAILABLE;

可以发现到这一行:

Class _Nonnull isa OBJC_ISA_AVAILABILITY;

这里的 isa 同样是指向一个 Class 的指针。上文中,我们也知道了类对象的 isa 指针是指向元类对象的。那么不难得出:

类对象的类方法,是保存在元类对象中的!

类对象和元类对象都是 Class 类型,仅仅服务的对象不同罢了。找到了元类对象,自然就找到了元类对象中的 methodLists,接下来就和实例对象的方法寻找调用一样的流程了。

关于对象、类、元类关系:


对象、类、元类关系
对象、类、元类关系

从上图可以看出:

对象的isa指针指向类对象,类对象的isa指针指向元类,元类的isa指针指向根元类,根元类的isa指针指向自身。

NSObject的元类的父类是NSObject , NSObject的isa指针又指向NSObject的元类,所以:

注意:在Objective-C中,几乎所有的类都是继承与NSObject,这里的几乎所有是因为官网关于Cocoa框架有介绍:Cocoa supplies two root classes: NSObject and NSProxy.不继承NSObject的都继承NSProxy,因为NSProxy的应用比较特殊,在Cocoa程序中比较少见,具体的例子可以参考官方API说明文档中关于NSProxy类的介绍,里面讲到了一些例子,比如NSDistantObject

定义Person类


image.png

使用object_getClass()可以获取某个对象的isa指针指向的对象:

Person *p = [[Person alloc] init];

id class = object_getClass(p1);

id metaClass = object_getClass(class);

id rootMetaClass = object_getClass(metaClass);

[self printMethods:class];

[self printMethods:metaClass];

[self printMethods:rootMetaClass];
//下面方法可以打印出类对象的名称和里面的方法
- (void) printMethods:(Class)cls
{
  unsigned int count ;

  Method *methods = class_copyMethodList(cls, &count);

  NSMutableString *methodNames = [NSMutableString string];

  [methodNames appendFormat:@"%@ - ", cls];

  for (int i = 0 ; i < count; i++) {

    Method method = methods[i];

    NSString *methodName = NSStringFromSelector(method_getName(method));

    [methodNames appendString: methodName];

    [methodNames appendString:@" "];

  }

  NSLog(@"%@",methodNames);

  free(methods);

}

打印结果:


打印结果

对象的isa指针指向它的类对象,类对象的isa指针指向它的元类对象,元类的isa指针指向它的根元类对象(这里是NSObject)

从打印结果可以看出,类对象打印出来的是对象的方法,元类对象打印出来的是类方法。

链接:https://blog.csdn.net/qq_22854687/article/details/51245568

上一篇下一篇

猜你喜欢

热点阅读