对象的本质.md

338 阅读16分钟

1. 将Objective-C转换为C\C++代码。

把 main.m 文件转换为 C\C++代码。用编译器llvm前端转换。

clang -rewrite-objc main.m -o main.cpp

但是以上命令,没有考虑到平台和架构。不同平台转换的代码是不一样的。

以下是转换成iOS平台的arm64架构上支持的C\C++.

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc oc源文件 -o 输出的cpp文件

2. NSObject的本质

xcode里点击代码NSObject源码如下:

@interface NSObject <NSObject> {
    Class isa;

}

上面得到的main-arm64.cpp里搜索 NSObject_IMPL(NSObject implementation)得到,NSObject的底层实现:

struct NSObject_IMPL {
    Class isa;
};

得出:

OC类底层实现就是一个结构体。
  • 知识点Class isaClass是什么。点进去:
typedef struct objc_class *Class;

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

可见Class就是一个指向objc_class的指针。 见 runtime(二)

  • 结构体所占内存
struct NSObject_IMPL {
    Class isa;
};

结构体的内存大小,就是他的成员变量的内存大小。这个例子里,isa是个指针,所以占8个字节。所以NSObject_IMPL也占八个字节。结构体的地址就是isa的地址(是其第一个成员变量的地址)。

3. NSObject所占内空间

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

[NSObject alloc]就是为NSObject分配内存空间,因为NSObject底层就是一个结构体,根据上面分析这个结构体占8个字节内存空间。所以暂时认为[NSObject alloc]系统分配了8个字节内存空间。

但是并非如此。

代码:

#import <objc/runtime.h>
#import <malloc/malloc.h>
        NSObject *obj =[[NSObject alloc]init];
        
        //获取NSObject实例对象的成员变量所占用的大小  >> 8个字节
        NSLog(@"%zd",class_getInstanceSize([NSObject class]));
        
        //获取obj指针所指向的内存大小 >> 16
        NSLog(@"%ld",malloc_size((__bridge void *)obj)); //malloc_size传进去的参数需要是C类型的指针。所以要做个桥接

  • class_getInstanceSize传进入的是Class对象。每一个类都有一个Class类型的间接属性,因为不是直接属性,所以不能用点语法获取。(后面会深入讲解)。

  • class_getInstanceSize返回的实际不是类的实例化对象所占的内存大小,而是实例对象的成员变量所占用的大小(见下面源码分析)

  • malloc_size是C函数。传进去的参数需要是C类型的指针。所以要做个桥接。

  • class_getInstanceSize属于<objc/runtime.h>框架下的方法,malloc_size属于<malloc/malloc.h>框架下的方法。

  • 系统为NSObject对象分配了16个字节的存储空间,其中前8个字节存放成员变量isa。

  • 结构体内存大小为其最大成员内存的倍数。

4.源码分析

苹果源码开源网址:opensource.apple.com/tarballs

寻找objc4.并下载数字对大的版本(最新)。

  1. class_getInstanceSize

源码工程里搜索class_getInstanceSize,最终得到

    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }
static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

WORD_MASK == 7, &~ WORD_MASK 是后三位清零操作,得出是8的倍数。
(x + WORD_MASK) & ~WORD_MASK 向上取8的倍数,也就是 7取的是8,11取的是16。

根据说明可以看出返回的是类的成员变量对齐后的内存大小。

  1. alloc源码

源码工程搜索allocWithZone. 实际调用的是_objc_rootAllocWithZone >> class_createInstance >> _class_createInstanceFromZone :

static __attribute__((always_inline)) 
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil)
{
    if (!cls) return nil;

    assert(cls->isRealized());

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();

    size_t size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (!zone  &&  fast) {
        obj = (id)calloc(1, size);
        if (!obj) return nil;
        obj->initInstanceIsa(cls, hasCxxDtor);
    } 
    else {
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;

        // Use raw pointer isa on the assumption that they might be 
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (cxxConstruct && hasCxxCtor) {
        obj = _objc_constructOrFree(obj, cls);
    }

    return obj;
}

可以看到最终调用C语言的alloc:obj = (id)calloc(1, size);传进入一个size是size_t size = cls->instanceSize(extraBytes); .

点进去发现:

    size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
    }

