1.对象引用计数器:
》占用4个字节
》引用计数器分两类:ARC(Automatic Reference Counting) 自动引用计数(xcode 默认) ,MRC 手动引用计数。
》当创建一个对象时:Iphone * ip = [ [Iphone alloc] init ] ; ip这个对象的引用计数变成1.
》当对象的引用计数是0的时候,系统立即把它回收。
》当一个对象调用 retain 时(前提为MRC,ARC是不允许调用) :[ ip retain ]; ip这个对象的引用计数 + 1 。
》当一个对象调用 release 时(前提为MRC,ARC是不允许调用):[ ip release ] ; ip 的引用计数 - 1。
》获得计数器 , retainCount方法: [ip retainCount];
》当对象ip的引用计数为0时,系统会立即调用dealloc对象方法。如果为MRC时,可以在类中重写dealloc对象方法:
- (void) dealloc{
NSLog(@"回收"); //验证
[当前类特有的成员变量 属于OC类 release] ; // 只要成员变量属于OC类
[super dealloc]; // 释放从父类继承来的属性对象,一定要写,不然内存回收失败。如果重写先关闭ARC内存管理
}
》注意:在写程序时,我们只须知道是如何运行就OK,不要去手动管理内存。
2.set方法手动内存管理:
》只要set方法中用了retain 那么就一定要重写dealloc对象方法(该方法是系统释放内存时自动调用)。
(1)形式一,手动写Person对象拥有Car对象:
- (void)setCar:(Car *)car{
if ( _car != car) {
[ _car release]; //当前对象要换其他车,则先对旧车引用计数-1.
_car = [car retain]; //对新对象car计数+1.
}
}
(2)形式二,编译器自动生成:@property (nonatomic , retain) Car * car ; //和形式一 一模一样
3.@property 的参数:
(1)set 方法内存管理相关的参数:
》retain : release 旧对象,retain 新对象----(适用于OC对象类型)
》assign : 直接赋值(默认,适用于非OC对象类型)
》copy : release 旧值,copy新值
(2)是否要生成set方法:
》readwrite : 同时生成setter 和 getter 的声明、实现(默认)
》readonly : 只会生成getter的声明、实现
(3)多线程管理:
》nonatomic : 性能高(一般就用这个)
》atomic : 性能低(系统默认)
(4)setter = functionNam 和 getter = functionNam 来指定方法的名称:
》setter : 决定了 set 方法的名称为functionNam,一定要有个冒号" : "。
》getter : 决定了 get 方法的名称。
》例子:@property (nonatomic,retain , setter = abc: , getter = getAbc) Car * car ;
那么自动生成的set / get 方法名 变成了 - (void)abc:(Car *)car ; - (Car *)getAbc ; 但也可以调用setCar方法来给成员变量 _car 赋值,get方法同理。
4.成员变量的 对象引用计数(MRC) 被循环判断且循环执行dealloc对象方法 和 内存泄漏问题:
》前提:两个类A和B,A类中有类型为B的成员变量 _b,B类中有类型为A的成员变量 _a
》A类的成员变量 _b 的set / get 方法:@property (nonatomic ,retain)B * b ;
B类的成员变量 _a 的set / get 方法:@property (nonatomic ,retain)A * a ;
实现了dealloc对象方法:- (void) dealloc{ [ name release] ; [surper dealloc] ; } //name = _a 或 _b
》main函数体如下:
A * pa = [ [A alloc] init ] ; // pa引用计数 = 1
B * pb = [ [B alloc] init ] ; // pb引用计数 = 1
[pa setB: pb] ; // pb引用计数 = 2
[pb setA: pa] ; // pa引用计数 = 2
// 一个alloc 对应一个 release
[pb release] ; // pb引用计数 = 1
[pa release] ; // pa引用计数 = 1
(1)问题:
》上面main函数体的代码出现内存泄漏:
两个对象引用计数 都等于1,在堆里面开辟的空间没有清空。两个对象都没有调用dealloc对象方法。
》如果上面的[pb setA: pa]; 这行代码去掉,那么会在[pa release] ; 这行代码, pa引用计数 = 0 ,会无限循环调用A类dealloc,B类dealloc , _a是个野指针,出现报错。
(2)解决方案:
》两个类中set方法管理内存,一个用retain , 一个用assign。
》在两类中做如下更改: 在A类:@property (nonatomic ,retain)B * b ;
在B类:@property (nonatomic ,assign)A * a ;
5.autorelease与release都是使对象计数器-1,说明:
(1)基本用法:
》对象 = [对象 autorelease] ;
》会将对象放到一个自动释放池中。
》会返回对象本身。
》调用完autorelease 方法后,对象的计数器不变》当自动释放池被销毁时,会对池子里面的所有对象做一次release操作。
(2)autorelease 的优点:
》不用再关心对象释放的时间;
》不用再关心什么时候调用relese。
(3)autorelease 的缺点:
》占用内存较大的对象不要随便使用autorelease,因为在开发当中要明确对象释放的时间,一般都会用release。
(4)错误写法:
》alloc之后调用了autorelease,以调用release:
@autoreleasepool
{ // { 开始代表创建了释放池,以栈的形式放入对象
Person * p = [ [ [ Person alloc ] init ] autorelease ];
........
[p release];
》连续调用多次autorelease:
@autoreleasepool
{ // { 开始代表创建了释放池
Person * p =[ [ [ [Person alloc] init] autorelease ] autorelease ] ;
........
(5)自动释放池:
》在IOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在
》当一个对象调用autorelease 方法时,会将这个对象放到栈顶的释放池
(6)自动释放池的创建方式:
》IOS 5.0以前:
NSAutoreleasePool *pool = [ [NSAutoreleasePool alloc ] init ] ;
......
[pool release] ;
》iOS 5.0开始:
@autorleasepool
{
...........
}
(7)autorelease 实际应用:
》系统自带的方法里面没有包含alloc , new , copy , 说明返回的对象都是autorelease的。例如:[NSString stringWithFormat: .......]
》开发中经常会提拱一些类方法,快速创建一个己经autorelease过的对象;创建对象时不要直接用类名,一般用self
+ (id)person
{
return [ [ [ self alloc ] init ] autorelease ] ;
}