C20_OC10-内存管理


每个对象都有一个隐式的int类型计数器,占4个字节内存。对象诞生(使用allocnewcopy创建对象)时默认计数为1;每增加一个调用,计数器加1;当计数器为0时,对象销毁,内存回收。
retain 计数器+1
release  计数器-1
retainCount 计数器值
销毁方法:dealloc。如果计数器值为0,则调用此方法销毁对象。
如果重写此方法,则最后须调用[super dealloc];

1.retainreleaseretainCount

#import <Foundation/Foundation.h>

@interface Persion : NSObject
@end

@implementation Persion
    //当retainCount为0,OC调用此方法回收内存
    - (void)dealloc{
        NSLog(@"注销");
        
        [super dealloc];
    }
@end

void test();
void test1();

int main()
{
    test();
    return 0;
}

void test1(){
    Persion * p = [Persion new];   // [[Persion alloc] init];
    NSLog(@"计数器a:%ld", [p retainCount]);    // 初始为1
    
    [p retain];     // 计数器+1
    NSLog(@"计数器b:%ld", [p retainCount]);    // 2
    
    [p release];    // 计数器-1
    NSLog(@"计数器c:%ld", [p retainCount]);    // 1
    
    [p release];    /** 变成僵尸对象 **/

    p = nil;        /** 转为空对象 **/
    
    /** OC中没有空指针错误,这一行仅仅是一行无意义的文本 **/
    NSLog(@"计数器d:%ld", [p retainCount]);}

void test(){
    Persion * p = [Persion new];
    NSLog(@"计数器a:%ld", [p retainCount]);    // 初始为1
    
    [p retain];     // +1
    NSLog(@"计数器b:%ld", [p retainCount]);    // 2
    
    [p release];    // -1
    NSLog(@"计数器c:%ld", [p retainCount]);    // 1
    
    /** retainCount为0,对象变成僵尸对象 **/
    [p release];    // -1
    
    /**
     野指针错误:EXC_BAD_ACCESS:坏内存、已被回收内存访问错误。
     操作僵尸对象报错:EXC_BREAKPOINT,message sent to deallocated instance **/
    NSLog(@"计数器d:%ld", [p retainCount]);
}

2.set方法与内存管理

1)汽车

/*
	文件:Car.c
	项目:dealloc

	作者:vigiles
	日期:14-5-8
	  Copyright (c) 2014年 beardap. All rights reserved.
*/
#import <Foundation/Foundation.h>

@interface Car : NSObject
    {
        NSString * _name;
    }

    -(void)setName:(NSString * )name;
    -(NSString*)name;

@end
--------------------------------------------------------------
/*
	文件:Car.m
	项目:dealloc

	作者:vigiles
	日期:14-5-8
	  Copyright (c) 2014年 beardap. All rights reserved.
*/
#import "Car.h"

@implementation Car
    -(void)setName:(NSString * )name{
        if (_name != name) {
            [_name dealloc];
            _name = [name retain];
        }
    }
    -(NSString*)name{
        return _name;
    }
    -(void)dealloc{
        [_name release];
        [super dealloc];
        NSLog(@"Car释放");
    }

    -(NSString *)description{
        return [NSString stringWithFormat:@"%@", _name];
    }
@end

2)宠物

/*
	文件:Pet.h
	项目:dealloc

	作者:vigiles
	日期:14-5-8
	  Copyright (c) 2014年 beardap. All rights reserved.
*/

#import <Foundation/Foundation.h>

@interface Pet : NSObject

    // 给@propert添加retain参数
    @property (retain) NSString * name;
@end
--------------------------------------------------------------
/*
	文件:Pet.m
	项目:dealloc

	作者:vigiles
	日期:14-5-8
	  Copyright (c) 2014年 beardap. All rights reserved.
*/

#import "Pet.h"

@implementation Pet
    @synthesize name;

    // 因为声明中添加了retain参数
    /* 自动实现的set方法会增加引用计数代码。如:
    - (void)setName:(NSString *)name{
        if (self->name != name) {
            [self->name retain];
            self->name = name;
        }
        
    }*/

    - (void)dealloc {
        [name release];
        
        NSLog(@"Pet释放了");
        
        [super dealloc];
    }

    -(NSString *)description{
        return [NSString stringWithFormat:@"%@", name];
    }
