OC内存管理之—引用计数

引用计数器
1 在Xcode4.2以及以后的版本中由于引入了ARC(Automatic Reference Counting)自动引用计数机制,程序编译时Xcode可以自动给你的代码添加内存释放代码,如果编写手动释放代码Xcode会报错,如果使用是Xcode4.2以后的版本,必须手动关闭ARC。
ObjC中的内存管理机制跟C语言中指针的内容是同样重要的,要开发一个程序并不难,但是优秀的程序则更测重于内存管理,它们往往占用内存更少,运行更加流畅。虽然在新版Xcode引入了ARC,但是很多时候它并不能完全解决你的问题。在Xcode中关闭ARC:项目属性—Build Settings--搜索“garbage”找到Objective-C Automatic Reference Counting设置为No即可。如下图所示:


2 内存管理原理
在C#、Java中都有GC自动管理内存,当我们实例化一个对象之后通常会有一个变量来引用这个对象(变量中存储对象的地址),当这个引用变量不再使用之后(也就是不再引用这个对象)此时GC就会自动回收这个对象,简单的说就是:当一个对象没有任何变量引用的时候就会被回收。当声明的对象出了它所在的域,他所指向的对象就会被GC自动回收。 但是在OC中没有垃圾回收机制,其实在OC中对内存的管理是依赖对象引用计数器来进行的:在OC中对象的声明方式都是为一个指针分配对象堆空间。在每一个OC对象内部都有一个与之对应的整数变量(retainCount),叫做“引用计数器”,当一个对象在创建之后它的引用计数器值为1(OC中调用一个对象的方法就是给这个对象发送消息),当调用这个对象的release方法之后它的引用计数器减一,如果一个对象的引用计数器值为0,则系统就会自动调用这个对象的dealloc方法来销毁这个对象。
3 、(1)在OC中当仅仅声明一个对象指针而不进行alloc分配内存的话,那么这个对象指针的引用计数器的值retainCount=0;引用计数器就是一个名为retainCount的整型变量,查看方式为:[对象指针 retainCount];
(2)当声明一个对象并且用alloc分配内存init进行初始化后,retainCount的值为1,然后每调用一次retain 其引用计数器的值加一,每调用一次release方法 其retainCount的值减一,当理论上计数器的值为0时 系统就会调用对象的dealloc方法来对对象内存进行释放。
(3)当retain的release一定的次数使其理论值为0时系统虽然调用它的dealloc方法对对象的堆内存进行释放,但当释放内存后 调用输出它的引用计数器的值即retainCount还是为一,但只限且仅限一次也就是说释放内存后系统会给计数器一次输出机会但输出的值不为0 而是一。当输出一次之后,再调用引用计数器输出retainCount的值就会变成一个错误的大型数值,可以认为这个错误数字是个未知的地址。但是释放内存后如果没有给对象指针赋值为nil,再调用对象的任何成员及其方法都会造成错误。
(4)当对象指针调用足够次数的release方法激发dealloc方法释放内存后,若给对象指针赋值为nil。无论调用对象指针的任何成员以及任何方法包括(retain和release)方法都不会造成错误。但是都不再起任何的作用,因为它的内存已经释放并且被标示为空。此时调用它的任何setter方法或其他方法都不能操作数据,因为它的对象已经为空。此时输出它的任何整型数值都为0,任何别的类型值都为“ (null)”。
(5)如果对象指针没有调用足够的次数就不能激发对象的dealloc方法进行内存释放,若在指针所指向的对象空间没有被释放之前为为对象指针赋值为nil,虽然能够运行但是会造成内存泄露。无论什么情况下为对象指针赋值为nil,其情形都与(4)中相同。
(6)注意:如果对象指针以retain的方式赋值给另一个指针,内容上完全是指针值拷贝赋值,但是他们的引用计数器的变量retainCount的值会自增一。就等于说同一对象空间的地址给多个对象指针赋值,retain方式进行对象指针赋值等于把同一对象绑定多个指针,有任何一个指针没有解除绑定系统就不会调用dealloc方法来释放对象内存。引用计数器的变量retainCount存的就是绑定对象的指针个数。切记:retainCount是这些指针共享的一个变量,也就是说同一对象以retain赋值绑定的任何指针调用引用计数器时,都是同一个retainCount,所以值都是相同的。
4、已知在@property和@synthesize配套生成成员的setter/getter 方法中,就有retain和copy的用法,切记retain是针对于一般的Object(对象)的,而copy(分配新堆首地址且所指向堆的内容完全拷贝)是针对于NSString字符串对象的。假设一个复合类Person类中有一个成员“NSString *name;”和另一个自定义的普通类"Computer *c;"作为成员。
自动生成setter/getter方法时就需要用以下语句:
@property(nonatomic,copy)Nsstring * name;
@property(nonatomic,retain)Computer *c;
----------------------------------------------------
@synthesize name,c;
如果要手动提供代码,不使用@自动生成set/get方法时,就等于以下代码:
-(void)setName:(NSString *) _name;
-(NSString *)name;
-(void)setC:(Computer *)_c;
-(Computer *)c;
----------------------------------------------------
-(void)setName:(NSString *)_name
{
if(_name!=name){
[name release];
name=[_name copy];
}
}
-(NSString *)name
{
return name;
}
-(void)setC:(Computer *)_c
{
if(c!=_c){
[c release];
c=[_c retain];
}
}
-(Computer *)c
{
return c;
}
注意:(一) 如果没有判断防止自身赋值的if语句,很肯能在第一句release后就把自身内存释放了,两个相同的空指针是无法完成retain操作的。(二) 如果没有release语句,若原先成员指向的有堆空间的话直接赋值就会造成原先的内存泄露。(三) 即使成员指针没有指向堆内存,仅仅对定义的指针release也不会出错,因为未初始化之前,它是nil值。

