对象的本质

了解对象的本质

我们先看下面的代码

@interface XDPerson : NSObject
{
    NSString *nickName; //成员变量
//    UILabel *label;   //实例变量 是特殊的成员变量 通过类实例出来的
}
@property (nonatomic,copy) NSString *name; //属性
@end

@implementation XDPerson

@end

clang -rewrite-objc main.m -o main.cpp 将文件编译成底层的.cpp文件。
我们可以在main.cpp的文件里面看到一些这样的代码

struct NSObject_IMPL {
    Class isa;
};
···
#ifndef _REWRITER_typedef_XDPerson
#define _REWRITER_typedef_XDPerson
typedef struct objc_object XDPerson;
typedef struct {} _objc_exc_XDPerson;
#endif

extern "C" unsigned long OBJC_IVAR_$_XDPerson$_name;
struct XDPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *nickName;
    NSString *_name;
};
// @property (nonatomic,copy) NSString *name;
/* @end */

// @implementation XDPerson
static NSString * _I_XDPerson_name(XDPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_XDPerson$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_XDPerson_setName_(XDPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct XDPerson, _name), (id)name, 0, 1); }
// @end

看XDPerson_IMPL结构体内有三个成员变量

  1. isa 来自父类继承;
  2. nickName;
  3. _name;

对于属性name在底层系统给我们编译了三样东西:

  1. 成员变量_name;
  2. getter方法 ,static NSString * _I_XDPerson_name(XDPerson * self, SEL _cmd)看到了默认带的两个参数,cls,SEL;
  3. setter方法,static void I_XDPerson_setName(XDPerson * self, SEL _cmd, NSString *name) 看到了默认带的两个参数+第三个参数,cls,SEL,name。这里setter方法里面的objc_setProperty是比较重要的。
    而我们的成员变量依旧只是一个成员变量。
    有此我们可以推出一个结论:
    1. 对象的本质在底层就是一个结构体XDPerson_IMPL;
    2. 属性与成员变量之间的区别,属性是又 成员变量 +getter方法+setter方法;

看.cpp文件里面method_list_t

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[2];
} _OBJC_$_INSTANCE_METHODS_XDPerson __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    2,
    {{(struct objc_selector *)"name", "@16@0:8", (void *)_I_XDPerson_name},
    {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_XDPerson_setName_}}
};

{{(struct objc_selector *)"name", "@16@0:8", (void *)_I_XDPerson_name},
{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)I_XDPerson_setName}}
这个地方解释一下SEL->selector,Type->方法前面,Imp->方法实现。
其中对方法签名做一下解释:
@ 返回类型 id ,16返回的长度,
@ 参数类型id,0-7,
: 参数类型SEL,8-15;

NSLog(@"char--%s",@encode(char));
    NSLog(@"short--%s",@encode(short));
    NSLog(@"int--%s",@encode(int));
    NSLog(@"long--%s",@encode(long));
     NSLog(@"long long--%s",@encode(long long));
    NSLog(@"unsigned char--%s",@encode(unsigned char));
    NSLog(@"unsigned short--%s",@encode(unsigned short));
    NSLog(@"unsigned int--%s",@encode(unsigned int));
    NSLog(@"unsigned long--%s",@encode(unsigned long));
    NSLog(@"float--%s",@encode(float));
    NSLog(@"BOOL--%s",@encode(BOOL));
    NSLog(@"void--%s",@encode(void));
    NSLog(@"char *--%s",@encode(char *));
    NSLog(@"id--%s",@encode(id));
    NSLog(@"Class--%s",@encode(Class));
    NSLog(@"SEL--%s",@encode(SEL));
    int array[] = {1,2};
    NSLog(@"int[]--%s",@encode(typeof(array)));
    typedef struct person{
        int age;
        NSString *name;
    }Person;
    NSLog(@"struct--%s",@encode(Person));
    
    typedef union teacher{
        char *a;
        BOOL b;
    }Teacher;
    NSLog(@"union--%s",@encode(Teacher));