@end

3)人类

/*
	文件:Persion.h
	项目:dealloc

	作者:vigiles
	日期:14-5-8
	  Copyright (c) 2014年 beardap. All rights reserved.
*/

#import <Foundation/Foundation.h>
#import "Car.h"
#import "Pet.h"

typedef struct {
    int year;
    int month;
    int day;
} Date;

@interface Persion : NSObject
    {
        int _age;
        NSString * _name;   // 手动管理内存时,OC对象数据类型的属性,务必在dealloc中release
        Car * _car;         // 当对象dealloc时,属性须release
    }

    /**
     使用@property声明的属性,会自动隐式的创建setter和getter。
     @property也可配置参数,有4种:
     1.与隐式setter方法中,对象引用相关的参数
        1)retain:release旧值;retain新值。适用于OC对象类型。
        2)assign:直接赋值;默认。适用于基本数据类型
        3)copy
     
     2.生否隐式创建setter
        1)readwrite:读写;默认。
        2)readonly:只读,仅生成getter
     
     3.多线程管理
        1)nonatomic:性能高,建议!非线程安全
        2)atomic:性能低;默认。线程安全。某时刻,只能有一个线程进行setter操作
     
     4.setter和getter的名称(别名)
        1)setter:修改默认set方法名,这个set方法务必有“:”冒号。因为set方法获取参数需要:分隔,如:
            @property(setter = doSet:) int age;  
            //以后使用不是[Obj setAge]; 而是[Obj doSet:199393];
        2)getter:修改默认get方法名,如:
            @property(getter = doGet) int hight;  
            //以后使用不是[Obj hight]; 而是[Obj doGet];
    **/
    // OC对象数据,管理引用,非线程安全
    @property (retain, nonatomic) Pet * pet;   // 宠物。当对象dealloc时,属性须release

    // 非对象数据,直接赋值,非线程安全,set方法别名,get方法别名
    @property (assign, nonatomic, setter = borning:, getter = borned) Date birthday;

    -(void)setAge:(int)age;
    -(int)age;

    -(void)setName:(NSString *)name;
    -(NSString *)name;

    -(void)setCar:(Car *)car;
    -(Car *)car;
@end
-------------------------------------------------------------------------
/*
	文件:Persion.m
	项目:dealloc

	作者:vigiles
	日期:14-5-8
	  Copyright (c) 2014年 beardap. All rights reserved.
*/
/**
 手动管理内存条例:
 1.只要调用了alloc,必须配对release(或autorelease)
 **/

#import "Persion.h"
#import "Car.h"

@implementation Persion

    /** 手动声明的set、get方法,须手动实现 **/
    -(void)setAge:(int)age{
        _age = age;
    }
    -(int)age{
        return _age;
    }

    // set方法操作OC对象时须要手动管理内存
    -(void)setName:(NSString *)name{
        NSLog(@"setName一次");
    
        // 如果传人的新对象和现有的原对象不是同一个,则更新
        if (_name != name) {
            // 原对象减少引用
            [_name release];
            NSLog(@"_name释放一次");
            
            // 新对象增加引用
            _name = [name retain];
            NSLog(@"_name更新一次");
        }
    }
    -(NSString *)name{
        return _name;
    }

    -(void)setCar:(Car *)car{
        if (_car != car) {
            [_car release];
            _car = [car retain];
        }
    }
    -(Car *)car{
        return _car;
    }

    /** 根据@property的参数隐式的自动实现set、get方法 **/
    @synthesize pet;        //既然指定了,那就使用这个没有下划线的属性
    //@synthesize birthday; //没有这一句,默认操作的属性是_birthday,带下划线。

    /** 当前对象的引用为0,销毁时 **/
    -(void)dealloc{
        // 1)对self拥有的全部对象属性做一次release
        [_name release];
        [_car release];
        [pet release];
        
        // 2)一定要最后[super dealloc];
        [super dealloc];
        
        NSLog(@"Persion回收了");
    }

    -(NSString *)description{
        return [NSString stringWithFormat:@"姓名:%@,年龄:%d,汽车:%@,宠物:%@,生日:%d", _name, _age, _car, pet, _birthday.year];
    }
