OC学习篇之—内存管理介绍和使用

OC学习篇 尼古拉斯.赵四 7100℃ 0评论

在之前的一片文章我们说了OC中谓词操作:点击进入,从今天开始我们就来看一下OC中最难的一部分内容:内存管理

为什么说他难呢?因为内存如果需要我们程序员去管理的话,那个难度肯定是很大的,如果是Java,垃圾回收器会把这份工作给做了,我们不需要关心,但是就是因为如此,Android运行速度上会慢一下,原因很简单,Java的垃圾回收器有很多收集算法的,这个在回收的过程中是很浪费时间的,效率自然就低了,但是如果这份工作给程序员自己去做的话,效率上肯定会增加,但是对于程序员来说任务就比较繁重了,而且还要特别的小心,千万不能造成内存溢出和泄露。

这里我们主要从四个方面来介绍一下内存管理

1、简单的例子来了解引用计数的使用

2、set方法来控制引用计数问题

3、销毁方法来控制引用计数问题

4、初始化方法来控制引用计数问题

下面就来简单看一下OC中的内存管理

这个例子有点复杂,我们慢慢分析

Dog.h

//
//  Dog.h
//  24_MemeryManager
//
//  Created by jiangwei on 14-10-12.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Dog : NSObject{
    NSString *_name;
}

- (void) setName:(NSString *)name;

@end

Dog.m

//
//  Dog.m
//  24_MemeryManager
//
//  Created by jiangwei on 14-10-12.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import "Dog.h"

@implementation Dog

- (void) setName:(NSString *)name{
    _name = name;
}

@end

Dog类中定义了name属性,并且给他提供了一个set方法

Person.h

//
//  Person.h
//  24_MemeryManager
//
//  Created by jiangwei on 14-10-12.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import <Foundation/Foundation.h>

@class Dog;
@interface Person : NSObject{
    Dog *_dog;
    NSString * _name;
}

- (id)initWithDog:(Dog*)dog;
- (void)setName:(NSString *)name;
- (void)setDog:(Dog *)dog;
- (void)playDog;
- (Dog *)dog;

- (void)dealloc;
@end

Person.m

//
//  Person.m
//  24_MemeryManager
//
//  Created by jiangwei on 14-10-12.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import "Person.h"

@implementation Person

- (id)initWithDog:(Dog*)dog{
    //使用初始化的时候传入dog
    self = [super init];
    if(self != nil){
        //因为初始化方法只会调用一次,所以这里就没有做判断了
        [_dog release];
        _dog = [dog retain];
    }
    return self;
}

- (void)setName:(NSString *)name{
    //这里name也是对象,所以也是需要进行改写
    _name = name;
    
    /*
     //这里的判断是因为setName方法可能会被多次调用
     if(_name != name){
        [_name release];
        [name copy];//这里使用了copy,而没有使用retain,这个是字符串独有的,其他对象类型都是使用retain的
     }
     */
}

//第一种方式
/*
- (void)setDog:(Dog *)dog{
    //引用计数需要+1
    _dog = [dog retain];
    
    //有时候可能需要替换Dog对象,所以这里还要注意释放Dog的引用
}
 */

//第二种方式
/*
- (void)setDog:(Dog *)dog{
    //使用nil去调用方法是没有错误的
    //但是当一个对象被销毁的时候,指针就变成野指针了,这时候调用方法会出错的
    [_dog release];
    _dog = [dog retain];
}
 */

//第三种方式
- (void)setDog:(Dog *)dog{
    //这里的判断是因为setName方法可能会被多次调用
    if(_dog != dog){
        [_dog release];
        _dog = [dog retain];
    }
}

- (void)playDog{
    NSLog(@"playDog");
}

- (Dog *)dog{
    return _dog;
}

- (void)dealloc{
    //对象类型的属性都需要在这里进行释放引用
    //对狗进行释放
    [_dog release];
    NSLog(@"dealloc is Executing");
    [super dealloc];
}

@end

Person类中有一个Dog的属性,然后提供了set方法。代码有点复杂,我们后面会详细说明

下面来看一下测试代码

main.m

//
//  main.m
//  24_MemeryManager
//
//  Created by jiangwei on 14-10-12.
//  Copyright (c) 2014年 jiangwei. All rights reserved.
//

#import <Foundation/Foundation.h>

#import "Person.h"
#import "Dog.h"

//内存管理
//alloc用来创建对象,创建完成之后,引用计数为1,只调用一次
//retain使引用计数+1,可以多调用几次
//release使引用计数-1,可以多调用几次
//当引用计数为0的时候会调用dealloc
//最新的Xcode版本默认情况下会开启ARC机制的,当开启这个机制之后,我们就不能手动的显示调用这些方法,编译器会报错
//所以我们可以将这个默认状态的ARC关闭,但是这个只是为了测试使用

int main(int argc, const char * argv[]) {

    /*
    Person *person = [[Person alloc] init];//引用计数为1
    NSLog(@"引用计数:%ld",[person retainCount]);
    
    //引用计数加1
    [person retain];
    
    [person release];
    
    NSLog(@"引用计数:%ld",[person retainCount]);
    [person release];
     */
    
    
    Dog *dog = [[Dog alloc] init];
    [dog setName:@"小黑"];
    
    Dog *dog1 = [[Dog alloc] init];//引用计数为1
    [dog setName:@"大黄"];
    
    Person *p1 = [[Person alloc] init];
    [p1 setName:@"张三"];
    [p1 setDog:dog];
    [p1 setDog:dog1];//狗的引用替换成了大黄
    
    Person *p2 = [[Person alloc] init];
    [p2 setName:@"李四"];
    [p2 setDog:dog];
    
    //这里引用计数为1,这个和我们之前说的引用计数管理有矛盾,所以我们在使用的时候需要手动的retain
    NSLog(@"引用计数为:%ld",[dog retainCount]);
    
    [dog1 release];//因为alloc的时候引用计数就为1了
    
    //这里就有一个问题了,dog1对象已经被销毁了,但是setDog对象还是用了dog1对象调用方法了,这就会报错了
    //所以又对set方法进行改进了
    [p1 setDog:dog1];
    
    //当人销毁的时候,还需要对狗的引用-1
    //在人的dealloc方法中实现
    
    Person *p3 = [[Person alloc] initWithDog:dog1];
    [dog1 release];//dog1的引用计数:0
    
    [p3 playDog];
    
    [p3 release];
    
    return 0;
}

下面我们来详细说明一下:

首先如果想演示这个例子的话,需要修改一下设置: