Objective-C基础——内存管理


一、基本原理

1.内存管理
内存管理管理范围:任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效;
栈:放局部变量,会自动回收;
堆:放一些动态产生的。
 
2.对象的基本结构
每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”;
每个OC对象内部专门有4个字节的存储空间来存储引用计数器。
 
3.引用计数器的作用
当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1;
当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收。


4.方法的基本使用
 retain :计数器+1,会返回对象本身
 release:计数器-1,没有返回值
 retainCount:获取当前的计数器
 dealloc:
 *当一个对象要被回收的时候就会调用
 *一定要调用[super dealloc],这句调用要放在最后面
 
 5.概念
 1>僵尸对象:所占用内存已经被回收的对象,僵尸对象不能再使用
 2>野指针:指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCES)
 3>没有指向任何东西的指针(存储的东西时nil、null、0),给空指针发送消息不会报错 

6.引用计数器的操作
给对象发送一条retain消息,引用计数器值+1(retain方法返回对象本身);
给对象发送一条release消息,引用计数器值-1(没有返回值);
给对象发送retainCount消息获得当前的引用计数器值。
 
7.对象的销毁
当一个对象的引用计数器值为0时,它就会将被销毁,其占用的内存被系统回收;
当一个对象被销毁时,系统会自动向对象发送一条dealloc消息(系统自动调用,不能直接调用dealloc方法);
一般会重写dealloc方法,在这里释放相关资源;
一旦重写了dealloc方法,就必须调用[super dealloc];,并且放在最后面调用(重写dealloc方法,验证一个对象是否回收);

一旦对象被回收了,它占用的内存就不在可用,坚持使用会导致程序崩溃(野指针错误)。


#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
    Person *p = [[Person alloc]init];
    
    NSUInteger c =[p retainCount];
    
    NSLog(@"计数器%lu",(unsigned long)c);
    
    //0 野指针:指向僵尸对象(不可用内存)的指针
    [p release];
    p = nil;
    //EXC_BAD_ACCESS 访问了一块坏的内存(已被回收,不可用的内存)
    //野指针错误
    //OC中不存在空指针错误,给空指针发送消息,不报错
    [p release];
    return 0;
}

@interface Person : NSObject

@end
@implementation Person
- (void)dealloc
{
    NSLog(@"Person对象被回收");
    [super dealloc];
}

@end


二、内存管理原则

1.原则分析
只要还有人在用某个对象,那么这个对象就不会被回收;
只要你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作);
当你不想使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release操作)。
 
2.谁创建,谁release
如果你通过alloc、new或[mutable]copy来创建一个对象,那么你必须调用release或autorelease;
不是你创建的,就不用你去[auto]release。
 
3.谁retain,谁release
只要你调用了retain,无论这个对象是如何生成的,你都要调用release。
 
4.总结
有始有终,有加就有减;
曾经让对象的计数器+1,就必须在最后让对象计数器-1。
 

三、内存管理代码规范

1> 只要调用了alloc,必须有release(autorelease),对象不是通过alloc产生的,就不用调用release。
2> set方法的代码规范
基本数据类型:直接赋值;
OC对象类型:
先判断是不是新传进来的对象;
对旧对象做一次release;
对新对象做一次retain。

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

3.dealloc方法的代码规范
一定要[super dealloc];,而且放在最后面;
对self(当前)所拥有的其他对象都做一次release;
- (void)dealloc  
  
{  
  
    [_car release];  
  
    [super dealloc];  
  
}  

四、@property参数

1.控制set方法的内存管理
retain:release旧值,retain新值(用于OC对象)(自动生成管理内存的set方法);
assign:直接赋值,不做任何内存管理(默认,用于非OC对象类型);
copy:release旧值,copy新值(一般用于NSString *)。
@interface Person : NSObject
@property int age;

// retain : 生成的set方法里面,release旧值,retain新值
@property (retain) Book *book;
@property (retain) NSString *name;

@end
@implementation Person

//- (void)setBook:(Book *)book
//{
//    if (_book != book)
//    {
//        [_book release];
//        
//        _book = [book retain];
//    }
//}

- (void)dealloc
{
    [_book release];
    [_name release];
    [super dealloc];
}

@end
注意:dealloc的重写仍需手动进行。
2.set方法内存管理相关的参数
 * retain : release旧值,retain新值(适用于OC对象类型)
 * assign : 直接赋值(默认,适用于非OC对象类型)
 * copy   : release旧值,copy新值
 
