iOS Runtime(二)-类和对象

2021-07-27  本文已影响0人  搬砖的crystal

一、类的基础数据结构

1. objc_class

OC类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。

typedef struct objc_class *Class;

查看objc/runtime.h中objc_class结构体的定义如下:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                       OBJC2_UNAVAILABLE;  // 父类
    const char *name                        OBJC2_UNAVAILABLE;  // 类名
    long version                            OBJC2_UNAVAILABLE;  // 类的版本信息,默认为0
    long 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

} OBJC2_UNAVAILABLE;

但在OC2.0版本中 已经不使用了(#if !OBJC2和OBJC2_UNAVAILABLE)。
在新版中,objc_class继承结构体objc_object,并且结构体内有一些函数(因为是c++结构体,在c上做了扩展,因此结构体中可以包含参数)。

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    //方法缓存
    cache_t cache;             // formerly cache pointer and vtable
    //其中只含有一个 64 位的 bits 用于存储与类有关的信息
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }

注释掉了Class ISA,是由于继承的objc_object结构体中有isa指针。

(1)class_rw_t和class_rw_t

objc_class结构体内部,class_rw_t是通过bits调用data方法的来的。在data方法内部,将bits与 FAST_DATA_MASK进行位运算,会得到class_rw_t结构体。

class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }

class_rw_t结构体(rw代表readwrite可读可写,t代表table表)

struct class_rw_t {
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;
    //方法列表
    method_list_t *methods;
    //属性列表
    property_list_t *properties;
    //协议列表
    const protocol_list_t * protocols;
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
};

指向常量的指针 ro,其中存储了当前类在编译期就已经确定的属性、方法以及遵循的协议。
其中还包含一个struct class_ro_t的结构体(ro代表readonly,只读)

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    //instance对象占用的内存空间
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif
    const uint8_t * ivarLayout;
    //类名
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    //成员变量列表
    const ivar_list_t * ivars;
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
};

class_ro_t 和 class_rw_t 的分析:
class_rw_t结构体内有一个指向class_ro_t结构体的指针。
每个类都对应有一个class_ro_t结构体和一个class_rw_t结构体。在编译期间,class_ro_t结构体就已经确定,objc_class中的bits的data部分存放着该结构体的地址。在runtime运行之后,具体说来是在运行runtime的realizeClass 方法时,会生成class_rw_t结构体,该结构体包含了class_ro_t,并且更新data部分,换成class_rw_t结构体的地址。
类的realizeClass运行之前:


realizeClass运行之前

类的realizeClass运行之后:


realizeClass运行之后 细看两个结构体的成员变量会发现很多相同的地方,他们都存放着当前类的属性、实例变量、方法、协议等等。区别在于:class_ro_t存放的是编译期间就确定的;而class_rw_t是在runtime时才确定,它会先将class_ro_t的内容拷贝过去,然后再将当前类的分类的这些属性、方法等拷贝到其中。所以可以说class_rw_t是class_ro_t的超集,当然实际访问类的方法、属性等也都是访问的class_rw_t中的内容。
(2)isa指针

在OC中,任何类的定义都是对象。类和类的实例(对象)没有任何本质上的区别。任何对象都有isa指针。

    NSString *str = @"aaa";

str本质是一个objc_object结构体,而这个结构体的成员变量isa指针指向了NSString类,NSString类其实是类对象。
struct objc_classs结构体里存放的数据称为元数据(metadata),类对象的isa指针指向的我们称之为元类(meta class)。元类中保存了创建类对象以及类方法所需的所有信息。



isa是一个Class类型的指针,每个实例对象有个isa指针,它指向对象的类,而Class里也有isa指针,指向meteClass(元类)。元类也是类,它也是对象。元类也有isa指针,它的isa指针最终指向一个根元类,根元类的isa指针指向自己本身。


2.objc_object与id

objc_object是表示一个类的实例的结构体

struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();

