OC底层源码/原理合集
建议先看下 IOS底层(三): alloc相关1.初探 alloc, init, new源码分析
之前有些可能不太理解, 为什么需要8字节存储
, 直接按本身字节数存不就好了吗? 例如内存条这样存放
存完之后读取, 假如第一个是4字节, 第二个是8字节, 第三个是8字节, 第四个是4字节...
当CPU开始读数据时候读完第一个4字节, 立马要变换读取长度8字节, 当读到第四个时候又要变化读取长度4字节读取, 这样每次都要做判断改变读取长度, 繁琐且会影响CPU速度。CPU意思: 大哥你别这样存了, 我难受!!!
因为通常存储指针特别多, 即8字节比较多, 就规定存储时候都是8字节为一段进行存储, 不足也为8, 以空间换取时间。
接下来, 我们验证下是否真的是按我们想象的一样8字节对齐
建立一个类, 并建立一些属性
@interface SATest : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;
@property (nonatomic, strong) NSString *hobby;
#import
#import
#import
#import "SATest.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
SATest *test = [[SATest alloc] init];
test.name = @"ShawnAlex";
test.age = 18;
test.height = 180;
test.hobby = @"女";
NSLog(@"%p", test);
}
return 0;
}
加个断点, 之后x
一下, 看下内存段
x:
以16进制打印对象地址空间
x test
先po一下前面指针, 确保的确读的是SATest
(前面第一个是栈顶指针起始位置)
然后我们8个一取 po
读一下, 因为ios是小端模式
(强制转换数据不需要调整字节内容
)地址要倒着读, 即从右往左
读, 那么我们取8位从右往左读一下, 可看到
看一下的确是能读取出来, 这也验证了的确是以8字节进行存储
其中有个问题? 第一个内存是什么?为什么读成这样子
SATest
继承于NSObject
, 而NSObject
创建时候有个默认的isa
@interface NSObject {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
这样读取是不是不方便, 那么我们x/4xg
先转换下, 再读取
x/4xg
意思是 以16进制4个片段一读取, 当然我们发现最后一个女
怎么没有读取出来呢?
x/5xg
一下可以看到, 读取出来了
附: 怎么看到全部内存段?
断点 → Debug Workflow
→ View Memory
输入 即可看到代码段
address 处读一下栈顶指针, 也可以看到内存段
我们接下来再看一个这个例子:
可看到, 自带isa8字节存储
, 而系统实际分配由于16字节对齐
, 所以开辟了16字节的内存空间
那么比如: 我带一些属性呢? 会怎么开辟内存空间?
可那看到计算内存空间大小这里, 开辟的是32
isa
: 8, 字符串name
: 8, 字符串nickname
: 8
8+8+8 = 24, 16字节对齐, 32返回, 所以这里size是32
其实有些时候有些疑问, 一会是8字节对齐? 一会是16字节对齐? 到底按照哪个对齐?
对于一个对象
, 对齐方式是8字节对齐, 具体可以看class_getInstanceSize
源码
class_getInstanceSize
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
#ifdef __LP64__
# define WORD_SHIFT 3UL
# define WORD_MASK 7UL
# define WORD_BITS 64
#else
# define WORD_SHIFT 2UL
# define WORD_MASK 3UL
# define WORD_BITS 32
#endif
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
苹果为了防止一切的容错, 采用的是16字节对齐
的内存, 主要是因为采用8字节对齐时, 两个对象的内存会紧挨着, 显得比较紧凑, 而16字节比较宽松, 利于苹果以后的扩展。
malloc_size
extern size_t malloc_size(const void *ptr);
malloc_size是采用16字节对齐
, 对象实际分配的内存大小必须是16的整数倍
#define SHIFT_NANO_QUANTUM 4
#define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM) // 16
static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
size_t k, slot_bytes;
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 quanta
slot_bytes = k << SHIFT_NANO_QUANTUM; // multiply by power of two quanta size
*pKey = k - 1; // Zero-based!
return slot_bytes;
}
算法原理:k + 15 >> 4 << 4 ,其中 右移4 + 左移4相当于将后4位抹零,跟 k/16 * 16一样 ,是16字节对齐算法,小于16就成0了