可以知道,CF框架规定对象所占内存空间大小不足16的时候,赋值为16.

5. 面试题

  • 一个NSObject对象占用多少内存

1.系统分配了16个字节给NSObject对象(通过malloc_size函数获得)。 2.但NSObject对象内部只使用了8个字节(64bit环境下)空间(可通过class_getInstanceSize函数获得)。

6.窥探NSObject内存

代码:

NSObject *obj =[[NSObject alloc]init];
        
//获取NSObject实例对象的成员变量所占用的大小  >> 8个字节
NSLog(@"%zd",class_getInstanceSize([NSObject class]));
        
//获取obj指针所指向的内存大小 >> 16
NSLog(@"%ld",malloc_size((__bridge void *)obj)); //malloc_size传进去的参数需要是C类型的指针。所以要做个桥接


在第一个NSObject处打断点,可以看出obj的内存地址为: 0x1007696e0

6.1 用xcode工具

【Debug --> Debug Workflow --> View memory】在 Address框里输入: 0x1007696e0

可以看出NSObject内存大小为16个字节【41 c1 40 9D FF FF 1D 00 00 00 00 00 00 00 00 00】(即20之前的都是),但后8个字节都是00 还没有使用。

  • 1B=1 byte = 8 bit

一个字节是由8个二进制位,2个16进制位组成。

十六进制数字与二进制数字的对应关系如下:0000 -> 0、0001 -> 1、0010 -> 2、0011 -> 3、0100 -> 4、0101 -> 5、0110 -> 6、0111 -> 7、1000 -> 8、1001 -> 9、1010 -> A、1011 -> B、1100 -> C、1101 -> D、1110 -> E、1111 -> F。

因此,==1个16进制数对应4个二进制数位==,2个16进制数位对应8个二进制数位,及1个字节。

6.2 用 lldb指令

  • LLDB指令
  1. print、p:打印 po: 打印对象
  2. 读取内存:
    • memory read/数量格式字节数 内存地址(即开始读的位置)
    • x/数量格式字节数 内存地址(即开始读的位置)
    • 格式: x--16进制,f--浮点,d--十进制
    • 字节数:b -- byte字节数, h -- half word 2字节, w -- word 4字节, g -- giant word 8字节
  3. 修改内存中的值: memory write 内存地址 数值(16进制)
  • 本例子
(lldb) x 0x1007696e0
0x1007696e0: 41 c1 40 9d ff ff 1d 00 00 00 00 00 00 00 00 00  A.@.............
0x1007696f0: 20 f7 86 9d ff 7f 00 00 00 00 00 00 00 00 00 00   ...............
(lldb) x/4xw 0x1007696e0
0x1007696e0: 0x9d40c141 0x001dffff 0x00000000 0x00000000
(lldb) x/4dw 0x1007696e0
0x1007696e0: -1656700607
0x1007696e4: 1966079
0x1007696e8: 0
0x1007696ec: 0
(lldb) memory write 0x1007696eb f4
(lldb) x 0x1007696e0
0x1007696e0: 41 c1 40 9d ff ff 1d 00 00 00 00 f4 00 00 00 00  A.@.............
0x1007696f0: 20 f7 86 9d ff 7f 00 00 00 00 00 00 00 00 00 00   ...............
(lldb) 


  1. x/4xw 0x1007696e0 结果: 0x1007696e0: 0x9d40c141 0x001dffff 0x00000000 0x00000000

为什么头四个字节是0x9d40c141,这涉及到数据存储的大小端问题,iOS是小端模式,这里是小端模式

0x1007696e0: -1656700607
0x1007696e4: 1966079
0x1007696e8: 0
0x1007696ec: 0

十进制表示为什么是这样? 0x9d40c141的十进制就是-1656700607,0x001dffff的十进制就是1966079

  1. memory write 0x1007696eb f4 把 f4写进 地址为0x1007696eb中,这块地址是0x1007696e0往后数12个字节。

