系统提供的有不同的内存管理方案,大致有如下三种:
- TaggedPointer (对于一些小对象,比如说NSNumber,NSString等采用此种方案)
- NONPOINTER_ISA (64位架构下iOS应用程序)
- 散列表 (散列表为复杂的数据结构,包含了引用计数表和弱引用表)
TaggedPointer
以下2段代码能发生什么事?有什么区别?
- (void)setName:(NSString *)name
{
if (_name != name) {
[_name release];
_name = [name retain];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 1000; i++) {
dispatch_async(queue, ^{
// 加锁
self.name = [NSString stringWithFormat:@"abcdefghijk"];
// 解锁
});
}
// dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//
// for (int i = 0; i < 1000; i++) {
// dispatch_async(queue, ^{
// self.name = [NSString stringWithFormat:@"abc"];
// });
// }
// NSString *str1 = [NSString stringWithFormat:@"abcdefghijk"];
// NSLog(@"%p", str1);
}
当赋值为abc的时候,会当成是一个TaggedPointer,直接修改成员变量,故不会报错,当赋值为abc的时候,会当成是一个NONPOINTER_ISA,多条线程同时操作一个属性,在nonatomic下面会有多线程的问题,如果要解决这个问题,可以把属性改成atomic。(就是判断isTaggedPointer,在mac平台,指针的最低有效位是1,就是TaggedPointer;在ios平台,指针的最高有效位是1,就是TaggedPointer)
NONPOINTER_ISA
indexed:0:这里存的是当前对象的类对象地址;1:除地址外还有内存管理方面数据
has_assoc:当前对象是否有关联对象
has_cxx_dtor:当前对象是否有使用到C++方面的内容
shiftcls:当前对象类对象的指针地址
wealy_referenced:是否有相应的弱引用指针
deallocting:是否正在进行dealloc操作
has_sidetable_rc:是否内存管理数据过大,过大或用sidetable储存
extra_rc:储存内存管理相关
散列表方式
alloc
用alloc的时候并没有引用计数+1,通过一系列调用,最终调用c函数的calloc;
dealloc:
可以看到执行过程比较简单,_objc_rootDealloc -> rootDealloc -> objc_object::rootDealloc,
在objc_object::rootDealloc中判断对象的几个条件:
- isa.nonpointer,32位系统和64位系统isa的结构不同,为0表示isa直接指向对象的class,为1表示isa不是直接指向class
- 是否有注册weak引用表
- 是否有association关联属性
- 是否有c++析构
- 是否有引用计数表
在object_dispose中调用objc_destructInstance主要做了以下操作:
- object_cxxDestruct,这里主要是用于释放对象的实例变量
- _object_remove_assocations,移除掉所有关联属性,即通过objc_setAssociatedObject添加的关联属性
- clearDeallocating,先清空weak变量表且将所有weak引用指向nil,然后清空引用计数表。
最后通过free函数清除对象内存。