@end

4main

/**
    手动管理内存条例:
    1.只要调用了alloc,必须配对release(或autorelease)
        没有alloc,就没有release
    2.set
        1)基本数据类型无需release
        -(void)setAge:(int)age{
            _age = age;
        }
        2)OC对象类型
        -(void)setCar:(Car *)car{
            (1)须首先判断是否传人的新对象
            if(car != _car){
                (2)把就对象release
                [_car release];
                (3)对新对象retain
                _car = [car retain];
            }
        }
    3.dealloc
        -(void)dealloc{
            1)对self拥有的全部对象属性做一次release
            [_car release];
            2)一定要最后[super dealloc];
            [super dealloc];
        }
 **/

#import <Foundation/Foundation.h>
#import "Persion.h"
#import "Car.h"

int main()
{
    // 只要调用了alloc,必须配对release(或autorelease)
    Persion * p = [[Persion alloc] init];
    
    Date bir = {1987, 3, 1};
    //p.birthday = bir;
    [p borning:bir];
    
    p.name = @"Eminem";     // 特殊情况,尽管是set方法,这个格式创建的NSString对象自动release
    
    NSString * name = @"Alizee";
    p.name = name;          // 没有alloc ,就没有release
    p.name = @"Alizee";
    
    // 只要调用了alloc,必须配对release
    Car * c = [[Car alloc] init];
    c.name = @"甲壳虫";
    p.car = c;
    
    //p.car = [[Car alloc] init];   //内存不能释放
    
    Pet * pet = [[Pet alloc] init];
    pet.name = @"加菲";
    p.pet = pet;
    
    NSLog(@"%@", [p description]);
    
    [pet release];
    [c release];
    
    [p release];    // Persion回收了
    
    return 0;
}


3.@class与对象的双向引用

1Persion

/*
 文件:Persion.h
 项目:dealloc
 
 作者:vigiles
 日期:14-5-8
 Copyright (c) 2014年 beardap. All rights reserved.
 */

#import <Foundation/Foundation.h>

/** 这里仅说明有一个Car类,没有引入 **/
@class Car;

@interface Persion : NSObject
@property(nonatomic, retain)NSString * name;

// 有属性-汽车
// 这里声明引用其它类的属性为 retain,那么对方的引用属性就为 assign
@property(nonatomic, retain)Car * car;
@end
---------------------------------------------------------------------------
/*
	文件:Persion.m
	项目:dealloc

	作者:vigiles
	日期:14-5-8
	  Copyright (c) 2014年 beardap. All rights reserved.
*/

#import "Persion.h"

/** 在这里引入(包含)属性所属类的头文件 **/
#import "Car.h"

@implementation Persion
    -(void)dealloc{
        // 在.h里配置属性为 retain,所以需要release
        [_car release];
        
        [super dealloc];
        NSLog(@"Persion释放");
    }
@end

2Car

/*
	文件:Car.h
	项目:dealloc

	作者:vigiles
	日期:14-5-8
	  Copyright (c) 2014年 beardap. All rights reserved.
*/

#import <Foundation/Foundation.h>

/** 这里仅说明有一个Persion类,没有引入 **/
@class Persion;

@interface Car : NSObject
    @property (nonatomic, retain) NSString * name;

    // 有属性-人类
    /** 一方配置retain,则另一方配置 assign ! **/
    @property (nonatomic, assign) Persion * persion;
@end
---------------------------------------------------------------------
/*
	文件:Car.m
	项目:dealloc

	作者:vigiles
	日期:14-5-8
	  Copyright (c) 2014年 beardap. All rights reserved.
*/

#import "Car.h"

/** 在这里引入(包含)属性所属类的头文件 **/
#import "Persion.h"

@implementation Car
    -(void)dealloc{
        /** 因为这个属性在.h中声明为assign,不release **/
        //[_persion release];
        
        [super dealloc];
        NSLog(@"Car释放");
    }
