欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

浅谈iOS isa指针

程序员文章站 2024-03-23 21:15:10
...

为什么突然想写一篇关于isa的文章呢!是在写kvo原理的时候谈到了isa。有些同学就问我isa到底是什么。咱们就简单的看看

要讲isa 就要了解 instance(实例) class object(类对象) metaclass(元类)

instance(实例)

在我们开发的时候,总有时会用id来接收一些对象。但是大家想过id是怎么实现的吗?看下面这段代码,这是objc/objc.h里面摘出来的代码。
不难看出,id是一个objc_object类型的结构体指针,这个结构体里面只有一个 Class类型的变量isa(还有这个对象自己定义的相关属性等,这里就不讨论)。我们创建对象的时候,其实内部是创建了一个这样的结构体。

/// 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;

说道这里咱们明白了实例的本质,是一个结构体,里面包含了一个 Class类型的变量isa

class object(类对象) metaclass(元类)

什么是类对象,它也是这个类生成的一个对象(但是明显不是该类的实例,下面我会有验证),但是这个对象里面包含的东西更多,类对象是一个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 *` */

这个结构体里面也包含Class类型 isa ,还有Class类型的 superClass,咱们这边只研究的这两个,其他的咱们暂时先不管

类对象怎么获取?最简单的方法[ClassName class],下面我们探究一下isa 和类对象关系
先给大家介绍两个方法
object_getClass用于获取对象的isa指针指向的对象
class_isMetaClass用于判断Class对象是否为元类
下面是代码

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface Person: NSObject


@property(nonatomic,strong) NSString *tiantian;
@end

@implementation Person

+(void)tt{
    NSLog(@"tt");
}


@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Person *p = [[Person alloc] init];
        Person *p1 = [[Person alloc] init];
//---------------------------------------------------------------------------------
        //实例对象调用class方法 获取类对象
        NSLog(@"[p class] - %@",[p class]);
        //实例对象调用class方法 获取类对象
        NSLog(@"[p1 class] - %@",[p1 class]);
        //类名调用class方法 获取类对象
        NSLog(@"[Person class] - %@\n",[Person class]);
//---------------------------------------------------------------------------------
        //获取p的isa
        NSLog(@"object_getClass(p) - %@",object_getClass(p));
        //获取p1的isa
        NSLog(@"object_getClass(p1) - %@",object_getClass(p1));
        //获取类对象的isa
        NSLog(@"object_getClass([p class]) - %@",object_getClass([p class]));
        
 
//---------------------------------------------------------------------------------
    }
    return 0;
}

MyTextKVCKVO[5141:12156410] [p class] - Person
MyTextKVCKVO[5141:12156410] [p1 class] - Person
MyTextKVCKVO[5141:12156410] [Person class] - Person
MyTextKVCKVO[5141:12156410] object_getClass(p) - Person
MyTextKVCKVO[5141:12156410] object_getClass(p1) - Person
MyTextKVCKVO[5141:12156410] object_getClass([p class]) - Person

运行完了之后看结果 一脸懵逼。怎么都一样?????
我们就仔细的判断一下 具体是什么情况,再看下面的代码

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface Person: NSObject


@property(nonatomic,strong) NSString *tiantian;
@end

@implementation Person

+(void)tt{
    NSLog(@"tt");
}


@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Person *p = [[Person alloc] init];
        Person *p1 = [[Person alloc] init];

        //输出 1 。 结论:不同对象的 类对象 是一样的
        NSLog(@"%d",[p class] == [p1 class]);
        //输出 1 , 实例对象获取的类对象 和 类名获取的类对象是一样的
        NSLog(@"%d",[p class] == [Person class]);
//---------------------------------------------------------------------------------
        //类对象 和 这个 p的isa是否相等
        NSLog(@"%d",[p class] == object_getClass(p));
        //类对象 和 这个 p1的isa是否相等
        NSLog(@"%d",[p class] == object_getClass(p1));
 
    }
    return 0;
}

MyTextKVCKVO[5228:12166573] 1
MyTextKVCKVO[5228:12166573] 1
MyTextKVCKVO[5228:12166573] 1
MyTextKVCKVO[5228:12166573] 1

根据上见面的结果咱们可以得出两个结论:
1、一个类的类对象(不管是对象获取,还是类名获取)都是同一个[Person class]
2、我们可以猜测一个类的所有实例对象的isa都是指向同一个 类对象[Person class]

在看superClass

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface Person: NSObject


@property(nonatomic,strong) NSString *tiantian;
@end

@implementation Person

+(void)tt{
    NSLog(@"tt");
}


@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Person *p = [[Person alloc] init];

        //获取p的isa
        Class p_isa = object_getClass(p);
        NSLog(@"%@",p_isa);
        
        //获取类对象的superClass
        Class p_isa_superClass = class_getSuperclass(p_isa);
        NSLog(@"%@",p_isa_superClass);
        
        //获取p对象的isa 的 isa
        Class p_isa_isa = object_getClass(p_isa);
        NSLog(@"%@",p_isa_isa);
        
        //判断p的isa 和 (p对象的isa的isa)是否一样
        NSLog(@"%d",p_isa == p_isa_isa);
        
        //判断p_isa 是不是元类
        NSLog(@"%d",class_isMetaClass(p_isa));
        //判断p_isa_isa 是不是元类
        NSLog(@"%d",class_isMetaClass(p_isa_isa));
        
    }
    return 0;
}

MyTextKVCKVO[5433:12185744] Person
MyTextKVCKVO[5433:12185744] NSObject
MyTextKVCKVO[5433:12185744] Person
MyTextKVCKVO[5433:12185744] 0
MyTextKVCKVO[5433:12185744] 0
MyTextKVCKVO[5433:12185744] 1

总结:一个实例对象isa指向的是这个对象的类对象类对象isa指向的是 该类的元类
下面的图可以加强理解

浅谈iOS isa指针
910291-57780799dbe7fd12.png

咱们再想一下 superClass 也是Class对象,也会有isa 和superClass。所以就能一直找到顶层父类,下面是一个大神做的图,总结的很好,也容易懂。


浅谈iOS isa指针
851897-847ffb4b1d53ef23.png