iOS 学习

Objective-C的isa指针

2017-11-21  本文已影响1人  后浪普拉斯

isa指针

我们先看一下类的定义:

objc.h

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

可以看出:

isa 是一个objc_class结构体类型的指针。
id 是一个objc_object 结构体类型的指针。

接下来我们看objc_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;
/* Use `Class` instead of `struct objc_class *` */

结构体的参数:

isa:class 类型的指针。每个对象中有isa 指针,指向对象的类;类Class 中也有isa 指针,指向元类;元类也有isa 指针,指向根元类;而根元类的isa指针指向自身,形成封闭循环。
super_class:指向父类指针,如果是根元类(NSObject或者NSProxy)的时候,此时是NULL。
version:类的版本信息,默认是0
info:运行时使用的位标示。例如0x1(CLS_CLASS)表示该类是普通class,0x2(CLS_META)表示该类是metaclass。                    
instance_size:类的实例变量大小,内存所占的空间。
ivars:表示多少个变量,指向成员变量结构体objc_ivar_list链表的指针。
methodLists:根据标识位的不同可能指向不同,可能指向实例方法列表,也可能指向类方法列表。
cache:objective-c的消息转发需要查找dispatch table,而且还有可能遍历继承关系,所以缓存最近使用的方法。
protocols:类需要遵守的协议。

我们看一下类的继承关系:

类的继承关系.jpeg
本质上对象时类的实例,其中包含类定义的成员变量和成员方法列表,而对象通过isa指针指向类。
类的本质上是一个对象,类其实是元类的实例,其中包含元类定义的类方法列表,类通过isa指针指向元类。
所有的元类继承一个根元类,而根元类的isa指针指向自己,形成封闭循环。
在objective-c中类和对象(类的实例)在本质上是没有区别的,任何类的定义都是对象,都包含isa指针。

类的继承关系的解读:

meta class:一个 class 对象的class。元类不存在对应的类,只是一种结构变量。
当我们给对象发送消息时,消息在此对象的类方法列表中寻找。
当我们给类发送消息的时,消息在此类的元类的方法列表中寻找。
  1. 每个Class都有一个isa指针指向一个唯一的Meta Class。
  2. 每个Meta Class的isa指针都指向最上层的Meta Class(即Root class,一般是NSObject类)。
  3. 最上层的Meta Class的isa指针指向自己,形成回路。
  4. 每一个Meta Class的super Class 指向原本类Class 的super Class的Meta Class;但是最底层的Meta Class的super class 指向Root class本身(一般是NSObject class本身)。
  5. 最上层的Root Class的super Class 指向nil。

类的动态操作

知道isa和super class在对象和类中的指向,我们就可以根据objc_class的定义来操作,将项目转换成MRC的模式。

//
//  main.m 在项目的.m 文件中
//  objectClass_dyn

#import <UIKit/UIKit.h>
#import <objc/objc.h>
#import <objc/runtime.h>
#import "AppDelegate.h"

void selfFun(id self, SEL _cmd);