@end

3main

#import <Foundation/Foundation.h>
#import "Persion.h"
#import "Car.h"

int main()
{
    Persion * p = [[Persion alloc] init];
    Car * c = [[Car alloc] init];
    
    p.car = c;      // 配置双向关联
    c.persion = p;  // 配置双向关联
    
    [c release];
    [p release];
    
    return 0;
}


4.autorelease

1Person

/*
	文件:Person.h
	项目:dealloc

	作者:vigiles
	日期:14-5-8
	  Copyright (c) 2014年 beardap. All rights reserved.
*/

#import <Foundation/Foundation.h>

@interface Person : NSObject
    @property(nonatomic, assign) int age;

    +(id)person;
    +(id)personWithAge:(int)age;
@end
-----------------------------------------------------------------------
/*
	文件:Persion.m
	项目:dealloc

	作者:vigiles
	日期:14-5-8
	  Copyright (c) 2014年 beardap. All rights reserved.
*/

#import "Person.h"

@implementation Person
    // 没有使用@synthesize指定属性,所以隐式操作_age对应age。

    // 仿Java创建对象的方式
    +(id)person{
        // 对象只能autorelease一次。alloc必须对应一个release或autorelease
        
        // 这一句,无论是Person还是子类,都返回Person对象
        //return [[[Person alloc] init] autorelease];
        
        // 如果是Person调用,返回Person对象;如果是Student调用,返回Student对象
        return [[[self alloc] init] autorelease];   // self 表示调用这个方法的类
    }

    // 仿Java重载构造方法。创建对象时初始化一个属性
    +(id)personWithAge:(int)age{
        //Person * p = [Person person];
        Person * p = [self person];
        p.age = age;
        return p;
    }

    -(void)dealloc{
        NSLog(@"Person,年龄:%d,注销", _age);
        [super dealloc];
    }
@end

2Student

/*
	文件:Student.h
	项目:dealloc

	作者:vigiles
	日期:14-5-9
	  Copyright (c) 2014年 beardap. All rights reserved.
*/

#import "Person.h"

@interface Student : Person
    @property (nonatomic, assign) int height;
    @property (nonatomic, retain) NSString * name;
@end
---------------------------------------------------------------------------
/*
	文件:Student.m
	项目:dealloc

	作者:vigiles
	日期:14-5-9
	  Copyright (c) 2014年 beardap. All rights reserved.
*/

#import "Student.h"

@implementation Student
    -(void)dealloc{
        [_name release];
        
        [super dealloc];
        
        NSLog(@"Student,姓名:%@,高:%d,注销", _name, _height);
    }
@end

3main

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

int main()
{
    // 内存自动释放池
    @autoreleasepool {
        /** 对象只能autorelease一次 **/
        // Person * p = [[[Person alloc] init] autorelease];
        // 使用自定义的类方法创建对象
        Person * p = [Person person];
        
        p.age = 993;
        
        //这一句不能有
        //[p release];  //池内的对象不需要release
        
        /** 自动释放池在栈里,后进先出,可以随意创建,不限数量 **/
        @autoreleasepool {
            Person * p2 = [Person personWithAge:34];
            
            /** 一些系统自动的类使用时都没有alloc、new、copy。对象都是默认autorelease,不要release **/
            NSString * str = [NSString stringWithFormat:@"p2.age:%d", p2.age];
            
            NSLog(@"%@", str);
        }
        
        // 继承自Person,继承了仿Java构造方法创建对象
        Student * s = [Student person];
        
        /** 
         当Person中的person方法里,return [[[Person alloc] init] autorelease] 时:
            报错:signal SIGABRT。
         因为[xxx person]返回的是Person对象,其没有height这个属性 **/
        s.height = 200;     
        
        // 不用release
        
        Student * s2 = [Student personWithAge:12];
        s2.name = @"三好"; //没有报错:signal SIGABRT。
        s2.height = 90;
    }
    
    return 0;
}


5.ARC自动内存管理机制

编译器编译时,自动根据alloc代码插入release代码。和JavaGC内存回收机制完全不同,GC是在运行阶段自动回收内存。XCode5.1IOS7默认的。

