iOS底层原理 :OC对象的本质

这是我参与更文挑战的第3天,活动详情查看: 更文挑战

前言

Objective-C中对象是什么,是怎么实现的呢?众所周知,Objective-C是有C++演变而来的,那么不难猜测,实现的方法就只有两种C++中的类class或者结构体。接下来就使用clang来看看Objective-C中类怎么实现的。

OC对象的实现

为了更加方便,我新建了一个macos的命令行项目,顺便把我的main.m改成了main.mm,可以对比C++的类和Objective-C的类。

main.mm文件代码:

#import <Foundation/Foundation.h>

class LQPeople
{
    private:
    int a;
    protected:
    int b`;`
    public:
    int c;
};

@interface LQTest : NSObject
@property(nonatomic,copy)NSString *name;
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}
复制代码

使用clang命令

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

得到我们的mian.cpp文件,打开cpp文件搜索我们的LQTest

image.png
发现LQTest的确是两种猜想中的一种实现的,是由结构体实现的,不过隐隐约约感觉有些不对劲,我们的name去哪了?原来是LQTest没有被实现…

再来一次…

main.mm文件代码:

#import <Foundation/Foundation.h>

class LQPeople
{
    private:
    int a;
    protected:
    int b;
    public:
    int c;
};

@interface LQTest : NSObject
@property(nonatomic,copy)NSString *name;
@end
@implementation LQTest
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}
复制代码

使用clang命令

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

得到我们的mian.cpp文件,然后打开cpp查看。

image.png
这下就对了,我们的对象LQTest变成了结构体,得出结论OC的对象就是struct实现的

小结

  1. OC的对象就是struct实现的。
  2. OC的对象没有实现,只会生成一个类标识。

OC对象的本质

我们已经知道OC的对象是由结构体实现的,接下来继续探索,对象具体是怎么实现的?对象的本质是什么?属性会变成什么?方法又会变成什么?

属性

先上代码:


@interface LQTest : NSObject
@property(nonatomic,assign)int i;
@property(nonatomic,assign)char c;
@property(nonatomic,assign)bool b;
@property(nonatomic,strong)NSString *name;
@property(nonatomic,strong)NSData *data;
@property(nonatomic,strong)NSDate *date;
@end
@implementation LQTest
-(void)test {
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}
复制代码

使用clang命令

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

得到我们的mian.cpp文件,然后打开查看。

image.png

LQTest第一个是父类,NSObject_IMPLNSObject_IMPL一个Class类型的isaClass是一个objc_class类型的指针,当我想继续查找objc_class时却只找到了一个定义,具体实现应该不在这里,这个暂时不深究。这个isa应该就是我们常用地self了。

struct NSObject_IMPL {
	Class isa;
};

typedef struct objc_class *Class;
复制代码

接下来查看定义的基础成员变量还保持着charboolint。但是NSStringNSDataNSDate却不是我们OC中的NSStringNSDataNSDate了。都是结构体objc_object了。objc_object这里也只看到一里面一个Class类型的isa指针,这里就不深究了。

// @class NSString;
#ifndef _REWRITER_typedef_NSString
#define _REWRITER_typedef_NSString
typedef struct objc_object NSString;
typedef struct {} _objc_exc_NSString;
#endif

// @class NSData;
#ifndef _REWRITER_typedef_NSData
#define _REWRITER_typedef_NSData
typedef struct objc_object NSData;
typedef struct {} _objc_exc_NSData;
#endif

// @class NSDate;
#ifndef _REWRITER_typedef_NSDate
#define _REWRITER_typedef_NSDate
typedef struct objc_object NSDate;
typedef struct {} _objc_exc_NSDate;
#endif

struct objc_object {
    Class _Nonnull isa __attribute__((deprecated));
};
复制代码

方法

在结构体这块定义里面我们并没有看到我们定义的方法。定义的方法去哪里了?我们继续查找。

