一、内存管理根源
1、任何继承了NSObject对象都需要做内存管理。栈空间的东西自动回收,而堆空间的不会,对象存在堆空间,需要手动回收。
二、引用计数器
1、怎样判断何时回收,每一个OC对象都有自己的整型引用计数器,表示对象被引用的次数。
2、当使用allco(init对计数器没影响,它不是分配内存,是初始化)copy,new创建一个新对象时,对象的引用计数器默认值为1,当对象的引用计数器为零时,就会被系统自动回收。给对象发送一个retain消息计数器加1,给对象发送一个release消息计数器减1,还可以调用retainCount消息获取当前引用计数器的值。main函数有一个循环,只要不返回程序就不退出。
3、僵尸对象只的是所占用内存已经被回收,不能再被使用。野指针是指向僵尸对象的指针。空指针是没有指向任何地方的指针。
- Person *p = [[Person alloc] init];
- [p release];//有alloc必需有release
- p=null;//防止野指针错误
-
- [p retain];//计数器加一,返回对象本身
- [p release];
- p = null;//防止野指针错误,在OC里面给一个空指针发送消息时不会报错的。
-
- //有retain必需有release。release必须成对出现,否则会出现野指针错误,当对象已经释放,但指针依然指向这块内存
- //继续访问这块内存将会报错,EXC_BAD_ACESS指针反问一块不可用的内存。
-
- //对象在销毁时会自动调用delloc
- - (void) dealloc
- {
- //dealloc方法,一定要调用,并且要放在最后。
- [super dealloc];
- }
三、多对象内存管理
1、当你想拥有某个对象时,应该对对象计数器retain,有加必有减,当不使用时release。
2、当使用allco(init对计数器没影响,它不是分配内存,是初始化)copy,new创建一个新对象时,对象的引用计数器默认值为1,当对象的引用计数器为零时,就会被系统自动回收。
- //Person类里面包含一本书
- @interface Person :NSObject
- {
- Book *_book;//再定义属性时要以下划线开头
- }
- - (void)setBook:(Book *)book;
- -(Book *)book;
- @end
- @implementation Person
- - (void)setBook:(Book *)book{
- //如果重复设置相同对象,不做任何操作
- if(_book == book){
- //对当前进行使用的对象进行release。对NULL对象release没关系
- [_book release];
- //把成员变量对象引用计数加一
- _book = [book retain];//retain对象返回的事对象本身
- }
- }
- - (Book *)book{
- return _book;
- }
- - (void)dealloc
- {
- //在person对象销毁的时候会自动调用该方法,这是应该对Book对象进行release
- [_book release];
- [super dealloc];//调用父类方法,放在最后
- }
-
- @end
- int main(){
- Person *p = [[Person alloc] init];
- Book *b = [[Book alloc] init];
- [p setBook:b];//这个时候这本书的引用应该2。set方法里应该自动给引用计数retain
- b = null;
- [b release];//有alloc就必须有release;谁创建谁release
- p = null;
- [p release];
- }
- Person *p = [[Person alloc] init];
- [p release];//有alloc必需有release
- p=null;//防止野指针错误
-
- [p retain];//计数器
3、@property属性会自动生成get方法和set方法。
- //@property会自动生成get和set方法,成员变量会以下划线开头,同时也可以自己实现get或set方法
- @property (monatomic ,retain) Book *book
- //retain方法就会自动在set方法里面添加如下代码,但是dealloc方法还是要自己写
- (void) setBook:(Book *)book{
- if(_book != book){
- [_book release];
- _book = [book retain];
- }
- }
- (void) dealloc{
- [_book release];
- [super dealloc];
- }
- //解决方法,1@property ,2重写dealloc方法
-
1>内存管理相关参数。retian会release旧值,retian新值。默认为assign是直接赋值,适用于非OC对象类型。copy 用于NSString
2>是否生产set方法。readonly只读,默认readwrite。
3>多线程管理。nonatomic,默认atomic,性能高。
4>set和get方法的名称。取方法名 set= setBook: 方法名后要有冒号。
5、循环引用
1、一个人里面有个身份证属性,一个身份证类有个人属性。在头文件中用@class声明成员变量是一个类,在.m文件中真的用到这个类的对象的方法时在引用#import。
2、开发中引用一个类的规范,在.h方法中@class声明一个类,在.m文件中用#import文件。
3、1@class可以解决循环引用,2效率高,#import是拷贝会包含被引用类的所有信息,使用#import会降低编译效率,如果一个文件改变了,后面的文件需要重新编译,降低编译效率,@class不会。
4、循环引用的解决办法,一端用assign,一端用retain
- //person
- @class Card
- @interface Perosn :NSObject
- @proterty (noatomic, assign) Card *card;
- @end
-
- #import "Card.h"
- @implementation Person
- -(void) dealloc{
- [super dealloc];
- }
- @end
-
- //card
- @class Person
- @interface Card:NSObject
- @property (noatomic,retain) Person *person;
- @end
-
- @implementatin Card
- -(void) delloc{
- [_person relase];
- [super dealloc];
- }
- @end
-
- //
- int main()
- {
- Person *p = [[Person alloc] init];
- Card *c = [[Card card] init];
- p.card = c;//循环引用
- c.person = p
- [p release];
- [c release];
- }
5、autorelease方法
1、autorelease是半自动释放,autorelease方法会返回对象本身,autorelease会将对象放大自动释放池中,当池子销毁时,会对池中对象进行release。
2、放到池子内的对象不允许再进行release操作。 不可以对对象联系调用两次autorelease方法,这样池子销毁时会调用两次release,出现野指针错误。
3、当子类继
- @autoreleasepool{//好处是不需要care对对象的release操作,并且对对象的操作一定要放在对对象操作的前面,缺点时不能精确控制对象的释放时间。最好显示调用retian release
-
- //对象的autorelease方法,会将对象放到池子里,不会对对象计数器的值进行改变
- Person *p = [[[Person alloc] init] autoreleas];//autorelease方法已定要写
- p.age =0;
- //[p release];如果不用autorelease,设置年龄的操作已定要在这一行前,如果在这一样后,就会出现野指针错误,autoreleas则不会
-
- @autoreleasepool{//对象池可以循环嵌套
- Person *p2 = [[[Person alloc] init] autoreleas];
- p2.age = 10;
- }
-
- }
- //实例,为了避免在每次创建一个对象都写autoreleas方法,可以在类里写一个类方法返回放在释放池中的对象
- +(id)person{
- return [[[Person(如果这里写self代码可扩展性好) alloc] init] autorelease];
- }
- //当子类继承父类的方法时,即拥有负累的所有方法和属性,但父类的方法可能不满足子类的要求,如上面返回的事一个person对象
- //可是子类不可能也是person对象,子类一会扩冲自己的属性,当你调用这个父类对象访问子类的属性时,父类没有
- //就会报不能识别的消息发送给某个对象,解决方法:1重写父类方法,2要实现谁调用这个方法我就返回谁,把Person改成self
- //同时还可以给对象设置一些属性值
- + (id)personWithAge:(int)age{
- Person *p = [[[Person alloc] init] autorelease];
- p.age = 10;
- return p ;
- }
三、模型类
用来存数据的类,称为模型类,写完属性,实现get,set方法,注意释放对象存储空间。如果使用@property,需要重写dealloc方法,release成员对象。
不一定要重写init方法。