1)强指针与弱指针

#import <Foundation/Foundation.h>

@interface Person : NSObject
@end

@implementation Person
    -(void)dealloc{
        NSLog(@"Person,注销");
    
        // ARC禁止手动调用super的dealloc
        //[super dealloc];
    }
@end

int main()
{
    // 因为使用了ARC,不再需要创建自动释放池
    // @autoreleasepool{}
    
    //Person * p = [[[Person alloc] init] autorelease]; // ARC禁用autorelease
    Person * p = [[Person alloc] init];
    
    /** 只要没有 强指针 指向对象,ARC机制就会释放对象
            默认情况,任何指针都是强类型的 __strong
            弱类型指针,需加前缀 __weak
    **/
    p = nil;    //添加断点
    
    NSLog(@"添加断点,测试对象释放");  //添加断点
    
    Person * p2 = [Person new];
    
    __weak Person * p3 = p2;
    
    p2 = nil;   // 在这里Person对象已经销毁了,因为对象是否存在只考虑强指针的感受
    
    p3 = nil;   // 弱指针很悲催,如果对象已经不存在,弱指针会被自动销毁
    
    //[p retain];           // ARC禁止手动retain
    //[p retainCount];      // ARC禁止手动retainCount
    //[p release];          // ARC禁止手动release
    
    /** 无意义的 **/
    __weak Person * p4 = [Person new];  // 对象创建即销毁。因为没有强指针指向它
    
    return 0;
}

2@property参数

#import <Foundation/Foundation.h>

@implementation Dog : NSObject
    -(void)dealloc{
        NSLog(@"Dog,注销");
    }
@end

@interface Person : NSObject
    // 声明强指针类型的属性。strong:强类型;weak:弱类型。这里没有下划线
    // strong、weak仍然是针对OC对象数据
    // strong作用就如同以前使用的retain;weak作用如同assign
    @property (nonatomic, strong) Dog * dog;
    @property (nonatomic, weak) Dog * dog2;
@end

@implementation Person
    -(void)dealloc{
        NSLog(@"Person,注销");
    }
@end

int main()
{
    Person * p = [Person new];
    
    Dog * d = [Dog new];
    
    p.dog = d;
    
    // 因为p属性dog的强引用,这一步d不会被回收
    d = nil;    // 添加断点
    
    NSLog(@"p.dog:%@", p.dog);  // p.dog:<Dog: 0x1002003d0>
    
    // Person对象被回收后,Dog对象才被回收
    p = nil;   // 加断点
    
    // --------------------------------------
    
    Person * p2 = [[Person alloc] init];
    Dog * dog2 = [Dog new];
    p2.dog2 = dog2; // 弱指针类型的属性
    dog2 = nil;     // 唯一一个强引用销毁,这里Dog对象就被注销回收了
    NSLog(@"p.dog2:%@", p.dog2);    // p.dog2:(null)
    p2 = nil;
    
    return 0;
}

3)非ARC项目转ARC项目

EditRefactorConvert to Objective-C ARC... 

4ARC项目中配置非ARC代码

项目创建后默认是ARC机制,但有时导入的一些框架是非ARC的,需要配置项目其允许手动管理内存。



5ARC中对象的双向引用

#import <Foundation/Foundation.h>

@class Dog;

@interface Person : NSObject
    /** ARC中的双向引用和非ARC时是一样的,一方声明为strong,另一方就得声明为weak **/
    @property (nonatomic, strong) Dog * dog;
@end

@implementation Person
    -(void)dealloc{
        NSLog(@"Person,注销");
    }
@end

@interface Dog : NSObject
    /** ARC中的双向引用和非ARC时是一样的,一方声明为strong,另一方就得声明为weak **/
    @property (nonatomic, weak) Person * person;
@end

@implementation Dog
    -(void)dealloc{
        NSLog(@"Dog,注销");
    }
@end

int main()
{
    Person * p = [Person new];
    Dog * d = [Dog new];
    p.dog = d;
    d.person = p;
  
    return 0;
}

- end

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值