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];
    
}
复制代码

 


本文转自农夫山泉别墅博客园博客,原文链接:http://www.cnblogs.com/yaowen/p/5314499.html,如需转载请自行联系原作者

相关文章
|
14天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
40 4
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
68 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
Java 测试技术 Android开发
让星星⭐月亮告诉你,强软弱虚引用类型对象在内存足够和内存不足的情况下,面对System.gc()时,被回收情况如何?
本文介绍了Java中四种引用类型(强引用、软引用、弱引用、虚引用)的特点及行为,并通过示例代码展示了在内存充足和不足情况下这些引用类型的不同表现。文中提供了详细的测试方法和步骤,帮助理解不同引用类型在垃圾回收机制中的作用。测试环境为Eclipse + JDK1.8,需配置JVM运行参数以限制内存使用。
32 2
|
1月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
56 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
1月前
|
存储 Java
深入理解java对象的内存布局
这篇文章深入探讨了Java对象在HotSpot虚拟机中的内存布局,包括对象头、实例数据和对齐填充三个部分,以及对象头中包含的运行时数据和类型指针等详细信息。
28 0
深入理解java对象的内存布局
|
1月前
|
存储 编译器 C++
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作(二)
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作
|
1月前
|
算法 Java
JVM进阶调优系列(3)堆内存的对象什么时候被回收?
堆对象的生命周期是咋样的?什么时候被回收,回收前又如何流转?具体又是被如何回收?今天重点讲对象GC,看完这篇就全都明白了。
|
1月前
|
存储 编译器 C++
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作(三)
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作
|
1月前
|
存储 编译器 C++
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作(一)
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作
|
3月前
|
存储 Java 程序员
Java中对象几种类型的内存分配(JVM对象储存机制)
Java中对象几种类型的内存分配(JVM对象储存机制)
86 5
Java中对象几种类型的内存分配(JVM对象储存机制)