7. Student的本质及内存布局


#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>

//struct NSObject_IMPL {
//    Class isa;
//};
//

struct Student_IMPL {
    Class isa;
    int _no;
    int _age;
};

<!--struct Student_IMPL {-->
<!--	struct NSObject_IMPL NSObject_IVARS;-->
<!--	int _no;-->
<!--	int _age;-->
<!--};-->


@interface Student : NSObject
{
    @public
    int _no;
    int _age;
}
@end

@implementation Student

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Student *stu = [[Student alloc] init];
        stu->_no = 4;
        stu->_age = 5;
        
        NSLog(@"%zd", class_getInstanceSize([Student class]));
        NSLog(@"%zd", malloc_size((__bridge const void *)stu));
        
        
        struct Student_IMPL *stuImpl = (__bridge struct Student_IMPL *)stu;
        NSLog(@"no is %d, age is %d", stuImpl->_no, stuImpl->_age);
    }
    return 0;
}

  • Student本质是为Student_IMPL结构体,其中第一个成员变量是为NSObject_IMPL的结构体。

  • Student占16个字节内存空间。其中成员变量NSObject_IVARS占8个字节。NSObject_IVARS的地址就是Student的地址(因为Student本质是结构体,结构体的地址是第一个成员变量的地址)

  • 因为Student本质是结构体,所以可以将stu强行转换为struct Student_IMPL *结构。测面应证了Student本质是结构体。

Printing description of stu:
<Student: 0x1007028b0>
(lldb) x 0x1007028b0
0x1007028b0: c9 11 00 00 01 80 1d 00 04 00 00 00 05 00 00 00  ................
0x1007028c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
(lldb) memory write 0x1007028b8 f0
(lldb) 

改变_no的值。 memory write 0x1007028b8 f0,_no这块内存地址内值改为f0,改后_no = 240.

9.更复杂的继承机构

代码:


@interface Person : NSObject
{
    // @public
    int _age;
}

@end

@implementation Person

@end

 //Student
@interface Student : Person
{
    int _no;
}
@end

@implementation Student


@end



int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Student *stu = [[Student alloc] init];
        NSLog(@"stu - %zd", class_getInstanceSize([Student class]));
        NSLog(@"stu - %zd", malloc_size((__bridge const void *)stu));

        Person *person = [[Person alloc] init];
        NSLog(@"person - %zd", class_getInstanceSize([Person class]));
        NSLog(@"person - %zd", malloc_size((__bridge const void *)person));
        
        
    }
    return 0;
}


先说结论打印的结果是:

2019-12-24 14:28:30.281796+0800 interview01_对象的本质[12437:1725586] stu - 16
2019-12-24 14:28:30.283184+0800 interview01_对象的本质[12437:1725586] stu - 16
2019-12-24 14:28:30.283266+0800 interview01_对象的本质[12437:1725586] person - 16
2019-12-24 14:28:30.283331+0800 interview01_对象的本质[12437:1725586] person - 16
Program ended with exit code: 0


分析:

转换为C/C++代码:


struct Person_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	int _age;
};

struct Student_IMPL {
	struct Person_IMPL Person_IVARS;
	int _no;
};


  • 题外话:Person的成员变量,_age。是public,protect还是private.对结果没有影响。底层代码也是一样的。即如果_age是private的,Student也会继承这个成员变量(转换成C/C++代码,可知)。

    • public修饰的对象 可以在所有类中使用。

    • private 关键字修饰的 只能在本类内部使用。

    • protected 修饰的对象只能被本类和子类内部使用。(默认的)

  • class_getInstanceSize 得到的是,实例对象的成员变量的内存大小。

    • Person中的NSObject_IMPL占8个字节,_age占4个字节。那么没什么Person的class_getInstanceSize,不是12呢。是因为内存对其的缘故。(即结构体内存大小,是内存最大的成员变量的倍数。为了提高CPU访问速度)。所以 class_getInstanceSize([Person class])是16.

    • Student的Person_IMPL占16个字节,_no占4个字节,那么根据上面应该class_getInstanceSize返回的是32个字节? 因为Student的Person_IMPL的16个字节并没有用完,Person_IMPL分配了16个字节,只用了12个。编译器会优化空间。所以Person_IVARS占了12个字节,剩下的4个字节被_no用了。所以class_getInstanceSize([Student class])是12+4 = 16.(student相当于有三个成员变量,isa、_age、_no,所以内存对齐是8 的倍数).

    • 由上面源码可知class_getInstanceSize,内存对齐是8的倍数((x + WORD_MASK) & ~WORD_MASK)。

  • malloc_size 得到的是,类的实例对象的内存大小。而且需要考虑

    1. 内存对齐。

    2. 最小内存大小16.

    3. Student同上面一样,Person_IMPL的16个字节并没有用完,Person_IMPL分配了16个字节,只用了12个。编译器会优化空间。所以剩下的4个字节被_no用了。

