zoukankan      html  css  js  c++  java
  • oc-31-多对象的内存管理

    在每个OC对象内部,都专门有8个字节的存储空间来存储引用计数器。
    引用计数器的常见操作
    retain消息:堆内存中对象的计数器变量 +1(该方法返回对象本身,要想计数器变量加1就要调用对象的retain方法)
    release消息: 堆内存中对象的计数器变量 -1(不代表释放对象,要想计数器变量减1就要调用对象的release方法)
    retainCount:获得对象当前的应用计数器值,输出:%ld %lu,NSLog(@"%lu",h.retainCount);
    注意:release不代表销毁对象,仅仅是引用计数器-1
    
    2.谁创建,谁release
    
    1)如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者 autorelease方法.
    2)不是你创建的就不用你去负责
    3.谁retain,谁release
    
    只要你调用了retain,无论这个对象时如何生成的,你都要调用release
    
    5)对象的销毁
    
    当1个对象的应用计数器为0时,那么它将被销毁,其占用的内存被系统回收。
    当对象被销毁时,系统会自动向对象发送一条dealloc消息。一般会重写dealloc方法,在这里释放相关的资源,dealloc就像是对象的“临终遗言”。
    一旦重写了dealloc方法,就必须调用[super dealloc],并且放在代码块的最后调用。
    注意:不能直接调用dealloc方法。
    一旦对象被回收了,那么他所占用的存储空间就不再可用,坚持使用会导致程序崩溃(野指针错误)
    
    注意;
    
    1) 如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收(除 非整个程序已经退出 )
    2)任何一个对象,刚生下来的时候,引用计数器都为1。(对象一旦创建好,默认引用计数器就是1)当使用alloc、new或者copy创建一个对象时,对象的引用计数器默认就是1.
    
    2、内存管理的分类
    
    Objective-C提供了三种内存管理方式:
    
    1.MannulReference-Counting(MRC,手动管理,在开发iOS4.1之前的版本的项目时我们要自己负责使用引用计数来管理内存,比如要手动 retain、release、autorelease 等,而在其后 的版本可以使用 ARC,让系统自己管理内存。)
    2.automatic reference-counting(ARC,自动引用计数,iOS4.1之后推出的)
    3.garbage collection(垃圾回收)。iOS不支持垃圾回收;
    
    ARC作为苹果新提供的技术,苹果推荐开发者使用ARC技术来管理内存;
    
    开发中如何使用:需要理解MRC,但实际使用时尽量用ARC。

    Gamer.h

    #import <Foundation/Foundation.h>
    #import "House.h"
    
    @interface Gamer : NSObject
    {
        House *_house; // 房间,游戏玩家在哪里房间
    }
    - (void)setHouse:(House *)house;
    - (House *)house;
    //@property House *house;
    @end

    Gamer.m

    #import "Gamer.h"
    
    @implementation Gamer
    - (void)dealloc
    {
        NSLog(@"玩家被释放");
        // 当玩家自己被回收时,对房间进行一次release操作减少堆内存中对象的计数器变量值.
        [_house release];
        [super dealloc];
    }
    
    - (void)setHouse:(House *)house
    {
        if (_house != house) {
            //当玩家换房间时,需要对旧房间做一次release操作减少堆内存中对象的计数器变量值
            [_house release];
            // 玩家要进入房间,玩家就要对房间进行一次retain操作增加堆内存中对象的计数器变量值.
            _house = [house retain];
        }
    }
    - (House *)house
    {
        return _house;
    }
    @end
    
    
    
    /*
     // 1、创建玩家对象
     Gamer *game = [[Gamer alloc] init];
     // 2、创建房间对象
     Room *room = [[Room alloc] init];
     // 3、让玩家进入房间
     game.room = room;//堆中对象的计数器变量没有加1因为没有调用retain方法,
     // 4、释放房间对象
     [room release];
     // 5、输出玩家的房间
     NSLog(@"玩家的房间是:%@",game.room);
     此时,会报野指针错误。
    
    解决方案:
    ,对房间进行一次retain。
     // 声明类
     @interface Gamer : NSObject
     {
     Room *_room;
     }
     - (void)setRoom:(Room *)room;
     - (Room *)room;
     
     // 实现类
     @implementation Gamer
     - (void)setRoom:(Room *)room
     {
     
     [room retain];//堆中对象的计数器变量加1后赋值
     _room = room;
     }
     - (Room *)room{
     return _room;
     }
     
     问题:房间无法被释放了怎么办?
     解决方法:
     在对象被释放时,要把该对象的所有对象类型的成员变量在dealloc当中进行释放:
     @implementation Gamer
     - (void)dealloc
     {
     [_room release];//堆中对象的计数器变量减1
     NSLog(@"玩家被释放");
     [super dealloc];
     }
     @end
     
     解决方案:判断新进房间与之前是否是同一个房间。
     - (void)setRoom:(Room *)room
     {
     if (_room != room) {
     // 释放旧房间
     [_room release];
     [room retain];//加1后赋值
     _room = room;
     
     }
     }
     
     总的来说,有以下几点规律:
     2)只要想要使用1个对象,那么就让对象的引用计数器+1。
     3)当不再使用这个对象时,就让对象的引用计数器-1。
     
     需求:
     1、让玩家换房间,进入1号房间后,再进入2号房间
     // 实现玩家类
     @implementation Gamer
     - (void)setRoom:(Room *)room
     {
     [_room release];//旧房间的计数器变量减1
     _room = [room retain];//新房间的计数器变量加1后赋值
     }
     - (Room *)room{
     return _room;
     }
     - (void)dealloc
     {
     [_room dealloc];
     NSLog(@"玩家被释放");
     [super dealloc];
     }
     @end
     int main(){
     // 1、创建玩家对象
     Gamer *game = [[Gamer alloc] init];
     // 2、创建房间对象
     Room *room = [[Room alloc] init];
     room.no = 1;
     Room *room1 = [[room alloc] init];
     room.no = 2;
     // 3、让玩家进入房间
     game.room = room;
     game.room = room1;
     // 4、释放房间对象
     [room release];
     // 5、释放玩家对象
     [game release];
     return 0;
     }
    
     总结:
     1、set方法的内存管理
     - (void)setRoom:(Room *)room
     {
     if(_room != room){
     [_room release];
     _room = [room retain];
     }
     }
     2、dealloc方法内存管理
     - (void)dealloc
     {
     // 当玩家不在了,代表不用房间了
     // 对房间进行一次release
     [_room release];
     // 重写dealloc方法,必须在最后调用父类的dealloc方法
     [super dealloc];
     }

    House.h

    #import <Foundation/Foundation.h>
    
    @interface House : NSObject
    @property int no;
    @end

    Huose.m

    #import "House.h"
    
    @implementation House
    - (void)dealloc
    {
        NSLog(@"%d房间被释放了",_no);
        [super dealloc];
    }
    @end

    main.m

    /**
     知识回顾:
     什么是内存管理:
     管理堆区的内存的分配和释放.
     分配内存:new alloc copy
     释放内存:release
     
     操作内存的方式:
     1)retainCount:获取对象的计数器的值
     2)retain:堆内存中对象的计数器变量 +1
     3)release:堆内存中对象的计数器变量  -1
     
     僵尸对象:已经被释放的对象
     野指针:指向僵尸对象的指针
     空指针:nil,给空指针发送消息不会报错.
     
     单个对象的内存管理
     Person *p = [[Person alloc] init]; // 计数器 1
     [p retain];  // 计数器 2
     [p retain];  // 3
     //想让p对象释放,怎么办?
     // 再做3次release
     [p release];
     [p release];
     [p release];
     
     一个对象是否被释放,通过什么方式确定?
     通过是否调用dealloc方法,在重写dealloc方法时,必须在最后的位置调用[super dealloc];
     
     
     多对象内存管理:
     1.模拟1个玩家进入1个房间的过程.
     分析:类:玩家,房间类,让玩家拥有一间房
     
     2.换房间的一个过程,玩家先进入10号房间,再进入20号房间.此时有什么问题?
     解决方法:
     在换房间时,需要release旧值,retain新值.
     
     */
    #import <Foundation/Foundation.h>
    #import "Gamer.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Gamer *g = [[Gamer alloc] init]; //g 1
            
            House *h = [[House alloc] init]; //h 1
            //g.house = h只是表示h变量多了一个引用的指向,[[House alloc] init]对象任然只有一个引用,因为没有调用retain方法所以计数器变量不会加1.
            // g.house调用了g的setHouse方法所以此时[[House alloc] init]对象的引用为2
            g.house = h;
            
            [h release];  // h 1
           
            g.house = h;  // 再重写进入房间
            
            NSLog(@"%lu",h.retainCount);
    
        }
        return 0;
    }
    // 换房间
    void demo()
    {
        Gamer *game = [[Gamer alloc] init]; // game :1
        
        House *house = [[House alloc] init]; // house :1
        house.no = 10;
        
        House *house2 = [[House alloc] init];
        house2.no = 20;
        
        House *house3 = [[House alloc] init];
        house3.no = 30;
        
        // 让人进入10号房
        game.house = house;
        
        // 让人进入20号房间
        game.house = house2;
        
        // 让人进入20号房间
        game.house = house3;
        
        
        // 释放房间
        [house release];
        [house2 release];
        [house3 release];
        [game release];
        
    }
  • 相关阅读:
    继承
    接口
    匿名内部类
    抽象类和接口的区别
    多态
    重载和覆写的区别|this和super区别
    Visual C# 2008+SQL Server 2005 数据库与网络开发――2.2.1 变量
    Visual C# 2008+SQL Server 2005 数据库与网络开发――2.3.1 选择语句
    Visual C# 2008+SQL Server 2005 数据库与网络开发―― 2.5错误和异常处理
    Visual C# 2008+SQL Server 2005 数据库与网络开发―― 2.3 语句
  • 原文地址:https://www.cnblogs.com/yaowen/p/5314499.html
Copyright © 2011-2022 走看看