人狠话不多,直接上干货。这是第一篇,之后还会持续更新,当作自己学习的笔记,也同时分享给大家,希望帮助更多人。

首先,我们来思考,下面这段代码的输出是否相同。答案很明显,p1、p2、p3是指向相同的对象,但是指针地址是不同的。那么问题来了,为啥呢。保留这个疑问,我们来探索alloc和init到底在底层到底做了什么。

- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.FFPerson *p1 = [FFPerson alloc];FFPerson *p2 = [p1 init];FFPerson *p3 = [p1 init];FFLog(@"p1 --- %@ - %p",p1,&p1);FFLog(@"p2 --- %@ - %p",p2,&p2);FFLog(@"p3 --- %@ - %p",p3,&p3);
}p1 --- <FFPerson: 0x2818cb7e0> - 0x16f501c68
p2 --- <FFPerson: 0x2818cb7e0> - 0x16f501c60
p3 --- <FFPerson: 0x2818cb7e0> - 0x16f501c58

我们在alloc这里打断点,这里要用真机测试,为了arm64架构。断点到这后,按住control这个调试按钮会变,点进去,好像要点两下,就会进入一段汇编,圈中的就是底层的实现入口了。

接下来我们就进入源码解析了,苹果这个源码是开源的,苹果开源库,从这里下载objc4的最新源码。直接全局搜索objc_alloc,我们发现会有很多,下面那张图展示为实现部分。

一步步找下去,_objc_rootAllocWithZone --> _class_createInstanceFromZone。这里大致内容就是创建对象,并且分配了大小,初始化isa。这里有个算法,实例化的对象的大小为8的倍数。

