iOS内存管理

内存管理的思考方式:

自己生成的对象,自己持有。

非自己生成的对象,自己也能持有。

不在需要自己持有的对象时释放

非自己持有的对象无法释放


一.手动内存管理

1.基本原理 

   对象的创建,OC创建对象时,不会直接返回该对象,而是返回一个指向该对象的指针。

 

Class *a=[[Class alloc]init]; 在alloc时,系统会给Class的对象分配内存空间,并且反回了指向未初始化的对象的一个指针

未初始化的Class对象接收到init消息时,init返回指向已经初始化Class对象的一个指针,然后将其赋值给变量a


如果指针a和b同时指向堆中同一块内存地址


Class *a=[[Class alloc]init];//自己生成并持有对象

Class *b=a;

当a释放时,b就成了无头指针(无头指针式危险的)


id objc=[NSMutableArray array];//取得的对象存在,但自己并不持有对象。

[objc retain];//自己持有对象


2.OC的内存管理采用引用计数(retain count)即在对象内部保存一个数字,用来表示被引用的次数,init,new,copy,mutacopy都会使retain count加1

当我们销毁对象时,先调用release方法,当retain count为0时,系统才会调用dealloc方法销毁对象。

在指针赋值时,retain count是不会自动增加的

Class *a=[[Class alloc]init];//reatin count=1;

Class *b=a;

[b retain];//retain count=2;

[a delloc];

这样在执行到[a release ]时,retain count只是减了1,指针b仍然有效


3.内存泄露: 

           当生成Class对象时,指针a就拥有了对象的访问权,如果失去了对象的访问权,但有没有将retain count减到0,就会造成内存泄露,即分配出去的内存无法回收

  Class *a=[[Class alloc]init];

  a=nil


4.Autorelease pool

autorelease的具体使用方法为:

(1)生成并持有NSAutoreleasePool对象

(2)调用已经分配的对象的autorelease实例方法。

(3)废弃NSAutorelease对象

源代码表示如下:

NSAutoreleasePool *pool=[NSAutorelease alloc]init];

id obj=[NSObject alloc]init];

obj autorelease];

pool drain];//等同于 obj release


为了方便程序员管理内存,apple在OC中引用了自动释放池,在遵守一定规则的情况下,可以进行自动释放

Class *a=[[Class alloc]init]autorelease]; //retain count =1,但无需release

autorelease pool需要手动创建

NSAutoreleasePool *pool=[[NSAutoreleasePool alloc]int];但是通常情况下,在我们创建一个iPhone项目时,Xcode会自动创建autoreleasePool,这个pool就写在Main函数里面。

在NSAutoreleasePool中包含了一个可变数组,用来存放被生命为autorelease的对象,当NSAutoreleasePool自身被销毁时,他会遍历这个数组,release数组中得每一个成员(只是release,没有销毁对象),此时若对象的retain count 大于1,对象就不会被销毁,造成内存泄露。

 默认的NSAutoreleasePool只有一个,但你可以在程序中创建NSAutoreleasePool,被标记为autorelease的对象会和最近的NSAutoreleasePool匹配

也可以嵌套使用NSAutoreleasePool,就像for循环那样。

但是使用NSAutoreleasePool管理内存是不推荐的,因为在一个NSAutoreleasePool里,如果有大量对象被标记为autorelease,在程序运行时,内存会剧增,直到NSAutoreleasePool被销毁时,才会释放。如果其中的对象足够多,就会发生内存警告,或者直接崩溃。

注意:

如果我们将主函数中得NSAutoreleasePool代码删掉,然后在自己的代码中将对象声明为autorelease,此时系统不会发生错误或者警告,此时如果用内存检测工具去检测内存时,会发现对象仍然被销毁了。

原因:其实在新生成一个Run Loop的时候,系统会自动创建一个NSAutoreleasePool,这个NSAutoreleasePool无法被删除。


但是做内存检测时,不要用NSString,因为OC对字符串进行了特殊处理。

例如 NSString *str=[[NSString alloc]stringWithString:@"444"];

在输出str的retain count的时候,你们发现retain count大于1。


5.手动管理内存Class *a=[[Class alloc]init];