2019-12-19 20:47:51.143558+0800 Object本质[2623:63103] char--c
2019-12-19 20:47:51.143887+0800 Object本质[2623:63103] short--s
2019-12-19 20:47:51.143906+0800 Object本质[2623:63103] int--i
2019-12-19 20:47:51.143966+0800 Object本质[2623:63103] long--q
2019-12-19 20:47:51.144035+0800 Object本质[2623:63103] long long--q
2019-12-19 20:47:51.144080+0800 Object本质[2623:63103] unsigned char--C
2019-12-19 20:47:51.144108+0800 Object本质[2623:63103] unsigned short--S
2019-12-19 20:47:51.144131+0800 Object本质[2623:63103] unsigned int--I
2019-12-19 20:47:51.144153+0800 Object本质[2623:63103] unsigned long--Q
2019-12-19 20:47:51.144173+0800 Object本质[2623:63103] float--f
2019-12-19 20:47:51.144193+0800 Object本质[2623:63103] BOOL--c
2019-12-19 20:47:51.144213+0800 Object本质[2623:63103] void--v
2019-12-19 20:47:51.144234+0800 Object本质[2623:63103] char *--*
2019-12-19 20:47:51.144256+0800 Object本质[2623:63103] id--@
2019-12-19 20:47:51.144277+0800 Object本质[2623:63103] Class--#
2019-12-19 20:47:51.144297+0800 Object本质[2623:63103] SEL--:
2019-12-19 20:47:51.144317+0800 Object本质[2623:63103] int[]--[2i]
2019-12-19 20:47:51.144337+0800 Object本质[2623:63103] struct--{person=i@}
2019-12-19 20:47:51.144359+0800 Object本质[2623:63103] union--(teacher=*c)

查看打印结果可以看到对应的typeEncode
当然我们可以从苹果官方文档可以看到TypeEncode或者Xcode 在@encode commod+shift+0 进入官方文档直接查看

ViewDidLoad编译

上面我们是在mac的情形下面编译出来的结构,现在我们在ViewDidLoad编译一下会出现什么呢?
首先提供编译指令:
xcrun -sdk iphonesimulator clang -rewrite-objc ViewController.m
xcrun -sdk iphoneos clang -rewrite-objc ViewController.m

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}
@end

查看cpp文件

#ifndef _REWRITER_typedef_ViewController
#define _REWRITER_typedef_ViewController
typedef struct objc_object ViewController;
typedef struct {} _objc_exc_ViewController;
#endif

struct ViewController_IMPL {
    struct UIViewController_IMPL UIViewController_IVARS;
};
/* @end */

// @interface ViewController ()

/* @end */

// @implementation ViewController

static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
}

struct UIViewController_IMPL {
    struct UIResponder_IMPL UIResponder_IVARS;
};
// @end

我们的类ViewController在底层也被编译成了 ViewController_IMPL 结构体 继承了父类UIVIewController的属性;
同时看_I_ViewController_viewDidLoad有默认的两个参数 self,_cmd。

 (objc_msgSendSuper)({self, class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));

精简一下代码,就可以看到[super ViewDidLoad],
其本质上就是给父类发送消息objc_msgSendSuper(self.super,"viewDidLoad");

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_$_INSTANCE_METHODS_ViewController __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"viewDidLoad", "v16@0:8", (void *)_I_ViewController_viewDidLoad}}
};

同样的在_method_list_t中也可以看到SEL,type,imp。

对象与类在内存中的数量

XDPerson *p1 = [XDPerson alloc];
    XDPerson *p2 = [XDPerson alloc];
    XDPerson *p3 = [XDPerson alloc];
    NSLog(@"p1-%p",p1);
    NSLog(@"p2-%p",p2);
    NSLog(@"p3-%p",p3);
    
    Class c1 = [XDPerson class];
    Class c2 = [XDPerson alloc].class;
    Class c3 = object_getClass([XDPerson alloc]);
     NSLog(@"c1-%p",c1);
     NSLog(@"c2-%p",c2);
     NSLog(@"c3-%p",c3);

2019-12-19 21:42:33.901531+0800 alloc探索[3108:105948] p1-0x600000639da0
2019-12-19 21:42:33.901750+0800 alloc探索[3108:105948] p2-0x60000063ae20
2019-12-19 21:42:33.901878+0800 alloc探索[3108:105948] p3-0x600000639620
2019-12-19 21:42:33.901996+0800 alloc探索[3108:105948] c1-0x10b8f30e8
2019-12-19 21:42:33.902107+0800 alloc探索[3108:105948] c2-0x10b8f30e8
2019-12-19 21:42:33.902205+0800 alloc探索[3108:105948] c3-0x10b8f30e8

从打印结果可以看出来,我们的对象有多个,我们的类只有一个。
说明对象alloc就是一个新的对象,占用一份新的内存。而类在系统内存中就只有一份。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343