OC 对象的创建

一个程序在运行的过程中,离不开对象的创建,那么对象究竟是怎么创建的呢?OC 作为一门高级语言,对象在底层又是怎么实现的,对象的本质又是什么呢?带着这些疑问,开启我们的探索旅程。

在开启我们的探索之旅之前,我们需要做一些准备工作。首先我们需要下载 objc源码,配置到工程中,以便于我们跟踪对象创建的过程。

对象的创建

创建对象的两种方法:

OC 中,我们一般有两种方法创建对象:

  • [[cls alloc] init]
  • new

通过 [[cls alloc] init] 创建对象

我们首先来看 [[cls alloc] init] 是怎么创建对象的:

+ (id)alloc {
    return _objc_rootAlloc(self);
}

// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
// Replaced by CF (throws an NSException)
+ (id)init {
    return (id)self;
}

- (id)init {
    return _objc_rootInit(self);
}

id
_objc_rootInit(id obj)
{
    // In practice, it will be hard to rely on this function.
    // Many classes do not properly chain -init calls.
    return obj;
}

从这几个方法中,可以看出:

  • 对象创建在 alloc 方法中

  • init 中只是简单的返回了已创建好的对象

    那么 init 方法存在的理由是什么呢?

    这个方法就是 工厂模式 的应用了。

    • alloc 方法一般都是系统准备好的用来创建对象的,作为用户(也就是各位程序员小哥哥小姐姐了)是接触不到的。

    • 但是作为用户,我们有时又必须在对象创建的时候做一些事情(如成员变量的初始化、赋值等),这个时候 init 方法就派上用场了。这个时候是不是对我们平时写的 init 方法有了更深的认识。

通过 new 创建对象

对象可以通过 new 方法来创建:

+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}

看到源码,是不是发现了什么:new = alloc + init

new 方法本质上是 allocinit 的结合体。

创建对象

归根结底,对象创建都是通过alloc方法来实现的,那么就以此为起点,跟踪对象创建的过成。

SMPerson *person = [SMPerson alloc];

我们在这行添加一个端点,然后进行 step into:

step-into.png

然后来到

id objc_alloc(Class cls)
{
    return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}

我们一步步跟踪,最后来到:

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

根据整个流程,我们可以画出对象创建的流程图:

<div style="text-align: center;"><img src="https://i.loli.net/2019/12/28/BEACOhb6Umjekui.png" width="50%"></div>

从流程图结合代码调试,对象创建的实质其实就是:

调试技巧

补充一些调试的小技巧 😉

方法跳转到声明

当我们调试到某个方法,cmd + spacejump to definition 时,只能看到方法声明而没有实现时:

  • 通过 step into

  • 借助控制台输出真正的方法实现

    debug-skill.png

当某个方法里代码很长时

我们可以将一些分支代码折叠:

Xcode->Perferences->Text Editing -> 勾选 Coding folding ribbon

然后在需要地方:

  • 折叠 option + cmd + ⬅️
  • 展开 option + cmd + ➡️

常用数据类型占用内存

data type ILP32 size ILP32 alignment ILP64 size ILP64 alignment
char 1 byte 1 byte 1 byte 1 byte
bool 1 byte 1 byte 1 byte 1 byte
short 2 byte 2 byte 2 byte 2 byte
int 4 byte 4 byte 4 byte 4 byte
long 4 byte 4 byte 8 byte 8 byte
long long 8 byte 4 byte 8 byte 8 byte
NSInteger 4 byte 4 byte 8 byte 8 byte
CF_index 8 byte 4 byte 8 byte 8 byte
pointer 4 byte 4 byte 8 byte 8 byte

OS X以及iOS中与硬件环境相关的预定义宏

宏定义 bits 架构
__i386__ 32 x86
__x86_64__ 64 x86
__arm__ 32 ARM
__arm64__ 64 ARM

__LP64__: 表示指针长度为64位,即地址长度以64位长度来表示。

lldb 命令

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

推荐阅读更多精彩内容