10.属性和方法

代码:


// Person
@interface Person : NSObject
{
    @public
    int _age;
}
@property (nonatomic, assign) int height;
@end

@implementation Person

@end



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

        Person *person = [[Person alloc] init];
        [person setHeight:10];
        [person height];
        person->_age = 20;
        
        Person *person1 = [[Person alloc] init];
        person1->_age = 10;
        
        
        Person *person2 = [[Person alloc] init];
        person2->_age = 30;

    }
    return 0;
}

  • 我们知道,属性会生成成员变量和setter、getter方法。但是所有的方法不存在实例对象中。(后面会涉及到底放在哪里)。因为成员变量每个实例对象都有可能不同,但是方法都是一样的。所以只存一份就行。所以上例,person,person1,person2不存方法。

11. libmalloc的源码

代码:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>

struct NSObject_IMPL {
    Class isa;
};

struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _age;
};

struct Student_IMPL {
    struct Person_IMPL Person_IVARS; //16
    int _no; // 4
    int _height; // 4
};

@interface Person : NSObject
{
    
    int _age;
}

@end

@implementation Person

@end

 //Student
@interface Student : Person
{
    @public
    int _no;
    int _height;
}
@end

@implementation Student


@end



int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        struct Student_IMPL stu_s;
        NSLog(@"%zd",sizeof(stu_s.Person_IVARS));
        
        Student *stu = [[Student alloc] init];
        stu->_no = 04;
        stu->_height = 05;
        NSLog(@"stu - %zd", class_getInstanceSize([Student class]));
        NSLog(@"stu - %zd", malloc_size((__bridge const void *)stu));

        Person *person = [[Person alloc] init];
        NSLog(@"person - %zd", class_getInstanceSize([Person class]));
        NSLog(@"person - %zd", malloc_size((__bridge const void *)person));
        
        
    }
    return 0;
}



结果为:

2019-12-25 09:10:28.776509+0800 interview01_对象的本质[16659:2742756] 16
2019-12-25 09:10:28.777503+0800 interview01_对象的本质[16659:2742756] stu - 24
2019-12-25 09:10:28.777596+0800 interview01_对象的本质[16659:2742756] stu - 32
2019-12-25 09:10:28.777666+0800 interview01_对象的本质[16659:2742756] person - 16
2019-12-25 09:10:28.777729+0800 interview01_对象的本质[16659:2742756] person - 16

结果分析:

Person没有争议。主要是Student有疑惑。

既然sizeof(stu_s.Person_IVARS)为16,那么class_getInstanceSize([Student class])应该为16+4+4,对齐后应该为32,为什么是24呢。

  • sizeof可以认为和class_getInstanceSize效果是一样的,传的是类型,sizeof(stu_s.Person_IVARS)相当于sizeof(struct Person_IMPL),计算的是结构体Person_IMPL的内存大小,确实是16.但是由于内存优化,Student的Person_IVARS用不了16,只用了12,另外4被_no用了。所以class_getInstanceSize([Student class])结果为 12 + 4 + 4,内存对齐为8的倍数(student有四个成员变量isa、_age、_no、_height,所以是8的倍数),所以是24.(可以用xcode-Debug-Debug Workflow - View Memory看)。

  • 细节:sizeof不是函数,编译的时候就确定了大小。