static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,int construct_flags = OBJECT_CONSTRUCT_NONE,bool cxxConstruct = true,size_t *outAllocatedSize = nil)
{ASSERT(cls->isRealized());// Read class's info bits all at once for performancebool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();bool hasCxxDtor = cls->hasCxxDtor();bool fast = cls->canAllocNonpointer();size_t size;// 获取大小size = cls->instanceSize(extraBytes);if (outAllocatedSize) *outAllocatedSize = size;id obj;if (zone) {obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);} else {
// 创建了对象,并分配了大小obj = (id)calloc(1, size);}if (slowpath(!obj)) {if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {return _objc_callBadAllocHandler(cls);}return nil;}
// 初始化isaif (!zone && fast) {obj->initInstanceIsa(cls, hasCxxDtor);} else {// Use raw pointer isa on the assumption that they might be// doing something weird with the zone or RR.obj->initIsa(cls);}if (fastpath(!hasCxxCtor)) {return obj;}construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;return object_cxxConstructFromClass(obj, cls, construct_flags);
}size_t instanceSize(size_t extraBytes) {
// size = 8 + 0size_t size = alignedInstanceSize() + extraBytes;if (size < 16) size = 16;return size;}
uint32_t alignedInstanceSize() const {
// 内存对齐,为了读取方便return word_align(unalignedInstanceSize());}
uint32_t unalignedInstanceSize() const {ASSERT(isRealized());
// 内部含有一个isa return data()->ro()->instanceSize;}
// 进行内存对齐,最后为8的倍数
static inline uint32_t word_align(uint32_t x) {// 7 + 8 = 15// 0000 1111// 1111 1000//&0000 1000// 0000 0111return (x + WORD_MASK) & ~WORD_MASK;
}

同理,我们在init那边打断点,发现进入的是objc_retain,这里我们发现,非taggedPointer的,直接引用计数加一,但是对象还是原来的对象,所以为什么p1、p2、p3指向的对象相同。

id
objc_retain(id obj)
{if (obj->isTaggedPointerOrNil()) return obj;return obj->retain();
}

根据源码分析,实例对象的大小会不等于实际分配的大小,就是因为内存对齐,详情可以看我源码的注释。接着我们来探索calloc底层到底做了什么。这里将移步libmalloc源码库了。

void *
calloc(size_t num_items, size_t size)
{return _malloc_zone_calloc(default_zone, num_items, size, MZ_POSIX);
}
static void *
_malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size,malloc_zone_options_t mzo)
{// 删除部分不需要的判断代码// 这里会跳转default_zone_callocptr = zone->calloc(zone, num_items, size);// 删除部分不需要的判断代码return ptr;
}static void *
default_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{zone = runtime_default_zone();// nano_callocreturn zone->calloc(zone, num_items, size);
}
static void *
nano_calloc(nanozone_t *nanozone, size_t num_items, size_t size)
{size_t total_bytes;// 删除部分不需要的判断代码if (total_bytes <= NANO_MAX_SIZE) {// 这里是核心void *p = _nano_malloc_check_clear(nanozone, total_bytes, 1);if (p) {return p;} else {/* FALLTHROUGH to helper zone */}}malloc_zone_t *zone = (malloc_zone_t *)(nanozone->helper_zone);return zone->calloc(zone, 1, total_bytes);
}
_nano_malloc_check_clear(nanozone_t *nanozone, size_t size, boolean_t cleared_requested)
{MALLOC_TRACE(TRACE_nano_malloc, (uintptr_t)nanozone, size, cleared_requested, 0);void *ptr;size_t slot_key;size_t slot_bytes = segregated_size_to_fit(nanozone, size, &slot_key); // Note slot_key is set here// 删除部分不需要的判断代码return ptr;
}static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{size_t k, slot_bytes;// NANO_REGIME_QUANTA_SIZE 为16
// SHIFT_NANO_QUANTUM 4
// 这里就是左移右移算法,最终为16的倍数if (0 == size) {size = NANO_REGIME_QUANTA_SIZE; // Historical behavior}k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quantaslot_bytes = k << SHIFT_NANO_QUANTUM;                           // multiply by power of two quanta size*pKey = k - 1;                                                  // Zero-based!return slot_bytes;
}

对象创建原理已经知道了,接下来就是对象的结构了。下一篇文章将会分析对象的本质,isa的结构,已经对象与isa的关系。

iOS 进阶之底层原理一OC对象原理alloc做了什么相关推荐

  1. 第1课-OC对象原理基础

    第1课-OC对象原理基础 [TOC] 在探索OC对象原理之前,我们首先需要了解以下知识点 1. lldb lldb是xcode自带的命令行调试工具. 我们可以通过: help:查看lldb常见命令 h ...

  2. 第2课-OC对象原理上-1

    第2课-OC对象原理上-1 [TOC] 1.1 alloc对象的指针地址和内存 首先我们看下面代码的执行 ZBPerson *p1 = [ZBPerson alloc]; ZBPerson *p2 = ...

  3. iOS底层原理总结 - OC对象的本质

    苹果官方文档 The Objective-C language defers as many decisions as it can from compile time and link time t ...

  4. iOS进阶之底层原理-isa与对象

    上一篇文章探究了对象的创建已经底层结构,这篇详细介绍isa.对象以及互相的关系. isa是什么 从源码分析,isa是个共用体,封装了类的信息. nonpointer:是否对isa指针开启指针优化,0为 ...

  5. iOS进阶之底层原理-block本质、block的签名、__block、如何避免循环引用

    面试的时候,经常会问到block,学完本篇文章,搞通底层block的实现,那么都不是问题了. block的源码是在libclosure中. 我们带着问题来解析源码: blcok的本质是什么 block ...

  6. iOS进阶之底层原理-线程与进程、gcd

    线程与进程 线程的定义 线程是进程的基本单位,一个进程的所有任务都在线程中执行 进程要想执行任务,必须的有线程,进程至少要有一条线程 程序启动默认会开启一条线程,也就是我们的主线程 进程的定义 进程是 ...

  7. iOS进阶之底层原理-weak实现原理

    基本上每一个面试都会问weak的实现原理,还有循环引用时候用到weak,今天我们就来研究下weak的实现原理到底是什么. weak入口 我们在这里打个断点,然后进入汇编调试. 这里就很明显看到了入口, ...

  8. iOS进阶之底层原理-应用程序加载(dyld加载流程、类与分类的加载)

    iOS应用程序的入口是main函数,那么main函数之前系统做了什么呢? 我们定义一个类方法load,打断点,查看栈进程,我们发现dyld做了很多事,接下来就来探究到底dyld做了什么. 什么是dyl ...

  9. iOS进阶之底层原理-消息机制

    消息发送的本质是objc_msgSend,至于为啥是这个,可以通过断点调试,这样就直接进入汇编,因为是汇编代码,熟悉常用指令即可,大部分根据注释走下去 objc_msgSend源码分析流程 这是一段汇 ...

最新文章

  1. (转)Ubuntu12.04上NFS Server安装使用过程
  2. python 类和对象 atm_Python 类和对象
  3. 用于显示本地通知的跨平台插件flutter_local_notifications
  4. linux应用开发:日志记录
  5. argparse模块_Argparse:一个具体案例教会你python命令行参数解析
  6. 树莓派安装MySQL数据库与卸载
  7. linux无盘工作站互不干扰,Linux环境下无盘工作站的架设和实现二
  8. FreeRTOS列表
  9. com.haodf.android,有坑!Android新版QQ获取packageInfo引发异常崩溃
  10. leetcode(300)—— Longest Increasing Subsequence(最长递增子序列)
  11. c246芯片组服务器主板,支持Xeon E-2100系列:ASRock 华擎 发布 C246M WS 主板
  12. 多媒体计算机特性,多媒体计算机的基本特性
  13. python中global和nonlocal用法的详细说明
  14. CAD修复块中心(com接口c#语言)
  15. 部署Apache Doris
  16. day07面向对象复习+课后练习
  17. 在线版音乐播放器APP(一)
  18. win10激活 错误代码0x80070424
  19. 加载图片出错时,加载其他图片
  20. found 2 critical severity vulnerabilities run `npm audit fix` to fix them, or `npm audit` for deta

热门文章

  1. css y轴溢出滚动条,x轴溢出显示
  2. 网站性能优化之yahoo军规
  3. 第三讲:WCF介绍(3)
  4. 【转】分享 97年世界编程大赛第一名写的程序
  5. 【算法系列】一道面试算法题
  6. ubuntu linux下解决“no java virtual machine was found after searching the following locations:”的方法
  7. 安装完Pycharm,启动时碰到“failed to load jvm dll“的解决方案
  8. Python错误“ ImportError:未命名模块”
  9. 使用CSS将文字长度限制为n行
  10. 在Python中以扩展名.txt查找目录中的所有文件