static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[25];
} _OBJC_$_INSTANCE_METHODS_LQTest __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	25,
	{{(struct objc_selector *)"test", "v16@0:8", (void *)_I_LQTest_test},
	{(struct objc_selector *)"i", "i16@0:8", (void *)_I_LQTest_i},
	{(struct objc_selector *)"setI:", "v20@0:8i16", (void *)_I_LQTest_setI_},
	{(struct objc_selector *)"c", "c16@0:8", (void *)_I_LQTest_c},
	{(struct objc_selector *)"setC:", "v20@0:8c16", (void *)_I_LQTest_setC_},
	{(struct objc_selector *)"b", "B16@0:8", (void *)_I_LQTest_b},
	{(struct objc_selector *)"setB:", "v20@0:8B16", (void *)_I_LQTest_setB_},
	{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LQTest_name},
	{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LQTest_setName_},
	{(struct objc_selector *)"data", "@16@0:8", (void *)_I_LQTest_data},
	{(struct objc_selector *)"setData:", "v24@0:8@16", (void *)_I_LQTest_setData_},
	{(struct objc_selector *)"date", "@16@0:8", (void *)_I_LQTest_date},
	{(struct objc_selector *)"setDate:", "v24@0:8@16", (void *)_I_LQTest_setDate_},
	{(struct objc_selector *)"i", "i16@0:8", (void *)_I_LQTest_i},
	{(struct objc_selector *)"setI:", "v20@0:8i16", (void *)_I_LQTest_setI_},
	{(struct objc_selector *)"c", "c16@0:8", (void *)_I_LQTest_c},
	{(struct objc_selector *)"setC:", "v20@0:8c16", (void *)_I_LQTest_setC_},
	{(struct objc_selector *)"b", "B16@0:8", (void *)_I_LQTest_b},
	{(struct objc_selector *)"setB:", "v20@0:8B16", (void *)_I_LQTest_setB_},
	{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LQTest_name},
	{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LQTest_setName_},
	{(struct objc_selector *)"data", "@16@0:8", (void *)_I_LQTest_data},
	{(struct objc_selector *)"setData:", "v24@0:8@16", (void *)_I_LQTest_setData_},
	{(struct objc_selector *)"date", "@16@0:8", (void *)_I_LQTest_date},
	{(struct objc_selector *)"setDate:", "v24@0:8@16", (void *)_I_LQTest_setDate_}}
};

// @implementation LQTest

static void _I_LQTest_test(LQTest * self, SEL _cmd) {
}

static int _I_LQTest_i(LQTest * self, SEL _cmd) { return (*(int *)((char *)self + OBJC_IVAR_$_LQTest$_i)); }
static void _I_LQTest_setI_(LQTest * self, SEL _cmd, int i) { (*(int *)((char *)self + OBJC_IVAR_$_LQTest$_i)) = i; }

static char _I_LQTest_c(LQTest * self, SEL _cmd) { return (*(char *)((char *)self + OBJC_IVAR_$_LQTest$_c)); }
static void _I_LQTest_setC_(LQTest * self, SEL _cmd, char c) { (*(char *)((char *)self + OBJC_IVAR_$_LQTest$_c)) = c; }

static bool _I_LQTest_b(LQTest * self, SEL _cmd) { return (*(bool *)((char *)self + OBJC_IVAR_$_LQTest$_b)); }
static void _I_LQTest_setB_(LQTest * self, SEL _cmd, bool b) { (*(bool *)((char *)self + OBJC_IVAR_$_LQTest$_b)) = b; }

static NSString * _I_LQTest_name(LQTest * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LQTest$_name)); }
static void _I_LQTest_setName_(LQTest * self, SEL _cmd, NSString *name) { (*(NSString **)((char *)self + OBJC_IVAR_$_LQTest$_name)) = name; }

static NSData * _I_LQTest_data(LQTest * self, SEL _cmd) { return (*(NSData **)((char *)self + OBJC_IVAR_$_LQTest$_data)); }
static void _I_LQTest_setData_(LQTest * self, SEL _cmd, NSData *data) { (*(NSData **)((char *)self + OBJC_IVAR_$_LQTest$_data)) = data; }

static NSDate * _I_LQTest_date(LQTest * self, SEL _cmd) { return (*(NSDate **)((char *)self + OBJC_IVAR_$_LQTest$_date)); }
static void _I_LQTest_setDate_(LQTest * self, SEL _cmd, NSDate *date) { (*(NSDate **)((char *)self + OBJC_IVAR_$_LQTest$_date)) = date; }
// @end
复制代码

我们可以看到我们的方法已经全部变成了c++的静态函数了,有一个_method_list_t来映射他们。

总结

  1. OC对象是由结构体实现的。
  2. OC对象的基础成员变量还保留为基础成员变量,但是类类型的成员变量都转变成结构体指针了。
  3. OC对象的类方法都是有静态函数实现,再用method_list映射。
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享