代码验证一 对retain和release以及retainCount进行验证 ,实例如下:
新建Person.h 编辑代码如下:
//
//  Person.h
//  Person
//
//  Created by apple on 15/8/11.
//  Copyright (c) 2015年 liu. All rights reserved.
//

#import <Foundation/Foundation.h>
@interface Person:NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) int age;
@end

在Person.m文件中编辑代码如下:
//
//  Person.m
//  Person
//
//  Created by apple on 15/8/11.
//  Copyright (c) 2015年 liu. All rights reserved.
//

#import "Person.h"

@implementation Person
@synthesize age,name;//不加@synthesize也不会出错
-(id)init    //在此覆盖重写了父类初始化成员的默认构造方法
{   if(self=[super init]){
    NSLog(@"构造函数被调用了,当前引用计数:%lu",[self retainCount]);
}
    return  self;
}
-(void)dealloc{
    NSLog(@"Invoke Person's dealloc method");
    [super dealloc];/*注意最后一定要调用父类的dealloc方法(
        两个目的:一是父类可能有其他的引用对象要释放
    二是:当前对象的真正释放操作是在super的dealloc中完成的)*/
}
@end
在main.m中调用如下:
//
//  main.m
//  Person
//
//  Created by apple on 15/8/11.
//  Copyright (c) 2015年 liu. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Person.h"

void Test1()
{
    Person *p;
     NSLog(@"alloc之前仅仅声明  retaincount=%lu",[p retainCount]);// 未初始化对象指针之前,仅仅声明计数器值为0
    p=[[Person alloc] init];
    p.name=@"liuxun";
    p.age=22;
    NSLog(@"alloc init后 retaincount=%lu",[p retainCount]); //用alloc初始化对象之后,计数器的值为1
   //结果:retaincount=1
    [p retain]; //引用计数器+1
    NSLog(@"retain一次后 retaincount=%lu",[p retainCount]);   //结果:retaincount=2
    [p release]; //调用一次release引用计数器-1
    NSLog(@"release一次后 retaincount=%lu",[p retainCount]);  //结果:retaincount=1
    [p release];   //结果: Invoke Person's dealloc method,说明release理论值为0时  释放对象
   NSLog(@"再release一次后 retaincount=%lu",[p retainCount]); //即使对象被释放了,若不赋nil值计数器仍为1,仅仅一次调用值为1
  // NSLog(@"释放对象后 retaincount=%lu",[p retainCount]);//虽然被释放后计数器值为1,但是如果再调用计数器就是个野地址值
   //NSLog(@"after dealloc age=%d",[p age]);//对象释放后不管调用任何成员,都将出现错误
    p=nil;
     NSLog(@"retaincount=%lu",[p retainCount]); //为指针赋值为nil后计数器值为0
    [p setAge:12];
    [p setName:@"hujintao"];
    NSLog(@"%d %@\n",[p age],p.name);
    [p release];
    //释放对象并为指针赋值为nil后,再调用任何成员变量或成员函数都不会出错,即使是release也不会出错,但无法完成任何操作。调用setter方法无法赋值,getter方法输出的数值类型为0,其他类型为(null)值。
}
void Test2()
{
    NSLog(@"================================");
    Person *p2=[[Person alloc] init];
    [p2 retain];
    NSLog(@"retaincount=%lu",[p2 retainCount]);
    p2=nil;//如果不调用足够的release次数激发dealloc进行对象的释放就为指针赋值为nil会造成内存泄露,一旦指针被赋值为nil,就被标识为空指针不再绑定任何内存,调用任何成员或方法都不会出错,但是都与上面相同不会产生任何作用。
    NSLog(@"retaincount=%lu",[p2 retainCount]);
    p2.age=12;
    p2.name=@"liu ju";
    [p2 retain];
    NSLog(@"retaincount=%lu",[p2 retainCount]);
    NSLog(@"%d %@\n",[p2 age],p2.name);
    [p2 release];
}
int main(int argc, const char * argv[])
{

    @autoreleasepool {
        Test1();
        Test2();
    }
    
    return 0;
}