那么为什么malloc_size((__bridge const void *)stu)不是24呢。我们看源码 malloc 都做了什么。

  • 我们看[Student alloc]发生了什么 ---> 调了allocWithZone. 在苹果开源网站下载objc4.搜索.m文件里的allocWithZone.发现_objc_rootAllocWithZone ---> class_createInstance ---> _class_createInstanceFromZone

可以看到最终调用了,calloc(1,size).意思是开辟一块内存空间,大小为size.那么传进去的size是多大呢。size_t size = cls->instanceSize(extraBytes);, instanceSize点进去--->alignedInstanceSize + extraBytes,再退回到_objc_rootAllocWithZone的实现,发现 obj = class_createInstance(cls, 0);,extraBytes的值为0.

又:

    // Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

所以 mallocclass_getInstanceSize 内存大小应该是一样的啊,都是Class的成员变量的大小。为什么和结果不一致呢?

  • 我们再来看看malloc源码:

malloc源码在 libmalloc中。

下载源码: opensource.apple.com/tarballs. 寻找 libmalloc 库。

先找下下 malloc.h 发现 malloc.h 和 _malloc.h, 在 _malloc.h中发现了 calloc函数声明。所以去 malloc.c 中 寻找 calloc实现。

calloc --> malloc_zone_calloc. 到这里看不懂了,直接说结论。

iOS系统不会你要多少内存,就给你分配多少内存,比如你需要81字节,就给你分配81个字节。它分配的内存大小只能是 16的倍数,最大为256(准确说NANO这种内存分配方式最大为256,还有其他的内存分配方式。看下面代码).这样有利于CPU访问。

在libmalloc源码中,搜索 buck. 会发现下面一段代码:


#define NANO_MAX_SIZE			256 /* Buckets sized {16, 32, 48, ..., 256} */

这个就是内存桶的概念,只会分配桶里的这些内存大小。

所以在这个例子里,因为需要 24个字节,但是系统会分配32个字节。

总结:

  • sizeof和class_getinstanceSize效果是一样的。

sizeof不是函数,是运算符。比如 sizeof(int),编译的时候就会替换成4,而不是运行的时候计算。

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

sizeof(p);结果是8.因为p是指针,sizeof(p)结果是p这个指针变量占用多少空间,编译的时候这块代码会换成8.

  • class_getinstanceSize是创建一个实例对象,至少需要内存空间大小。

结构体内存对齐:分配的内存是最大成员变量的倍数。

  • malloc_size 创建一个实例对象,实际上分配了多少内存。

iOS平台内存对齐:实例对象分配的内存是16的倍数。

11.1 源码总结

  • class_getInstanceSize --> alignedInstanceSize --> word_align : 8的倍数

  • allocWithZone -> _objc_rootAllocWithZone >> class_createInstance >> _class_createInstanceFromZone:

{
	 size = cls->instanceSize(extraBytes);
         其中 extraBytes 传的是0;
	 其中 instanceSize  = alignedInstanceSize() + extraBytes; 且最小16
	 
}
  • malloc是根据内存桶分配内存空间,是16的倍数,最大256;

12.Linux内存对齐方式——glibc源码。

Linux操作系统包涵了Linux内核与其他自由软件项目中的GNU组件和软件,可以被称为GNU/Linux

GNU是一个计划,GNU操作系统是这个计划的主要目标。

Linux内存分配这块用的是GNU的组件。

我们去GNU官网下载内存管理这方便的库,即glibc.

然后 点击 【软件】,搜索 libc --> 点击Source ---> 点击 via ftp.

下载最新的,tar.gz 格式。然后拖入一个xcode工程中,用xcode打开。

搜索 #define MALLOC_ALIGNMENT

发现如下代码:


#ifndef _I386_MALLOC_ALIGNMENT_H
#define _I386_MALLOC_ALIGNMENT_H

#define MALLOC_ALIGNMENT 16

#endif /* !defined(_I386_MALLOC_ALIGNMENT_H) */

说明在 i386 架构下, 是16个内存空间.

