OC内存管理01--对象的内存管理

什么是内存管理?

内存管理,顾名思义就是对系统的内存进行管理。那么就会有人有疑问了,为什么我们以前从来都没做个内存管理,为什么程序依旧可以运行呢?这就与我下面介绍的MRC与ARC有关了。


ARC - 自动引用计数机制(Automatic Reference Counting)

在oc进行对象创建的时候会自动创建一个用来管理对象的引用计数器,并且更具创建的关键字,自动将引用计数置数。它采用的是“语法糖”机制,即当创建一个对象时,将对象像“糖”一样“包”起来,即创建对象后自动在其后面添加释放语句。

而在我们平时编写代码时,编译器会默认采用ARC模式,所以程序会自动帮我们回收空间,不需要我们做内存管理。


MRC - 手动引用计数机制(Manual Reference Counting)

虽然ARC可以自动帮我们回收空间,但苹果的编译机制决定了ARC的编译存在了一定的问题,经常使得编译的程序在实体机上运行时出现系统崩溃的情况,这是由于某些实体机没有ARC机制或者过早释放导致的。而且,在ARC模式下的空间由于是aoturelease的,会导致程序在运行过程中所占用的内存越来越大,不仅占用系统资源,甚至可能导致系统卡死等情况出现。

MRC即通过手动编写代码的方式来释放程序内存,使得我们不用某些对象时可以及时释放空间,节约内存。


在介绍内存管理之前,我们需要先了解oc的存储机制,在oc中内存大概分为以下几个区域:

1、堆区:主要存储自定义的对象等,它是所有应用共享的一块内存空间。当使用它的时候系统会自动分配内存,但需要手动释放空间。
2、栈区:主要存储变量、指针等,当使用时系统会自动创建自动回收,无需手动释放。
3、常量存储区:主要存储系统中的常量,当使用时会自动访问,不能修改。
4、自由存储区:存储其他数据。
5、特殊变量存储区域:存储全局变量与静态变量,自动释放。




当每一个OC对象创建时,都会创建一个相应的引用计数器,引用计数置1


引用计数,即该对象被引用的次数,一般来说是指有几个指针访问着这块空间。当我们刚创建这个对象时,只有一个指针访问了这个对象所以引用计数置1。

在引用计数中,每一个对象负责维护对象所有引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到0时,该对象就将释放占有的资源。


在程序中我们可以通过retainCount来查看引用计数。

注:关键字:

alloc,new,copy为初始化一个对象,因此当使用这三个关键词创建对象时,对象的引用计数置1.
retain为使对象增加一个引用(指针),因此当使用retain时对象的引用计数+1.
release为释放对象的引用(指针),因此当对象release时对象引用计数-1.


那么如何进行内存管理呢?

如我们创建了一个对象

Person *person1 = [[Person alloc] init];
NSLog(@"Count = %ld", person1.retainCount);

这时Person的引用计数为1
Count = 1

当我们不需要这个对象时就需要释放这个对象,在oc中我们使用关键字release释放对象的引用,这个对象引用为一因此只需要释放一次就可以了。

为了观察对象释放是否释放,我们需要在对象里重写dealloc方法,该方法是对象引用计数为0时自动走该方法释放对象空间。

Person:

@interface Person : NSObject<NSCopying,NSMutableCopying>

@property (nonatomic, retain)NSString *name;
@property (nonatomic, assign)NSInteger age;
@property (nonatomic, assign, getter=isLogin)BOOL login;
@property (nonatomic, copy)NSString *coString;

@end

dealloc:

- (void)dealloc{
    
    NSLog(@"Person dealloc");
    [super dealloc];
    [_name release];
    [_coString release];
    
}
在主函数中执行下列代码:

Person *person1 = [[Person alloc] init];
NSLog(@"Count = %ld", person1.retainCount);
[person1 release];

我们会发现运行后控制台显示如下:

 Count = 1
 Person dealloc

如果我们再次调用release时系统就会通知你该空间已被释放

overreleased while already deallocating; break on objc_overrelease_during_dealloc_error to debug

