OC内存管理的一些研究
1.内存分配 静态区 栈区 堆区
- 静态区: 1.代码区 2.初始化区 (初始化的全局变量和静态变量)3.未初始化区(未初始化的全局变量和静态变量)
- 栈区:由编译器自动分配并释放,存放函数的参数值,局部变量等。栈是系统数据结构,线程之间栈控件是不共享的。优点是快速高效,缺点时有限制,数据不灵活。栈区地址从高到低分配;[先进后出]
- 堆区:比较灵活,动态分配和释放,堆中的对象都是以指针来访问的,指针从线程栈中来,但不独属于某个线程,堆也是对复杂的运行时处理的基础支持,还有就是ARC还是MRC、“谁分配谁释放”说的都是堆上对象的管理,堆区的地址是从低到高分配;
2.内存管理(堆区内存)
引用计数机制:
3.内部实现原理
对象调用alloc方法实际上内部调用了 + (instancetype)allocWithZone:(struct _NSZone *)zone方法, allocWithZone 调用NSAllocateObject(类,,)
retainCount ,retain ,release方法实际调用的是CoreFondation框架中的_CFDoExternRefOperation(OPERATION_retainCount,self)
_CFDoExternRefOperation(OPERATION_retain,self)
_CFDoExternRefOperation(OPERATION_release,self)
苹果是采用散列表(引用计数表)的形式来管理引用计数
4.autorelease工作原理
//四种写法
1.通过类方法addObject
// NSAutoreleasePool* pool=[NSAutoreleasePool new];
//
// person* per1=[person new];
// [NSAutoreleasePool addObject:per1];
// [pool drain];
2.通过实例方法addObject
//2
// NSAutoreleasePool* pool=[NSAutoreleasePool new];
//
// person* per1=[person new];
// [pool addObject:per1];
// [pool drain];
3.通过autorelease方法
NSAutoreleasePool* pool=[NSAutoreleasePool new];
person* per1=[person new];
[per1 autorelease];
[pool drain];
//4.
@autoreleasepool {
person* per1=[person new];
[per1 autorelease];
}
autorelease实例方法本质就是调用NSAutoreleasePool对象的addObject方法;
iOS 内部实现
objc_autoreleasePoolPush() 等同于NSAutoreleasePool* pool=[NSAutoreleasePool new];
objc_autorelease(per1) 等同于[per1 autorelease];
objc_autoreleasePoolPop(pool) 等同于[pool drain];
4.ARC规则
1.不能使用retain/release/retainCount/autorelease
内存管理是编译器的工作,因此没有必要使用retain/release/retainCount/autorelease方法,编译器会在适当的时候插入这些方法
2.不要显式调用dealloc
3.arc环境四个修饰符 __weak __strong __autoreleasing __unsafe_unretained
__strong 修饰的对象为强引用 对象默认的是__strong修饰符
__weak 修饰的对象为弱引用
__unsafe__unretained 不安全的所有权修饰符,符有__unsafe__unretained修饰符的变量不属于编译器的内存管理对象。
__autoreleasing 在ARC下代替autorelelease的工作,将变量赋值给符有__autoreleasing修饰符的变量 和在MRC下对象调用autorelease方法同理。
理解:
- 在ARC环境中,使用cory alloc new mutable copy 生成的对象,编译器会帮助我们添加release 使用 [NSMutableArray array]诸如此类的方法生成的对象 编辑器会把对象注册到autoreleasePool 中。
- 在访问__weak修饰符的变量时,实际上必定要访问注册在autoreleasePool中的对象。
4.规则
alloc 实际上是调用NSAllocateObject 但是在ARC下禁止使用
init 开头的方法必须是实例方法并且必须要返回对象,返回的类型可以是id类型或该方法声明类的对象类型
5属性
5.1在ARC下属性关键字与所有权修饰符的关系
5.2 在类中声明的实例变量和同一个属性 所有权修饰符必须一致,否则报错
例如:
{
id obj;
}
@property(nonatomic,weak)id obj; 编译报错
6数组
在内存管理方面,静态数组与可变数组的区别:
静态数组:编译器能够根据变量的作用域自动插入释放赋值对象的代码。
可变数组:编译器不能确定数组的生存周期,无法插入释放内存的代码,所以对可变数组来讲,一定要先使可变数组中每个元素强引用失效,再去废弃内存块。
ARC的实现
1__strong修饰符
//1.new alloc 等创建的对象实现过程
{
id __strong obj=[NSObject new];
//内部实现
id obj=objc_msgSend(NSObject,@selectot(alloc));
objc_msgSend(obj,@selector(init));
//变量作用域结束时 释放
objc_release(obj);
}
//2. 通过alloc new copy 方法之外的实现例如 array方法
{
id __strong obj6=[NSMutableArray array];
//内部实现
id obj6=objc_msgSend(NSMutableArray,@selector(array));
objc_retainAutoreleasedReturnValue(obj);
objc_release(obj);
+(id)array{
id obj6=objc_msgSend(NSMutableArray,@selector(alloc));
objc_msgSend(obj,@selector(init));
return objc_autoreleaseReturnValue(obj);
}
}
2.__weak 修饰符
- __weak修饰符修饰的变量所引用的对象被废弃,则将nil赋值给变量。
- __weak修饰符的变量,即是使用注册在autoreleasePool中的对象。
2.1验证1
{
//obj1附加__strong修饰符并已经被对象赋值
id __weak obj1=obj;
//编译器模拟代码
id obj1;
objc_initWeak(&obj1,obj);
objc_destroyWeak(&obj1);
//id obj1;
obj1=0;
objc_storeWeak(&obj1,obj);
objc_storeWeak(&obj1,0);
}
objc_storeWeak函数把第二参数的赋值对象的地址作为键值,将第一参数符有weak修饰符的变量的地址注册到weak表中,如果第二个参数为0,则把变量的地址从weak表中删除,
将废弃的对象的地址作为键值进行检索,查询出对应的附有__weak修饰符的变量的地址,对于一个键值,可以注册多个变量的地址。
在对象被销毁时内部实现的过程是1.objc_release 2.引用计数为0所以执行dealloc 3._objc_rootDealloc 4.object_dispose 5.objc_destructInstance 6.objc_clear_deallocating 在调用.objc_clear_deallocating函数时内部实现是1.从weak表中获取废弃对象的地址为键值得记录 2.将包含在记录中的所有附有__weak修饰符变量的地址,赋值为nil,3.从weak表中删除该记录,4.从引用计数表中删除废弃对象的地址为键值得记录。
ex:
id obj8=[NSObject new];
id __weak obj9=obj8;
NSLog(@"obj===%@",obj9);输出obj===<NSObject: 0x60800001e740>
/**************************************/
id __weak obj8=[NSObject new];
NSLog(@"obj===%@",obj8);输出obj===(null)
/*
*/
2.验证2
{
//obj 是__strong 修饰符切已被赋值的变量
id __weak obj1=obj;
NSLog(@“%@“,obj1);
}
上述代码可以转换为如下形式:
id obj1;
objc_initWeak(&obj1,obj);
id tmp=objc_loadWeakRetained(&obj1);
objc_autorelease(tmp);
NSLog(@“%@“,tmp);
objc_destroyWeak(&obj1);
objc_loadWeakRetained($obj1)函数取出__weak 修饰符的obj1所引用的对象并retain操作,
objc_autorelease(tmp)将tmp变量注册进入释放池中。
3.__autoreleasing 修饰符
将对象赋值给附有__autoreleasing修饰符的变量等同于MRC下给对象发送autorelease消息,