这个结构体只有一个类的isa指针。当向一个OC对象发送消息时,运行时库会根据实例对象的isa指针找到这个实例对象所属的类。Runtime库会在类的方法列表及父类的方法列表中去寻找与消息对应的selector指向的方法。找到后即运行这个方法。

当创建一个特定类的实例对象时,分配的内存包含一个objc_object数据结构,然后是类的实例变量的数据。NSObject类的alloc和allocWithZone:方法使用函数class_createInstance来创建objc_object数据结构。

另外还有我们常见的id,它是一个objc_object结构类型的指针。它的存在可以让我们实现类似于C++中泛型的一些操作。该类型的对象可以转换为任何一种对象,有点类似于C语言中void *指针类型的作用。

二、类和对象的操作函数

runtime提供了大量的函数来操作类与对象。类的操作方法大部分是以class为前缀的,而对象的操作方法大部分是以objc或object_为前缀。

1.操作函数
(1)类名
// 获取类的类名
const char * class_getName ( Class cls );
(2)父类(super class)和元类(meta class)
// 获取类的父类
Class class_getSuperclass ( Class cls );

// 判断给定的Class是否是一个元类
BOOL class_isMetaClass ( Class cls );
(3)实例变量大小(instance_size)
// 获取实例大小
size_t class_getInstanceSize ( Class cls );
(4)成员变量(ivars)

在objc_class中,所有的成员变量、属性的信息是放在链表ivars中的。ivars是一个数组,数组中每个元素是指向Ivar(变量信息)的指针。

// 获取类中指定名称实例成员变量的信息
Ivar class_getInstanceVariable ( Class cls, const char *name );

// 获取类成员变量的信息
Ivar class_getClassVariable ( Class cls, const char *name );

// 添加成员变量
BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types );

// 获取整个成员变量列表
Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );

不能向编译后得到的类中增加实例变量,因为编译后的类已经注册在Runtime中,类结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经确定,同时Runtime会调用class_setIvarLayout或class_setWeakIvarLayout来处理strong weak 引用,所以不能向存在的类中添加实例变量。

(5)属性(Property)
// 获取指定的属性
objc_property_t class_getProperty ( Class cls, const char *name );

// 获取属性列表
objc_property_t * class_copyPropertyList ( Class cls, unsigned int *outCount );

// 为类添加属性
BOOL class_addProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );

// 替换类的属性
void class_replaceProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );
(6)方法(methodLists)
// 添加方法
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );

// 获取实例方法
Method class_getInstanceMethod ( Class cls, SEL name );

// 获取类方法
Method class_getClassMethod ( Class cls, SEL name );

// 获取所有方法的数组
Method * class_copyMethodList ( Class cls, unsigned int *outCount );

// 替代方法的实现
IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );

// 返回方法的具体实现
IMP class_getMethodImplementation ( Class cls, SEL name );
IMP class_getMethodImplementation_stret ( Class cls, SEL name );

// 类实例是否响应指定的selector
BOOL class_respondsToSelector ( Class cls, SEL sel );
(7)协议(objc_protocol_list)
// 添加协议
BOOL class_addProtocol ( Class cls, Protocol *protocol );

// 返回类是否实现指定的协议
BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );

// 返回类实现的协议列表
Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );
2. 示例
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface DJClass : NSObject <NSCopying, NSCoding>

@property (nonatomic, strong) NSArray *array;

@property (nonatomic, copy) NSString *string;

- (void)method1;

- (void)method2;

+ (void)classMethod1;
@end

NS_ASSUME_NONNULL_END
@interface DJClass () {
    NSInteger       _instance1;

    NSString    *   _instance2;
}

@property (nonatomic, assign) NSUInteger integer;

- (void)method3WithArg1:(NSInteger)arg1 arg2:(NSString *)arg2;

@end

@implementation DJClass

+ (void)classMethod1 {

}

- (void)method1 {
    NSLog(@"call method method1");
}

- (void)method2 {

}