我们会发现当我们对person1进行操作输出时,系统仍然可以正常执行而且 person1.retainCount为1

然而,当我们多次运行后,就会程序崩溃的现象。

出现这样现象的原因是指针未能释放的问题

我们发现虽然对象的空间释放了,但指向对象的指针没有释放,所以当我们访问指针时他会随机访问系统区域中的某一块区域,这样的指针我们称之为“野指针”。

所以当我们如下操作时:

Person *person1 = [[Person alloc] init];
NSLog(@"Count = %ld", person1.retainCount);
[person1 release];
        
person1.name = @"Flack";
NSLog(@"%@", person1.name);
person1指针会访问内存的某一块区域,当存在访问的值时系统就可以正常运行,但当它访问不到该属性使系统就会崩溃,并显示如下信息:

EXC_BAD_ACCESS(code=1, address=0x18)
总的来说,该内存已经释放而指针仍然存在造成了这个错误。

在程序中我们可以通过关键字retain来增加对象的引用计数,如:

Person *person1 = [[Person alloc] init];
NSLog(@"Count = %ld", person1.retainCount);
[person1 retain];
NSLog(@"Count = %ld", person1.retainCount);
[person1 release];

运行结果如下:

 Count = 1
 Count = 2
我们发现程序没有被释放,说明程序的引用数增加了,过程如下:


那么我们再释放一次:

Person *person1 = [[Person alloc] init];
NSLog(@"Count = %ld", person1.retainCount);
[person1 retain];
NSLog(@"Count = %ld", person1.retainCount);
[person1 release];//释放一次
NSLog(@"Count = %ld", person1.retainCount);
[person1 release];//再次释放
运行结果如下:

 Count = 1
 Count = 2
 Count = 1
 Person dealloc

结果说明对象已经被释放了,那么我们可以得出结论:

当对象创建后,会自动创建一个引用计数器,并将引用计数置1.(关键字:alloc,new,copy)

当对象retain时,对象的引用计数+1(关键字:retain)

当对象release后,对象的引用计数-1(关键字:release)

当对象引用计数为0时,自动走dealloc方法释放空间。

由于指针仍然存在,导致retainCount不为0。

多对象的内存管理

知道了单个对象的内存管理,那么复合的多个对象内存应该如何管理呢?

如:某人拥有一本书,这本书属于某个人

在这个例子里我们可以得出两个类,一个是“人”,另一个是“书”。这两个类互相包含,如下:

Person.h:

#import "Book.h"
@interface Person : NSObject

@property (nonatomic, copy)NSString *name;
@property (nonatomic, retain)Book *book;


@end

Book.h:

#import "Person.h"

@interface Book : NSObject

@property (nonatomic, copy)NSString *name;
@property (nonatomic, retain)Person *person;

@end


我们需要重写dealloc方法来释放对象中的对象

Person.m:

- (void)dealloc{
    
    NSLog(@"Person dealloc");
    [super dealloc];
    [_name release];
    [_book release];
    
}

Book.m:

- (void)dealloc{
    
    NSLog(@"Book dealloc");
    [super dealloc];
    [_name release];
    [_person release];
    
}

但当我们在程序中执行如下代码时:

Person *person = [[Person alloc] init];
Book *book = [[Book alloc] init];
[person release];
[book release];

发现程序报错崩溃了



出现这样错误的原因是我们在程序中互相引用了两个对象,这种现象我们称为-循环引用

只需要将Book和Person中的某一个变成声明即可以消除循环引用现象

//#import "Person.h"
@class Person;

@interface Book : NSObject

@property (nonatomic, copy)NSString *name;
@property (nonatomic, retain)Person *person;

@end

当我们再次运行程序就会发现程序正常运行了。

为了保证程序的正常运行,我们通常将该对象中的retain改为弱引用assign

//#import "Person.h"
@class Person;

@interface Book : NSObject

@property (nonatomic, copy)NSString *name;
@property (nonatomic, assign)Person *person;

@end













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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值