天天看点

【黑马程序员】---Objective-C内存管理总结

------- android培训、 java培训、期待与您交流! ----------

Objective-C内存管理

基本数据类型存储在栈中,系统会自动释放存储空间,而OC对象存储在堆中,系统不会自动回收对象,故而引起内存管理。

1.引用计数器的基本操作

每一个对象都有一个引用计数器,它在内存中占用4个字节

(1)方法的基本使用

  1> retain :计数器+1,会返回对象本身

 2> release :计数器-1,没有返回值

 3> retainCount :获取当前的计数器

  4> dealloc

当一个对象要被回收的时候,就会调用

一定要调用[super dealloc],这句调用要放在最后面

(2).概念

 1> 僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用

 2> 野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCESS)

 3> 空指针 :没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错

Person p = [[Person alloc ] init];
[p retain];
[p release];
int size = [p retainCount];
[p release]
           
// 当一个Person对象被回收的时候,就会自动调用这个方法
- (void)dealloc
{
    NSLog(@"Person对象被回收");
    
    // super的dealloc一定要调用,而且放在最后面
    [super dealloc];
}
           

心得体会:

(1)当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1

(2)当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收

(3)不要直接调用dealloc方法

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

(5)OC不存在空指针错误,给空指针发送消息,不报错

2.set方法的内存管理

 内存管理代码规范:

  1.只要调用了alloc,必须有release(autorelease)对象不是通过alloc产生的,就不需要release

 2.set方法的代码规范

 1> 基本数据类型:直接复制

- (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方法的代码规范

  1> 一定要[super dealloc],而且放到最后面

  2> 对self(当前)所拥有的其他对象做一次release

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

心得体会:

如果你有个OC对象类型的成员变量,就必须管理这个成员变量的内存。

[email protected]

 1.set方法内存管理相关的参数

retain : release旧值,retain新值(适用于OC对象类型)

assign : 直接赋值(默认,适用于非OC对象类型)

copy   : release旧值,copy新值

 2.是否要生成set方法

  readwrite : 同时生成setter和getter的声明、实现(默认)

readonly  : 只会生成getter的声明、实现

 3.多线程管理

  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;

@property (readwrite, assign) int height;

@property (nonatomic, assign) int age;

@property (retain) NSString *name;
@end
           

心得体会:

(1)不同类型的参数可以组合使用。

(2)一般都会添加nonatomic参数,提高性能。

4.循环引用

当一个类引用了另外一个类,另一个类又引用了这个类的时候,就叫做循环引用。

@class Person;

@interface Card : NSObject

@property (nonatomic, assign) Person *person;

@end
           
#import "Card.h"

@interface Person : NSObject

@property (nonatomic, retain) Card *card;

@end
           

心得体会:这种代码编译会报错。当使用@class在两个类相互声明,就不会出现编译报错

@class  

仅仅是告诉编译器,Card是一个类

@class和#include的区别:

(1)#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息

(2)@class相比#include而言,能提高效率。当#include引入的文件更改时,所有引入的文件都要重新编译一遍

心得体会:

(1)在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类

(2)两端循环引用的解决方案是,一端用retain一端用assign。

5.autorelease

(1) 给某个对象发送一条autorelease消息时,就会将这个对象加到一个自动释放池中

(2)当自动释放池销毁时,会给池子里面的所有对象发送一条release消息

(3)调用autorelease方法时并不会改变对象的计数器,并且会返回对象本身

@autoreleasepool
 {
    // 1
    Person *p = [[[Person alloc] init] autorelease];
 
 }
           
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];
 }
           

自动释放池

(1) 在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)

(2)当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池

// 自动释放池的创建方式
 // 1> iOS 5.0前
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
 [pool release]; // [pool drain];
 
 
 // 2> iOS 5.0 开始
 @autoreleasepool
 {
    
 }
           

心得体会:

(1)autorelease实际上只是把对release的调用延迟了,对于每一次autorelease,系统只是把该对象放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有对象会被调用Release。

6.autorelease应用

(1)系统自带的方法里面没有包含alloc、new、copy,说明返回的对象都是autorelease的。

NSString *str2 = [NSString stringWithFormat:@"age is %d", 10];
           

(2)开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象

+ (id)person
 {
    return [[[self alloc] init] autorelease];
 }
           

心得体会:

(1)创建对象时不要直接用类名,一般用self,这样于子类调用。

继续阅读