- (void)method3WithArg1:(NSInteger)arg1 arg2:(NSString *)arg2 {

    NSLog(@"arg1 : %ld, arg2 : %@", arg1, arg2);
}

@end
    DJClass *myClass = [[DJClass alloc] init];
    unsigned int outCount = 0;

    Class cls = myClass.class;

    // 类名
    NSLog(@"class name: %s", class_getName(cls));
    // 父类
    NSLog(@"super class name: %s", class_getName(class_getSuperclass(cls)));

    // 是否是元类
    NSLog(@"MyClass is %@ a meta-class", (class_isMetaClass(cls) ? @"" : @"not"));

    // 变量实例大小
    NSLog(@"instance size: %zu", class_getInstanceSize(cls));

    // 成员变量
    Ivar *ivars = class_copyIvarList(cls, &outCount);
    for (int i = 0; i < outCount; i++) {
        Ivar ivar = ivars[i];
        NSLog(@"instance variable's name: %s at index: %d", ivar_getName(ivar), i);
    }
    free(ivars);

    // 指定名称成员变量信息
    Ivar string = class_getInstanceVariable(cls, "_string");
    if (string != NULL) {
        NSLog(@"instace variable %s", ivar_getName(string));
    }

    // 属性操作
    objc_property_t * properties = class_copyPropertyList(cls, &outCount);
    for (int i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        NSLog(@"property's name: %s", property_getName(property));
    }
    free(properties);

    // 获取指定属性
    objc_property_t array = class_getProperty(cls, "array");
    if (array != NULL) {
        NSLog(@"property %s", property_getName(array));
    }

    // 方法操作
    Method *methods = class_copyMethodList(cls, &outCount);
    for (int i = 0; i < outCount; i++) {
        Method method = methods[i];
        NSLog(@"method's signature: %s", method_getName(method));
    }
    free(methods);

    // 获取实例方法
    Method method1 = class_getInstanceMethod(cls, @selector(method1));
    if (method1 != NULL) {
        NSLog(@"method %s", method_getName(method1));
    }

    // 获取类方法
    Method classMethod = class_getClassMethod(cls, @selector(classMethod1));
    if (classMethod != NULL) {
        NSLog(@"class method : %s", method_getName(classMethod));
    }

    // 类实例是否响应指定的selector
    NSLog(@"MyClass is%@ responsd to selector: method3WithArg1:arg2:", class_respondsToSelector(cls, @selector(method3WithArg1:arg2:)) ? @"" : @" not");

    // 方法的具体实现
    IMP imp = class_getMethodImplementation(cls, @selector(method1));
    imp();

    // 协议
    Protocol * __unsafe_unretained * protocols = class_copyProtocolList(cls, &outCount);
    Protocol * protocol;
    for (int i = 0; i < outCount; i++) {
        protocol = protocols[i];
        NSLog(@"protocol name: %s", protocol_getName(protocol));
    }

    // 类是否实现指定的协议
    NSLog(@"MyClass is%@ responsed to protocol %s", class_conformsToProtocol(cls, protocol) ? @"" : @" not", protocol_getName(protocol));

三、动态创建类和对象

1.动态创建类
// 创建一个新类和元类
Class objc_allocateClassPair ( Class superclass, const char *name, size_t extraBytes );

// 销毁一个类及其相关联的类
void objc_disposeClassPair ( Class cls );

// 在应用中注册由objc_allocateClassPair创建的类
void objc_registerClassPair ( Class cls );
#import "ViewController.h"
#import <objc/runtime.h>
#import "DJClass.h"
@interface ViewController ()
@end
@implementation ViewController
-(void)viewDidLoad{
    [super viewDidLoad];
    
    Class cls = objc_allocateClassPair([DJClass class], "DJSubClass", 0);
    class_addMethod(cls, @selector(method3), (IMP)imp_Method3, "v@:");
    class_replaceMethod(cls, @selector(method1), (IMP)imp_submethod1, "v@:");
    class_addIvar(cls, "_ivar1", sizeof(NSString *), log(sizeof(NSString *)), "i");

    objc_property_attribute_t type = {"T", "@\"NSString\""};
    objc_property_attribute_t ownership = { "C", "" };
    objc_property_attribute_t backingivar = { "V", "_ivar1"};
    objc_property_attribute_t attrs[] = {type, ownership, backingivar};

    class_addProperty(cls, "property2", attrs, 3);
    objc_registerClassPair(cls);

    id instance = [[cls alloc] init];
    [instance performSelector:@selector(method3)];
    [instance performSelector:@selector(method1)];

}

