------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------
OC加强--第一天学习总结:
1. 内存管理范围:
1)管理任何继承NSObject 的对象,对其他的基本数据类型无效。
2. OC内存管理的原理
1)引用计数器的作用:引用计数器是判断对象要不要回收的依据(存在一种例外:对象值为nil 时,引用计数器为0 不回收空间)就是计数器是否为0,若为0 则不存在。
2)引用计数器的操作;
给对象发送消息,进行相应的计数器操作;
retain 消息:使计数器加1.
release消息:使计数器减1;
retainCount : 得到引用计数器的值;
3) 如果一个对象被释放的时候,会有“临终遗言”(会调用该对象的dealloc 方法);
注意:(1)dealloc 方法是NSObject的,一般我们需要重写dealloc方法。
(2)在dealloc方法内部,要调用[super dealloc];
3.内存管理的分类:
1)MRC 手动内存管理;
2)ARM 自动内存管理;
3)垃圾回收,OC一般不使用;
4.内存管理研究的内容;
1) 野指针:
(1) 定义的指针没有初始化。
(2)指向的空间已经被释放了。
2) 内存泄漏;
Person *P =[Person new];如果栈区的P已经被释放了,而堆区的空间还没释放,此时会造成内存泄漏;
5.单个对象内存管理野指针问题:
1) 如果内存对象已经被释放掉了,就不能在使用了。
2)给nil 发送任何消息,都没有效果,所以nil调用任何方法,都不会有结果;
3) 避免使用僵尸对象方法是,对象释放后,给对象付值为nil;
6. 单个对象内存泄漏问题:
1)对象创建完成,使用之后,没有释放内存;此时会泄漏内存;
2)不遵守内存管理规则,多次申请而不释放(或者少释放);
3) 不当的使用new,或者中间使用了nil给对象复制.
4) 在方法中,对会传入的对象进行了retain 然后只进行了一次释放;
7. 多个对象内存管理:
1)注意野指针的访问:一个对象在没有销毁之前,可以任意多次的调用自己的方法,如果因为里面可能调用了已经销毁的对象了,但是此时会报错(一定要开启僵尸对象检测模式)。
2)一定要注意, 当一个对象作为另一个对象的实例变量的时候,注意变量一定要销毁,销毁的方法有,在set方法中,如果调用的还是同一个方法,则加上一个[P retain]否则,如果不是同一个方法,则要先加上一个dealloc 然后再[P retain];
3) 如果在一个类中,有其他的对象(关联关系),则在写set方法书写的时候,要先release 旧值,然后retain新值。
8. set方法的内存管理原则:
1)基本数据类型的数据作为实例变量,set方法直接赋值既可。
2)如果在一个类中有其他类的对象(关联关系)则在set方法书写的过程中,要先判断是否是头一个对象,如果不是,应该先释放旧值(release 旧值),然后再retain 新值。如果是同一个对象,则只需让set方法直接赋值既可。
一般写法为:
-(void)setDog:(Dog *)dog{
if(_dog !=dog){
[_dog release];
_dog =[dog retain];
}
}
9.@property参数:
1)格式:@property (参数1,参数2)数据类型 方法名
2)替换set/get方法名称:
格式为:
@property(nonatomic assign setter =isVip: )bool vip;
@property(nonatomic assign getter =isVip: )bool vip;
10.@class的使用:
@class 类名XXX: 含义是:告诉编译器,XXX是一个类,至于类有哪些属性和方法,此处不去检测,好处:如果XXX 文件内容发生了改变,而不需要重新编译。如果要引用另一个文件中的实例变量,进行相应的操作,则需要在.m头文件中引用(#import 类名)。
#import与@class的区别
1.import会包含这个类的所有信息,包括实体变量和方法,而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,暂时不用考虑,后面会再告诉你。
2.在头文件中, 一般只需要知道被引用的类的名称就可以了。 不需要知道其内部的实体变量和方法,所以在头文件中一般使用@class来声明这个名称是类的名称。 而在实现类里面,因为会用到这个引用类的内部的实体变量和方法,所以需要使用#import来包含这个被引用类的头文件。
3.在编译效率方面考虑,如果你有100个头文件都#import了同一个头文件,或者这些文件是依次引用的,如A–>B, B–>C, C–>D这样的引用关系。当最开始的那个头文件有变化的话,后面所有引用它的类都需要重新编译,如果你的类有很多的话,这将耗费大量的时间。而是用@class则不会。
4.如果有循环依赖关系,如:A–>B, B–>A这样的相互依赖关系,如果使用#import来相互包含,那么就会出现编译错误,如果使用@class在两个类的头文件中相互声明,则不会有编译错误出现。
所以,一般来说,@class是放在interface中的,只是为了在interface中引用这个类,把这个类作为一个类型来用的。 在实现这个接口的实现类中,如果需要引用这个类的实体变量或者方法之类的,还是需要import在@class中声明的类进来.
11.循环retain问题:会导致两个对象都会内存泄漏;
防止方法:
1)让某个对象多释放一次(要注意释放顺序)2)一端使用assign 一段使用retain(推荐使用)
12.NSString类的内存管理:
1)字符串的常量池:如果需要的字符串在常量池中已经存在,则不会再次分配空间。
13.autorelease的基本使用:
1)自动释放池是一种特殊的栈结构,对象可以加入到自动释放池中,自动释放池结束的时候,会给池中的对象发送一条release消息。
2)加入自动释放池,格式:[对象 autorelease];
3)加入到自动释放池中以后,引用计数不会改变;
4)@autoreleasepool{自动释放池开始 }自动释放池结束;
5)当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中。
实例如下:
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
//1 创建自动释放池
Person *p = [Person new]; // p 1
@autoreleasepool {//自动释放池开始
[p run];
NSLog(@"%lu",p.retainCount); // 1
// [p autorelease] 把对象p加入到自动释放池中
// 注意:加入到自动释放池中以后, 引用计数不会变化
[p autorelease]; //加入自动释放池,
NSLog(@"%lu",p.retainCount); // 1
[p run];
}//自动释放池结束 [p release];
[p run];
return 0;
}