IOS第四节:OC中的内存管理机制

来源:互联网 发布:上古卷轴5捏脸数据整合 编辑:程序博客网 时间:2024/06/05 17:27
【自动释放池】

[autorelease方法和自动释放池]
每一个对象都有一个autorelease方法,调用autorelease方法之后不会像调用release方法一样马上释放对象,
当一个对象调用一个autorelease的时候,这个对象就会被放倒main函数开始时设置的自动释放池里
当整个函数执行完毕之后,autoreleasepool才会销毁,销毁时把自己内部所有的对象全部释放掉,方式就是对所有的对象都执行一次release。
它和对象在哪里创建的没有关系,只要执行autorelease方法即可实现自动释放池
调用几次autorelease方法就往自动释放池里放几次,在自动释放池销毁的时候就会销毁几次

[自动释放池的嵌套]
自动释放池可以实现一个栈的结构,一个自动释放池中可以包含另一个自动释放池

[自动释放池的原理]
1、系统中有一个自动释放池栈,当程序运行到一个@autoreleasepool{}时,就会把当前的autoreleasepool压入栈中,在退出的时候,也是后进先出
2、当遇到与之对应的}时,自动释放池出栈,出栈的时候会对池中所有的对象进行一次releease操作
3、自动释放池栈中只有栈顶的自动释放池是活动的,其他的都在休眠
4、当调用autorelease的时候,会把这个对象放到栈顶的那个自动释放池中

[自动释放池的使用场景]

总的来说一句话,只要是从方法中返回出来的对象,方法中都需要在返回对象之前调用自动释放池

(1)、getter方法里返回对象
当要通过普通的方法来返回成员对象给外面的时候,外面不需要对获得到的这个成员对象进行内存管理,这时候外面就需要使用自动释放池保护相关操作,并在成员对象的getter函数中应该使用如下写法
用法:
先retain
再autorelease
 其实就是autorelease延长了生命周期
(2)、类方法中创建自身对象(快捷构造方法)
其实所有的NS开头的构造方法内部都调用了autorelease

[成员对象赋值时进行的操作]
因为成员对象是一个对象,所以就会有引用计数,在赋值成员对象的时候应该先释放掉旧的对象(调用release将旧对象的引用计数-1),然后赋值给类成员新的对象(调用retain将新对象的引用计数+1)

【手动内存管理】
在手动内存管理的时候(关闭了自动内存管理)
[手动内存管理的关键字]
@property (retain) Room *room;
retain 在setter喝getter方法中加入一些内存管理代码
当属性是一个普通的OC对象的时候,使用retain

[MRC中的@property]
@property (assign) int age 
assign 直接赋值,不生成内存管理的代码
基本数据类型是在栈内的,栈内的变量是不需要内存管理的

@property (copy) NSString *name
copy表示复制对象,NSString一般使用copy,其他的对象都使用retain

@property (nonatomic)int age

[生成多线程的线程安全关键字]
nonatomic:表示非原子的,不会生成线程安全的代码,速度会比较快,在ios中属性通常都是这种的。一般要把所有的关键字都写成nonatomic的

@property (readwrite) int age

[控制权限的关键字]
readonly:只读,不可写,只能生成getter方法
writeonly:只写,不可读,只能生成setter方法
readwrite:可读写,能生成getter和setter方法

[修改方法名称的关键字]
@property (getter = isMan, setter = myMan:)BOOL sex;
通过property 属性修改getter/setter方法的名称
这样使用的时候就可以调用
sex = 类名.isMan;
[p myMan:YES];
一般情况下都不修改setter和getter方法

(5)关键字的组合方法
多个属性之间可以这样组合
@property (atomic, assign)int age;

【自动内存管理】
ARC机制可以帮我们解决所有的内存问题 
这是一种编译器特性,编译器会在适当的时候加入内存管理代码
当打开了自动内存管理时,在代码中不允许写内存管理的操作
我们可以写一个dealloc,但是在dealloc中一定不能写[super dealloc],因为编译器已经自动添加了

[强指针 弱指针]
强指针:默认所有指针都是强指针
只要有强指针一个对象,这个对象就不会被释放
如果没有强指针指向一个对象,这个对象就会被立即回收
例子
如果一个对象只有一个指针指向,那么如果这个指针不指向这个对象,对象就立即死了
出了作用域之后,指针也会被释放(栈自动回收)
__strong就是强指针的标示,默认所有的指针都是强指针,所以没卵用