运行结果如下:

=================================================================================

复杂情况(复合类)之 代码验证二:
新建并编辑Shoe.h 代码如下:
//
//  Shoe.h
//  shoe1
//
//  Created by apple on 15/8/11.
//  Copyright (c) 2015年 liu. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Shoe : NSObject
{
    NSString *  _shoeColor;
    int  _shoeSize;
}
@property(nonatomic,assign) int shoeSize;
@property(nonatomic,copy) NSString *  _shoeColor;
-(void)setColorAndSize:(NSString *) pColor shoeSize:(int) pSize;
@end

编辑Shoe.m文件 代码如下:
//
//  Shoe.m
//  shoe1
//
//  Created by apple on 15/8/11.
//  Copyright (c) 2015年 liu. All rights reserved.
//

#import "Shoe.h"

@implementation Shoe
@synthesize shoeSize,_shoeColor;
//构造函数
-(id)init
{
    if(self=[super init]){
        _shoeColor=@"black";
        _shoeSize=35;
    }
    NSLog(@"一双%@  %d码的鞋子造好了",_shoeColor,_shoeSize);
    return self;
}
-(void)setColorAndSize:(NSString *) color shoeSize:(int) size
{
    self._shoeColor=color;
    self.shoeSize=size;
}
//析构函数
-(void)dealloc
{
    NSLog(@"%@ %d码的鞋子正在被人道销毁",_shoeColor,_shoeSize);
    [super dealloc];
}
@end

新建Man.h并编辑代码如下:
//
//  Man.h
//  shoe1
//
//  Created by apple on 15/8/11.
//  Copyright (c) 2015年 liu. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Shoe.h"
@interface Man : NSObject
{
    NSString *_name;
    Shoe *_shoe;
}
-(void)setName:(NSString *)name;
-(NSString *)Name;
-(void)wearShoe:(Shoe *)shoe;//或者@property(nonatomic,retain)_shoe;
@end

编辑Man.m文件 代码如下:
//
//  Man.m
//  shoe1
//
//  Created by apple on 15/8/11.
//  Copyright (c) 2015年 liu. All rights reserved.
//

#import "Man.h"

@implementation Man
-(id)init
{
    if(self=[super init]){
        _name=@"no name";
    }
    NSLog(@"新人\"%@\"出生了",_name);
    return  self;
}
-(void)setName:(NSString *)newName{
    if (_name!=newName) {
        [_name release];
        _name=[newName copy];
    }
}
-(NSString *)Name{
    return _name;
}
-(void)wearShoe:(Shoe *)shoe{
//    _shoe=shoe;//使用默认赋值方式assign的情况下
    if (_shoe!=shoe) {
        [_shoe release];
        _shoe=[shoe retain];
    }
}
//析构函数
-(void)dealloc{
    NSLog(@"\"%@\"要死了",_name);
    [_shoe release];
    [super dealloc];
}
@end
在main.m文件调用如下:
//
//  main.m
//  shoe1
//
//  Created by apple on 15/8/11.
//  Copyright (c) 2015年 liu. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Man.h"
int main(int argc, const char * argv[])
{

    @autoreleasepool {
        Man *jimy=[Man new];
        [jimy setName:@"Jimy"];
        Shoe *black40=[Shoe new];
        [black40 setColorAndSize:@"black" shoeSize:40];
        [jimy wearShoe:black40];
        
        Man *mike = [Man new];
        [mike setName:@"mike"];
        [mike wearShoe:black40];//mike跟jimmy,现在共同拥有一双40码黑色的鞋子
        [black40 release];
        [jimy release];
       [mike release];
        
    }
    return 0;
}

运行结果如下:





























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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值