void imp_Method3(id self,SEL _cmd){
    NSLog(@"call method method3");
}

void imp_submethod1(id self,SEL _cmd){
    NSLog(@"call method submethod1");
}
@end
//输出结果
2021-07-22 13:45:11.529352+0800 DJTestDemo[3314:98781] call method method3
2021-07-22 13:45:11.529470+0800 DJTestDemo[3314:98781] call method submethod1
2.动态创建对象
// 创建类实例
id class_createInstance ( Class cls, size_t extraBytes );

// 在指定位置创建类实例
id objc_constructInstance ( Class cls, void *bytes );

// 销毁类实例
void * objc_destructInstance ( id obj );

调用class_createInstance的效果与+alloc方法类似。区别看下面的示例:

    id theObject = class_createInstance(NSString.class, sizeof(unsigned));
    id str1 = [theObject init];

    NSLog(@"%@", [str1 class]);

    id str2 = [[NSString alloc] initWithString:@"test"];
    NSLog(@"%@", [str2 class]);
//输出结果
2021-07-22 13:57:12.877791+0800 DJTestDemo[3444:105852] NSString
2021-07-22 13:57:12.877925+0800 DJTestDemo[3444:105852] __NSCFConstantString

可以看到,使用class_createInstance函数获取的是NSString实例,而不是类簇中的默认占位符类__NSCFConstantString。

四、实例操作函数

实例操作函数主要是针对创建的实例对象的一系列操作函数。

1.针对整个对象进行操作的函数
// 返回指定对象的一份拷贝
id object_copy ( id obj, size_t size );

// 释放指定对象占用的内存
id object_dispose ( id obj );

以上两个函数不能在ARC下使用。

2.针对对象实例变量进行操作的函数
// 修改类实例的实例变量的值
Ivar object_setInstanceVariable ( id obj, const char *name, void *value );

// 获取对象实例变量的值
Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue );

// 返回指向给定对象分配的任何额外字节的指针
void * object_getIndexedIvars ( id obj );

// 返回对象中实例变量的值
id object_getIvar ( id obj, Ivar ivar );

// 设置对象中实例变量的值
void object_setIvar ( id obj, Ivar ivar, id value );

如果实例变量的Ivar已经知道,那么调用object_getIvar会比object_getInstanceVariable函数快,相同情况下,object_setIvar也比object_setInstanceVariable快。

3.针对对象的类进行操作的函数
// 返回给定对象的类名
const char * object_getClassName ( id obj );

// 返回对象的类
Class object_getClass ( id obj );

// 设置对象的类
Class object_setClass ( id obj, Class cls );

五、获取类定义

OC动态运行库会自动注册我们代码中定义的所有的类。我们也可以在运行时创建类定义并使用objc_addClass函数来注册它们。runtime提供了一系列函数来获取类定义相关的信息,这些函数主要包括:

// 获取已注册的类定义的列表
int objc_getClassList ( Class *buffer, int bufferCount );

// 创建并返回一个指向所有已注册类的指针列表
Class * objc_copyClassList ( unsigned int *outCount );

// 返回指定类的类定义
Class objc_lookUpClass ( const char *name );
Class objc_getClass ( const char *name );
Class objc_getRequiredClass ( const char *name );

// 返回指定类的元类
Class objc_getMetaClass ( const char *name );
上一篇 下一篇

猜你喜欢

热点阅读