一.内存的五大区
栈区:编译器自动分配并释放,存放函数的参数值、局部变量、基本类型的变量或对象引用类型
堆区:由程序员分配和释放
全局区: 全局变量和静态变量是放在一块的
常量区: 常量、字符串
代码区
二.ARC的核心思想
自己生成的对象,自己持有.
非自己生成的对象,也可以持有.
自己持有的对象不再需要时,需要对其进行释放.
非自己持有的对象无法释放.
三. MRC的内存泄漏问题
- (void)setName:(NSString *)name
{
//如果_name==name,你先release再retain没有任何意义,还会导致崩溃
if(_name != name)
{
//因为已经重新设值新的name了,所以要把原来的_name引用计数-1
[_name release];
//同时要将新的name引用技术+1
_name = [name retain];
}
}
在dealloc()方法里面需要将_name置nil.
四.copy问题
浅拷贝是拷贝了指向对象的指针
深拷贝不但拷贝了对象的指针,还在系统中再分配一块内存,存放拷贝对象的内容
不管是NSString还是NSMutableString
1.copy都产生NSString类型,mutableCopy都产生NSMutableString类型.
2.不过NSSring的copy只是拷贝指针(浅拷贝)不可变对象进行copy操作都是浅拷贝
其他的都会产生新的对象.(深拷贝)不可变对象进行mutableCopy和可变对象进行copy或mutableCopy
copy.png
五.内存优化问题
apple使用taggedPointer技术来优化内存
taggedPointer:如果定义的常量内容较小,64bit的地址完全可以存下这个内容,那么会直接把内容存到64位的数据里面,也就是指针+内容.
arm64后,isa指针的33位存储真正的地址,其他的位用来存储引用计数等信息.
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
};
extra_rc:表示引用计数-1.如果19位不够存储,那么会使用has_sidetable_rc.
has_sidetable_rc:是否使用SideTable表
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
}
SideTable里面有个RefcountMap散列表,key为对象value为引用计数.
为什么每个对象都有一个SideTable呢?分离锁:增加读写效率
六.dealloc方法
什么时候调用dealloc()?
当一个对象调用release()方法的时候会检查引用计数,如果引用计数=0则会调用dealloc()方法.
dealloc()都做了什么?
//释放对象
- (void)dealloc {
//---
_objc_rootDealloc(self);
}
判断是否是taggedPointer对象,判断是否有弱引用等,如果都没有则直接释放该对象.
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
/*
*是否TaggedPointer,它并不是真正的指针
*是否有弱引用
*是否有关联
*是否有析构器
*是否引用计数过大
*/
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
//快速释放
free(this);
}
else {
//---一般执行这里
object_dispose((id)this);
}
}
id object_dispose(id obj)
{
if (!obj) return nil;
//---释放实例变量、移除关联属性、弱引用指向nil
objc_destructInstance(obj);
//释放自己
free(obj);
return nil;
}
移除关联属性,将弱引用置为nil
这些都是在运行时做的,不是编译的时候就做的..毕竟类似关联对象都是运行时的东西.
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
//---释放实例变量
if (cxx) object_cxxDestruct(obj);
//---移除关联属性
if (assoc) _object_remove_assocations(obj);
//---将弱引用置为nil
obj->clearDeallocating();
}
return obj;
}
所以说:arc是llvm和runtime协作的结果
七.autoreleasePool原理
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
命令行objc_rewrite可得:
1.autoreleasepool会自动在第一行加入autoreleasepoolobj = objc_autoreleasePoolPush()
2.在最后一行加入objc_autoreleasePoolPop(autoreleasepoolobj)
autoreleasepool是以栈为结点的双向链表结构.
八.其他
如果你写了strong,那么编译器会帮你retain.
如果你给一个对象用assign修饰,那么编译器不会帮你retain,可以就提前释放导致崩溃.
如果你给NSMutableArray写了个copy,那么内部会对NSMutableArray进行copy操作,会导致NSMutableArray变成NSArray.