内存布局
保留区
- 预留给系统处理nil等
- **
0x00400000**开始
代码段
- 编译之后的代码
数据段
- 字符串变量
- 全局变量
- 静态变量
堆
- alloc
访问堆区内存时,一般是先通过对象读取到对象所在的栈区的指针地址,然后通过指针地址访问堆区
栈
- 局部变量
- 函数参数
内存从高到底分配 内存是连续的
内核区
- 系统用来进行内核处理操作的区域
具体内存布局👇
内存管理方案
ARC
MRC
Tagged Pointer
-
小对象是不会进行retain和release操作的 因此不用担心过度释放问题
-
专门用来
处理小对象,例如NSNumber、NSDate、小NSString等 -
NSTaggedPointerString类型,存在常量区 -
1.
NSTaggedPointerString:标签指针,是苹果在64位环境下对NSString、NSNumber等对象做的优化。对于NSString对象来说 -
- 当
字符串是由数字、英文字母组合且长度小于等于9时,会自动成为NSTaggedPointerString类型,存储在常量区 - 当有
中文或者其他特殊符号时,会直接成为__NSCFString类型,存储在堆区
- 当
-
2.
__NSCFString:是在运行时创建的NSString子类,创建后引用计数会加1,存储在堆上 -
3.
__NSCFConstantString:字符串常量,是一种编译时常量,retainCount值很大,对其操作,不会引起引用计数变化,存储在字符串常量区
从64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储
在没有使用Tagged Pointer之前, NSNumber等对象需要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber对象的地址值
使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中
当指针不够存储数据时,才会使用动态分配内存的方式来存储数据
objc_msgSend能识别Tagged Pointer,比如NSNumber的intValue方法,直接从指针提取数据,节省了以前的调用开销
如何判断一个指针是否为Tagged Pointer?
iOS平台,最高有效位是1(第64bit) 16进制转为2进制
isTaggerPointer
pointer & 1
Mac平台,最低有效位是1
DEMO
崩溃原因是多条线程对同一个对象进行释放,导致对象过度释放,所以才会崩溃。