黑马IOS学习总结1--内存管理

1、基本理论

alloc new (mutable)copy 这三者会使计数器+1

retain 返回其调用的对象本身

示例:

Person * p = [[Person alloc] init];    // retainCount 为1 ,p是在栈内存中,其内保存着指向堆内存中开创出来的Person对象地址的值。

[p retain];  // 返回p本身,即可以写成 p = [p retain]  此时retainCount 为2 

[p release]; // 计数器减1 retainCount 为1

[p release]; // 计数器再减1 retainCount 为0 ,将自动调用person对象的 dealloc方法,释放内存。注意,此时,p 中仍然保存着person对象的地址值,但此时,联系已经断开了,堆内存中的person对象已经消除了,称为僵尸对象!p此时也称为野指针!

p.age = 5; // 注意,此处等于调用僵尸对象赋值,可能会不报错。在Xcode中开启内存管理开关(僵尸对象检查机制),就会报错。打开方法:Edit schema --> Diagnostics -->Objective-c Enable Zombie Objects 上打钩

[p release]; // 此时会报错。野指针指向的僵尸对象没法release;

p = nil; // 将 p 这个野指针清空,此时称为 空指针

[p release]; // 此处无错,空指针指向空对象可以release

2、SET方法内存管理

情况一:Person类中有一个属性car

-(void) setCar:(Car *) car{

  _car = car;   // line 1

}

这样做是不妥的,主方法中调用一下说明这个不妥:

Person * p = [[Person alloc]init];

Car * c = [[Car alloc]init];  

p.car = c;  // 调用上面set方法中的line 1 ,因无alloc copy new ,故 虽然 _car 使用并指向了 Car对象,但对Car对象的retainCount并无影响!

[c release]; // Car对象retainCount 为 0 ,Car 对象消失

------此处,Person类对象还没消失,但其内的_car属性已无意义,因其所指的对象已经消失--------

[p release]

情况二:为改进情况一,修改set方法如下

-(void) setCar:(Car *) car{

  _car = [car retain];   // line 2

}

-(void)dealloc{

   [_car release];  // 覆写dealloc方法,既然_car retain了一下Car对象,那么Person对象覆灭时候,作为其内属性的_car也应该release掉这次引用,这也是符合黄金法则的。

   [super dealloc];

}

其意思是,既然_car要使用Car对象,就应该对Car对象做一次引用保留,增加一次retainCount,说明_car在这个属性在使用着Car对象。

但这样仍然是不完善的,主文件中调用一下说明这个不完善:

Person * p = [[Person alloc]init];

Car * c1 = [[Car alloc]init];  

p.car = c1;  // c1的Car对象 引用计数为 2

Car * c2 = [[Car alloc]init];  

p.car = c2;  // c2的Car对象 引用计数为 2。同时,_car 不再指向 c1的Car对象!

[c2 release]; // c2 retainCount 1

[c1 release]; // c1 retainCount 1

[p release];  // p覆灭,同时c2 retainCount 0; c1 retainCount 1,造成内存泄露

情况三:继续改进情况二

-(void) setCar:(Car *) car{

    if(_car != car){

    [_car release];   // 如果_car有旧值,那么这一步会使其释放所保留其指向对象的引用;如果没有旧值,等效于 [nil release] 也无不可

    _car = [car retain];   

    }

}

3、@property的参数释义

1)有关set方法内存管理的

retain 即上面情况三,release旧值,retain新值。适用于OC对象

assign 即上面情况一,直接赋值,适用于非OC对象的基础类型(int,long,float等)   注意,默认是assign的。如果用到此项,即便默认,也要显式的写上!这是好习惯!

copy release旧值,copy新值。

2)是否要生成set方法

readwrite 同时生成setter和getter的声明和实现

readonly 只生成getter的声明和实现

3)多线程管理 

nonatomic 非多线程 性能高

atomic 多线程  性能低 注意,默认是atomic的

示例:

@property(nonatomic,retain) NSString * name;

@property(nonatomic,assign) int age;

4、循环引用问题

即:A类中有一个属性是B对象,B类中有一个属性是A对象。这种情况在使用过程中,是会报错的。解决方法如下:

在一个类中用assign声明属性,另一个类中用retain声明属性即可

额外说明:在.h文件中,用 @class 方式声明一个欲使用的类,在.m文件中再用#import引用类 (如有用到该类方法需导入此类时)

5、autorelease

A 使用了autorelease,就不必再顾虑对象在何处release了,对象会被扔到自动释放池里,工作完了会自动release池子里的对象。autorelease返回对象本身。

注意:

1)使用了autorelease,对象的引用计数并不会变化。

2)占用内存较大的对象,不要随便使用autorelease,因为他会一直占用着内存,等待自动释放池销毁。反之,占用内存较小的对象,使用autorelease则没有多大影响。

3)autorelease错误写法1:

@autorelease{

  Person * p = [[[Person alloc] init] autorelease];  // 计数器为 1

     [p release]; // 计数器为0 此时p为野指针

 } // 自动释放池在此行销毁,会再次release一次其内的对象,也即再一次[p release],而此时p是野指针了,所以会报错

错误写法2:

@autorelease{

  Person * p =[[[[Person alloc] init] autorelease]autorelease];  

 }// 连续多次调用autorelease等效于自动释放池一销毁,会多次调用release,会引发野指针调用错误。

 B autorelease实用技巧

若每次创建对象,都如此这般 Person * p = [[[Person alloc] init] autorelease]; 会显得很繁琐,尤其是多次创建对象。此时,可以在类中建立一个静态方法,示例如下:

+(id) person{  return [[[self alloc] init ] autorelease]; }

这样,每次创建对象的时候,只要 Person * p = [Person person];即可。

注意,这里用self而不是Person是有讲究的,原因是考虑到Person的子类情况。例如,GoodPerson 继承 Person类,如果此处不用self,而用Person,那么 GoodPerson *gp = [GoodPerson person];实际返回的还是Person对象,这样子类中的一些方法就无法使用了。

6、ARC

ARC的判断准则:只要没有强指针指向对象,就会释放对象。

指针分两种:

1)强指针,默认情况下,所有指针都是强指针 __strong

2)  弱指针,__weak   使用示例:__weak Person * p = [[Person alloc] init];

手动管理内存到ARC的等效转变

@propery(nonatomic,retain)  -----> @property(nonatomic,strong) 适用于OC对象

@propery(nonatomic,assign)  -----> @property(nonatomic,weak)  适用于OC对象

@propery(nonatomic,assign)  -----> @property(nonatomic,assign)  适用于基本类型

循环引用问题在ARC中的解决:一个属性用strong,另一个用weak即可。

原文地址:https://www.cnblogs.com/appzhang/p/3588169.html