其他架构:

_GENERIC_MALLOC_ALIGNMENT_H
#define _GENERIC_MALLOC_ALIGNMENT_H

/* MALLOC_ALIGNMENT is the minimum alignment for malloc'ed chunks.  It
   must be a power of two at least 2 * SIZE_SZ, even on machines for
   which smaller alignments would suffice. It may be defined as larger
   than this though. Note however that code and data structures are
   optimized for the case of 8-byte alignment.  */
#define MALLOC_ALIGNMENT (2 * SIZE_SZ < __alignof__ (long double) \
			  ? __alignof__ (long double) : 2 * SIZE_SZ)


#endif /* !defined(_GENERIC_MALLOC_ALIGNMENT_H) */


搜索 #define SIZE_SZ,发现其实是 size_t. 用 sizeof(size_t)发现是8.

__alignof__ (long double)打印下发现是16.

所以最终也是16.

13. OC的对象

Objective-C中的对象,简称OC对象,主要可以分为3种。

  • instance对象(实例对象)
  • class对象(类对象)
  • meta-class对象(元类对象)

14. instance对象

instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象

        // instance对象,实例对象
        NSObject *object1 = [[NSObject alloc] init];
        NSObject *object2 = [[NSObject alloc] init];

object1、object2是NSObject的instance对象(实例对象) 它们是不同的两个对象,分别占据着两块不同的内存

instance对象在内存中储存的信息包括:

  • isa指针。(isa的地址就是实例对象的地址)
  • 其他成员变量。

不储存方法信息。

15.class对象

类对象

        // instance对象,实例对象
        NSObject *object1 = [[NSObject alloc] init];
        NSObject *object2 = [[NSObject alloc] init];

        // class对象,类对象
        // class方法返回的一直是class对象,类对象
        Class objectClass1 = [object1 class];
        Class objectClass2 = [object2 class];
        Class objectClass3 = object_getClass(object1);
        Class objectClass4 = object_getClass(object2);
        Class objectClass5 = [NSObject class];
  • objectClass1 ~ objectClass5都是NSObject的class对象(类对象)

  • 它们是同一个对象。每个类在内存中有且只有一个class对象

  • class对象在内存中储存的信息主要包括:

    1.isa
    2.superclass
    3.属性信息
    4.对象方法。(减方法)
    5.协议信息
    6.成员变量信息:成员变量类型,成员变量名字等描述信息。

class 方法

  • 即有对象方法,又有类方法.
  • class方法返回的一直是class对象。[[NSObject class] class] 返回的还是class对象。

16.meta-class对象

meta中文翻译:“元的”。 meta-class 元类对象

        // meta-class对象,元类对象
        // 将类对象当做参数传入,获得元类对象
        Class objectMetaClass = object_getClass(objectClass5);
  • objectMetaClass是NSObject的meta-class对象(元类对象)

  • 每个类在内存中有且只有一个meta-class对象

  • object_getClass传入实例对象获得的是类对象,传入类对象获得的是元类对象。

  • BOOL ret = class_isMetaClass(objectMetaClass) 查看对象是否是meta-class对象。

meta-class对象和class对象的内存结构是一样的

但是用途不一样,在内存中存储的信息主要包括

  1. isa指针
  2. superclass指针
  3. 类的类方法(class method)

17. objc_getClass 与 object_getClass

这两个runtime方法。runtime源码在objc4库中。在苹果开源网站下载。

源码如下:

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}
Class objc_getClass(const char *aClassName)
{
    if (!aClassName) return Nil;

    // NO unconnected, YES class handler
    return look_up_class(aClassName, NO, YES);
}

1.Class objc_getClass(const char *aClassName) 1> 传入字符串类名 2> 返回对应的类对象

2.Class object_getClass(id obj)
1> 传入的obj可能是instance对象、class对象、meta-class对象
2> 返回值
a) 如果是instance对象,返回class对象
b) 如果是class对象,返回meta-class对象
c) 如果是meta-class对象,返回NSObject(基类)的meta-class对象

3.- (Class)class、+ (Class)class
1> 返回的就是类对象