弱指针指向的对象不会影响对象的回收
__weak时弱指针的标示,如果在对象创建的时候用弱指针接收的话,那么就该对象在创建后直接就被杀死了,所以不要用弱指针指向一个刚刚创建出来的对象
弱指针的作用
在自动内存管理的时候会有循环引用的问题,在其中的一个对象指针上使用弱指针就能解决循环引用的问题
例如
人里有狗,狗里有人
这种情况就会产生循环应用的问题
人会管理狗的生命周期
狗不会管理人的生命周期
所以狗中的人成员对象就应该设置为弱指针
当多个对象之间的引用构成一个环,则这个环中所有指针都不会被释放掉,只有其中的一个成员对象是弱指针,才能顺利的释放

[ARC中的@property]
在ARC中,使用@property的时候一般
对基本数据类型使用nonatomic和assign
对字符串数据类型使用nonatomic和copy
对对象数据类型使用nonatomic和strong/weak
其中weak是为了防止循环引用,像在Dog类中的person对象这样的需要使用weak,一般对象都是strong
例:
@property (nonatomic, assign) int age;
@property (nonatomic, copy) NSString *name;
@property (nonatomic,strong) CZDog *dog;
解释:assign:对该成员赋值时不改变引用计数
 copy:对该成员赋值时释放旧的对象保持新对象引用计数为1
strong:强指针
weak:弱指针

[instancetype]
instancetype可以检查类型,并且只能作为返回值类型,其他什么都不能做

[ARC中的自动释放池]
方法创建的对象被放在自动释放池中,即使没有强指针指向方法创建出来的对象,这个对象也不会被立即释放。
方法返回的对象,可以在没有强指针指向的情况下存活于自动释放池中
但是这里说的方法不包括alloc,alloc是比较特殊的,它返回的对象没有执行放进自动释放池的操作

[由自动释放池引出的关于构造函数的思考]
所以这就解释了为什么alloc出来的指针是中间指针,而最后返回的指针是init前缀的构造方法产生的指针
因为init前缀的构造方法都是工厂方法,工厂方法里肯定会执行三步操作
第一步:释放self(释放调用此init工厂方法的本对象)
第二步:创建新对象(创建一个新的对象接收传入的初始化值)
第三步:对接收完的新对象进行autorelease操作
第四步:返回新对象给一个强指针
 
[引用计数增加的情况]
强指针指向,+1
跳出作用域,-1
赋值nil, -1
autoreleasepool的边界右括号,-1

【自动释放池的一道面试题】
autoreleasepool{
for(long i = 0; i < NSIntegerMax; i++)
{
NSString *str = [NSString stringwithFormat:@“dawd”];
str = [str uppercaseString];
str = [str lowercaseString];
str = [str capitalizedString];
}
}
问题出在使用这个方法[NSString stringwithFormat:@“dawd”];创建出来的对象释放在自动释放池中的,但是for循环一直处于自动释放出中,所以就一直不释放,解决办法如下:
autoreleasepool{
for(long i = 0; i < NSIntegerMax; i++)
{
autoreleasepool{
NSString *str = [NSString stringwithFormat:@“dawd”];
str = [str uppercaseString];
str = [str lowercaseString];
str = [str capitalizedString];
]
}
}
PS:这个操作会导致CPU满载,另外每一个项目都都要注意内存和CPU的占用问题

[OC的ARC特点]
OC的ARC是编译器特性,不是运行时特性,java是运行时检测那个对象需要被释放,所以效率低
但是oc是编译的时候由编译器自动添加内存管理代码,所以不会对效率产生任何影响

【经典的循环引用问题】
person包含dog
dog也包含person
如果全部都加上手动内存管理的情况下,则每一个对象的引用计数都是2,则在程序结尾调用release后,都为1,则互相之间都释放不了
MRC解决方法(使用retain/assign):将其中任一一个应用其他对象的成员,在定义的时候设置assign关键字,即赋值给这个成员对象的时候不会增加引用计数
多个对象之间成环引用的情况的解决方法也是一样的,只要把任一成语对象引用设置为assign即可

ARC解决方法(使用strong/weak):将其中任一一个应用其他对象的成员,在定义的时候设置weak关键字,即赋值给这个成员对象的时候不会增加引用计数
多个对象之间成环引用的情况的解决方法也是一样的,只要把任一成语对象引用设置为weak即可


0 0
原创粉丝点击