int main(int argc, char * argv[]) {
    @autoreleasepool {

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

根据isa的原理动态的操作类

#import <UIKit/UIKit.h>
#import <objc/objc.h>
#import <objc/runtime.h>
#import "AppDelegate.h"

void personPrint(id self, SEL _cmd);

int main(int argc, char * argv[]) {
    @autoreleasepool {
        
        //1、通过objc_allocateClassPair创建一个新类
        //2、通过class_addMethod和 class_addIvar方法给新类添加方法和属性
        //3、通过调用objc_registerClassPair 使用新类
        
        
        /**
         创建新类
         //objc_allocateClassPair(Class _Nullable superclass, const char * _Nonnull name,
         size_t extraBytes)
         @param superclass 新创建类的超类,或使用nil创建新的根类
         @param name 新类的名称
         @param extraBytes 类占的空间大小
         @return 返回一个新类
         */
        //创建一个NSDemo的新类
        Class personClass = objc_allocateClassPair([NSObject class], "Person", 0);

        //创建标志位
        BOOL ok = NO;
        
        /**
         为类添加方法,此方法只能在objc_allocateClassPair 和 objc_registerClassPair两个函数之间添加变量
         class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
         const char * _Nullable types)
         @param cls 增加方法的类
         @param name 添加到本类中方法的名称
         @param imp  指向方法实现的指针,当方法的实现是用�objective-c的形式的时候,我们可以用class_getMethodImplementation获取方法的IMP
         @param types 表示返回类型和参数,例如:"v@:" :void类型方法,没有参数传入; "i@:" : int类型的方法,没有参数; "i@:@":int类型的方法,传入一个参数
         @return 添加是否成功
         这个方法会覆盖父类的方法,但是不会覆盖自己已经存在的方法,如果本类已存在这个方法,添加不会成功,如果要重写本类的方法实现,需要调用method_setImplementation方法
         */
        ok = class_addMethod(personClass, @selector(personFunc), (IMP)personPrint, "@v:");
        ok == YES ? nil : NSLog(@"Class class_addMethod failed");
        
        /**
         向ivars中添加变量
         class_addIvar(Class _Nullable cls, const char * _Nonnull name, size_t size,
         uint8_t alignment, const char * _Nullable types);
         @param cls 添加变量的类
         @param name 添加变量的名称
         @param size 添加变量的尺寸
         @param alignment 对齐方式,log2(sizeof(pointer_type))
         @param types  类型 @encode(pointer_type)
         @return 添加成功返回YES,添加失败返回NO
         */
        ok = class_addIvar(personClass, "personName", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
        ok == YES ? nil : NSLog(@"class class_addIvar failed");
        
        //用objc_registerClassPair完成类的创建
        objc_registerClassPair(personClass);
        
        //创建类的实例
        id person = class_createInstance(personClass, 0);
        
        if ([person respondsToSelector:@selector(personFunc)]) {
            [person performSelector:@selector(personFunc)];
        }
        //给实例变量赋值
        object_setInstanceVariable(person, "alex", nil);
        
        void *outValue = (void *)0x1;
        
        /**
         获取类实例变量的值
         object_getInstanceVariable(id _Nullable obj, const char * _Nonnull name,
         void * _Nullable * _Nullable outValue)

         @param obj 类的实例
         @param name 字符串,一个在类实例变量中,你希望获得的字符串
         @param outValue 一个指向实例变量的指针
         */
        
        object_getInstanceVariable(person, "alex", &outValue);
        if(nil == outValue){
            NSLog(@"outValue is nil\n");
        }
        
        //释放实例变量的内存
        object_dispose(person);
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

void personPrint(id  self, SEL _cmd)
{
    NSLog(@"self: %@ -- _cmd: %s",self,_cmd);
    
    Class isa = object_getClass(self);
    
    while (1) {
        
        if (isa == isa->isa) {  //Finally, NSObject's metaclass points to itself
            NSLog(@"== : %p, %@", isa, objc_getMetaClass(class_getName([isa class])));
            break;
        }
        
        NSLog(@"!= : %p, %@", isa, objc_getMetaClass(class_getName([isa class])));
        isa = isa->isa; //Then, isa is assigned to NSDemo's metaclass
    }
}

最终的结果:

2017-11-23 11:49:23.812675+0800 objectClass_dyn[66473:12273045] self: <Person: 0x6000000107f0> -- _cmd: personFunc
2017-11-23 11:49:23.813606+0800 objectClass_dyn[66473:12273045] != : 0x60000024cba0, Person
2017-11-23 11:49:23.813863+0800 objectClass_dyn[66473:12273045] != : 0x60000024cbd0, Person
2017-11-23 11:49:23.814810+0800 objectClass_dyn[66473:12273045] == : 0x10ac58e58, NSObject
2017-11-23 11:49:23.814999+0800 objectClass_dyn[66473:12273045] outValue is nil

可以看出:
1、我们动态的创建一个类,而且动态的绑定了方法,并且实现了动态的调用。
2、isa的原理,递归不断的指向自己的父类

上一篇 下一篇

猜你喜欢

热点阅读