Class *b=a;

[b retain]

[b release];

b=nil;

把一个指针赋值给另外一个指针时,a指针所指向的对象的引用次数并没有增加,也就是说,其retain count =1

然后 [b retain] retain count +1; 那么此时执行 a release 只是a指针放弃了该对象的访问权。对象的retain count减去1

对象并未被销毁,只有b release之后,才会将对象销毁掉,在对象销毁后,指针仍然存在,因此最后把指针赋空,release一个空指针式合法的



6.属性和内存管理

@property实际上是getter和setter方法,

nonatomic:如果你的程序里只有一个主线程,即不会在2个或者更多的线程上工作时访问同一个变量,那么你可以声明为nonatomic,这样不会去考虑线程安全问题。

但相反情况下,可以声明为atomic,也可以不声明,因为系统默认为nonatomic,这样就会加上一个锁,在同一时间,只会有一个变量访问这个该变量。

但是用锁是要付出代价的,一个声明为atomic的属性在设置和获取这个变量的时候要比声明nonatomic的慢,因此如果不打算编写多线程代码,最好把其声明为nonatomic


assign是系统默认的属性,它几乎适用于OC所有的变量类型,对于非对象类型的变量,assign是唯一的选择。但其声明对象是,只是创建了一个弱引用,因此声明对象时,最好不要用


关于assign合成那个的setter 类似于这样

-(void)setObjA:(ClassA *)a

{


objA=a;


}


声明retain特性的setter方法为

-(void)setObjA:(ClassA *)a{

if(objA!=a){

[objA release]

objA=a;

objA retain];

}



}

很明显,retain的setter中变量retain了一次,因此

在程序中self.objA=a;

只写了这一句,仍然需要release,才能保证retain count是正确的

但如果值谢了objA=a;

这里只是进行了一次浅复制,retain count并为增加,这样写的话,就不需要release

这两句的区别是,第一个是setter,第二个只是简单的指针赋值

copy的setter是这样的

-(void)setObjA:(ClassA*)a

{

ClassA * temp=objA;

objA=[a copyWithZone:nil];

[temp release];

}

复制必须经过实现copyWithZone这个方法(该方法生成并持有对象的副本),因此copy这个特性只适用于拥有这个方法的类型,即必须这个类支持复制,复制是把原来的对象release掉,

然后让指针指向一个新的对象的副本,因此即使在setter里面release了原来的对象,你仍然需要在后面release掉其副本。


二、自动内存管理(ARC)

1.Id类型用于隐藏对象类型的类名部分,相当于C语言中的void*

Id和对象类型在没有明确指定所有权修饰符时,默认为__strong强引用,持有强引用的变量在超出其作用域时被废弃。

Id __strong obj = [[NSObject alloc]init];  自己生成并持有对象

2.__week修饰符

引用计数式内存管理必然会发生循环引用的问题。

下面说一个循环引用的例子

    {id test1=[[Test alloc]init];

    //test1持有test对象A的强引用

    

    id test2=[[Test alloc]init];

    //test2持有test对象B的强引用

    

    [test1 setObject:test2 ];

    //Test对象Aobj_成员变量持有对象B的强引用,此时持有Test对象B的强引用变量为Test对象Aobj_test1

    

    [test2 setObject:test1 ];


    //test对象Bobj_成员变量持有test对象A的强引用。此时持有test对象A的强引用变量为Test对象Bobj_Test1

}

    /*

     因为Test0变量超出其作用域,强引用失效,所以自动释放Test对象

     因为Test1变量超出其作用域,强引用失效,所以自动释放Test对象

     此时持有Test对象A的强引用的变量为TestBobjc_

     此时持有Test对象B的强引用的变量为TestAobjc_

     发生内存泄露

     


     */

所谓内存泄露就是应当废弃的对象在超出其生存周期后仍然存在。

__week 修饰符可以避免循环引用 弱引用不持有对象,所以在超出其变量作用域时,对象即被释放。

其另一个优点为 当持有某对象的弱引用时,若该对象被废弃,则此弱引用将自动失效,且被赋值为nil。


参考 :小议内存管理和Object-c高级编程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值