3.是否要生成set方法
 * readwrite : 同时生成setter和getter的声明、实现(默认)
 * readonly  : 只会生成getter的声明、实现
 
 4.多线程管理
 * nonatomic : 性能高 (一般就用这个)
 * atomic    : 性能低(默认)
 
 4.setter和getter方法的名称
 * setter : 决定了set方法的名称,一定要有个冒号 :
 * getter : 决定了get方法的名称(一般用在BOOL类型)
@interface Person : NSObject


// 返回BOOL类型的方法名一般以is开头
@property (getter = isRich) BOOL rich;

//   
@property (nonatomic, assign, readwrite) int weight;
// setWeight:
// weight

// 
@property (readwrite, assign) int height;

@property (nonatomic, assign) int age;

@property (retain) NSString *name;
@end

五、循环引用

1.@class
对于循环依赖关系来说,比如A类引用B类,同时B类也引用A类,当使用@class在两个类相互声明,就不会出现编译报错;
使用@class 类名;就可以引用一个类,说明一下它是一个类。
 
2.@class和#import的区别(面试)
#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中B *b只是类的声明;
 
如果有上百个文件都#import了同一个文件,或者这些文件依次被#import,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍;而相对来讲,使用@class方式就不会出现这种问题了(关乎性能);
 
在.m实现文件中,如果需要引用到被引用类的实例变量或者方法时,用@class方式,还需要使用#import方式引入被引用类。
 
3.开发中引用一个类的规范
在.h文件用@class来声明类;
在.m文件中用#import来包含类的所有东西。

4.两端循环retain引用的解决方案
比如A对象retain了B对象,B对象retain了A对象,这样会导致A对象和B对象永远无法释放;
解决方案:一端用retain,一端用assign(用了assign,重写dealloc方法就不要写它的release)。

六、autorelease

1.基本用法
给某个对象发送一条release消息时,就会将这个对象加到一个自动释放池中;
当自动释放池销毁时,会给池子里面的所有对象发送一条release消息;
调用autorelease方法时并不会改变对象的计数器,并且会返回对象本身;
autorelease实际上只是把对release的调用延迟了,对于每一次autorelease,系统只是把该对象放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有对象会被调用release。
  2.自动释放池的创建方式
 1> iOS 5.0前
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
 [pool release]; // [pool drain];
2> iOS 5.0 开始
 
@autoreleasepool
 {
    
 }

3.autorelease的好处
 1> 不用再关心对象释放的时间
 2> 不用再关心什么时候调用release
 
4.autorelease的使用注意
 1> 占用内存较大的对象不要随便使用autorelease
 2> 占用内存较小的对象使用autorelease,没有太大影响
 
 
 5.错误写法
 1> alloc之后调用了autorelease,又调用release
@autoreleasepool
 {
    // 1
    Person *p = [[[Person alloc] init] autorelease];
 
    // 0
    [p release];
 }

 2> 连续调用多次autorelease
 
@autoreleasepool
 {
    Person *p = [[[[Person alloc] init] autorelease] autorelease];
 }


6.应用实例
开发中一般可以为类添加一个快速创建一个autorelease对象的类方法(代码封装)
+ (id)对象名  
  
{  
  
return [[[self alloc] init] autorelease];  
 


4.规律
一般来说,除了alloc、new或copy之外的方法创建的对象都被声明了autorelease
 

七、内存管理总结

计数器的基本操作
1> retain : +1
2> release :-1
3> retainCount : 获得计数器


set方法的内存管理
1> set方法的实现
- (void)setCar:(Car *)car
{
    if ( _car != car )
    {
        [_car release];
        _car = [car retain];
    }
}


2> dealloc方法的实现(不要直接调用dealloc)
- (void)dealloc
{
    [_car release];
    [super dealloc];
}


三、@property参数
1> OC对象类型
@property (nonatomic, retain) 类名 *属性名;
@property (nonatomic, retain) Car *car;
@property (nonatomic, retain) id car;


// 被retain过的属性,必须在dealloc方法中release属性
- (void)dealloc
{
    [_car release];
    [super dealloc];
}


2> 非OC对象类型(int\float\enum\struct)
@property (nonatomic, assign) 类型名称 属性名;
@property (nonatomic, assign) int age;


四、autorelease
1.系统自带的方法中,如果不包含alloc、new、copy,那么这些方法返回的对象都是已经autorelease过的
[NSString stringWithFormat:....];
[NSDate date];


2.开发中经常写一些类方法快速创建一个autorelease的对象
* 创建对象的时候不要直接使用类名,用self


------ Java培训、Android培训、iOS培训、.Net培训 、期待与您交流! -------
  • 0
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值