OutMan——Objective-C内存管理

Objective-C提供的内存管理方式:

一、Objective-C提供了三种内存管理方式:
1. Manual Reference Counting(MRC,手动管理计数,在开发ios4.1之前版本的项目时,我们要自己负责使用引用计数器来管理内存,比如要手动retain、release、autorelease等,而在其后的版本可以使用ARC,让系统自己管理内存)
2. Automatic Reference Counting(ARC,自动引用计数,ios4.1版本之后推出的)
3. Garbage Collection(垃圾回收,ios不支持垃圾回收)

单个对象的内存管理

一、为什么要内存管理
1. 移动设备的内存有限,为了可以使移动设备上的软件运行流畅
2. 如果对象不再使用了,就应该回收它的内存空间,防止内存泄露

二、内存管理的范围
        任何继承了NSObject的对象,对其他数据类型(int、char、double、float、struct、enum等)无效

三、引用计数器
(1)对象的基本结构
1. 每个OC对象都有自己的引用计数器,是个整数,表示“对象被引用的次数”,即有多少人正在使用这个OC对象
2. 每个OC对象内部专门有8个字节的存储空间来存储引用计数器
这里写图片描述

(2)引用计数器的作用
1. 当使用alloc、new或copy创建一个新对象时,新对象的引用计数器默认就是1
2. 当一个对象的引用计数器值为0时,对象占用的内存空间就会被系统回收,换句话说,如果对象的引用计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收,除非整个程序已经退出

四、引用计数器的操作
1. 给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身)
2. 给对象发送一条release消息,可以使引用计数器值-1(没有返回值)
3. 给对象发送一条retainCount消息,可以获得当前引用计数器值

五、对象的销毁
1. 当一个对象的引用计数器的值为0时,那么它将被销毁,其占用的内存空间被系统回收
2. 当一个对象被销毁时,系统会自动向对象发送一条dealloc消息
3. 一般重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言
4. 一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用
5. 不要直接调用dealloc方法
6. 一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)

六、僵尸对象、野指针和空指针
1. 僵尸对象:所占用内存空间已经被回收的对象,僵尸对象不能再使用
2. 野指针:(1)指向僵尸对象的指针,给野指针发送消息会报错(EXC_BAD_ACCESS),(2)定义的指针没有经行初始化
3. 空指针:没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错

七、如何将项目从ARC模式改为MRC
这里写图片描述

八、单个对象内存管理的代码演示
(1)Person类的设计
                                                                     Person.h文件


#import <Foundation/Foundation.h>

@interface Person : NSObject

@property int age;   // 年龄

@end

                                                                     Person.m文件


#import "Person.h"

@implementation Person

// 重写dealloc方法
- (void)dealloc
{
    NSLog(@"Person被销毁了");
    // 一定要调用[super dealloc],并且要放到最后调用
    [super dealloc];
}

@end

-设计注意:
1. 一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用

(2)main.m文件
这里写图片描述
设计注意:
1. 当使用alloc、new或copy创建一个新对象时,新对象的引用计数器默认就是1
2. 给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身)
3. 给对象发送一条release消息,可以使引用计数器值-1(没有返回值)
4. 给对象发送一条retainCount消息,可以获得当前引用计数器值
5. 当一个对象的引用计数器的值为0时,那么它将被销毁,其占用的内存空间被系统回收
6. 当一个对象被销毁时,系统会自动向对象发送一条dealloc消息
7. 一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)

多个对象的内存管理

一、多对象内存管理原则分析
(1) 原则分析
1. 只要还有人在用某个对象,那么这个对象就不会被回收
2. 只要你想用这个对象,那么就让对象的计数器+1
3. 当你不再使用这个对象时,就让对象的计数器-1
(2)谁创建,谁release
1. 如果你通过alloc、new、copy或mutablecopy来创建一个对象,那么你必须调用release或autorelease
2. 换句话说,不是你创建的,就不用你去release或autorelease
(3)谁retain谁release
1. 只要你调用了retain,无论这个对象是如何生成的,你都要调用release
(4)总结:
1. 有始有终,有加就有减
2. 曾经让对象的计数器+1,就必须在最后让对象的计数器-1

二、set方法内存管理代码规范
(1)基本数据类型:直接赋值
例:

- (void)setAge:(int)age
{
    _age = age;
}

(2)OC对象类型
例:

- (void)setBook:(Book *)book
{
    // 先判断传进来的是否是新对象
    if (book != _book)
    {
        // 对旧对象做一次release操作
        [_book release];
        // 对新对象做一次retain操作
        _book = [book retain];
    }
}

三、dealloc方法的代码规范(不要直接调用dealloc方法)
1. 一定要调用[super dealloc],而且要放到最后调用
2. 对当前对象所拥有的其他对象做一次release操作
例:

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

四、set方法内存管理代码实现(多对象内存管理演示)
(1)Person类的设计
                                                                     Person.h文件


#import <Foundation/Foundation.h>
#import "Book.h"

@interface Person : NSObject
{
    Book *_book;   // 书
    int _age;   // 年龄
}

// book的setter和getter声明
- (void)setBook:(Book *)book;
- (Book *)book;

// age的setter和getter声明
- (void)setAge:(int)age;
- (int)age;

@end

                                                                     Person.m文件


#import "Person.h"

@implementation Person
// book的setter和getter实现
- (void)setBook:(Book *)book
{
    // 先判断传进来的是否是新对象
    if (book != _book)
    {
        // 对旧对象做一次release操作
        [_book release];
        // 对新对象做一次retain操作
        _book = [book retain];
    }
}
- (Book *)book
{
    return _book;
}

// age的setter和getter声明
- (void)setAge:(int)age
{
    _age = age;
}
- (int)age
{
    return _age;
}

// 重写dealloc方法
- (void)dealloc
{
    [_book release];
    NSLog(@"%d岁的人被销毁了", _age);
    [super dealloc];
}
@end

(2)Book类的设计
                                                                     Book.h文件


#import <Foundation/Foundation.h>

@interface Book : NSObject

@end

                                                                     Book.m文件


#import "Book.h"

@implementation Book
// 重写dealloc方法
- (void)dealloc
{
    NSLog(@"书被销毁了");
    [super dealloc];
}
@end

-设计注意:
1. 基本数据类型:直接赋值
例:

- (void)setAge:(int)age
{
    _age = age;
}
  1. (2)OC对象类型
    例:
- (void)setBook:(Book *)book
{
    // 先判断传进来的是否是新对象
    if (book != _book)
    {
        // 对旧对象做一次release操作
        [_book release];
        // 对新对象做一次retain操作
        _book = [book retain];
    }
}
  1. 一般重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言
    例:
- (void)dealloc
{
    [_book release];
    NSLog(@"%d岁的人被销毁了", _age);
    [super dealloc];
}
  1. 一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用

(3)main.m文件
这里写图片描述

五、set方法内存管理(@property内存管理)
(1)@property Book *book;会自动生成setter的实现(没有管理内存)为:

- (void)setBook:(Book *)book
{
    _book = book;
}

(2)@property(retain)Book *book;会自动生成setter的实现(已经管理了内存)为:

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

六、set方法内存管理(@property的参数)
(1)set方法内存管理相关的参数
1. retain:release旧值,retain新值(适用于OC对象类型)
2. assign:直接赋值(默认,适用于非OC对象类型)
3. copy:release旧值,copy新值
(2)是否要生成set方法
1. readwrite:同时生成setter和getter的声明和实现(默认)
2. readonly:只生成getter的声明和实现
(3)多线程管理
1. nonatomic:性能高(一般就用这个,对属性不加锁,多线程下线程不安全)
2. atomic:性能低(默认,对属性加锁,多线程下线程安全)
(4)setter和getter方法的名称
1. setter:决定了set方法的名称,一定要有个冒号
2. getter:决定了get方法的名称(一般用在成员变量为BOOL类型)

七、模型设计(综合)练习
        要求设计一个微博用户类,包含以下属性:姓名、微博号码、密码、头像、性别、手机和生日
        设计一个类表示一条微博,包含以下属性:微博内容、微博配图、发送时间、微博发送人、转发的微博、被评论数和被转发数
(1)User(用户)类的设计
                                                                     User.h文件


#import <Foundation/Foundation.h>

typedef enum
{
    kSexMan,   // 男
    KsexWomen   // 女
} Sex;

typedef struct
{
    int year;
    int month;
    int day;
} Date;

@interface User : NSObject

@property(nonatomic, retain)NSString *name;   // 姓名
@property(nonatomic, retain)NSString *account;   // 微博账号
@property(nonatomic, retain)NSString *password;   // 微博密码
@property(nonatomic, retain)NSString *icon;   // 微博头像
@property(nonatomic, assign)Sex sex;
@property(nonatomic, retain)NSString *phoneNum;   // 手机号码
@property(nonatomic, assign)Date brithday;   // 生日

@end

                                                                     User.m文件


#import "User.h"

@implementation User

- (void)dealloc
{
    [_name release];
    [_account release];
    [_password release];
    [_icon release];
    [_phoneNum release];
    NSLog(@"%@被释放了", _name);
    [super dealloc];
}
@end

(2)Status(微博)类的设计
                                                                     Status.h文件


#import <Foundation/Foundation.h>
#import "User.h"

@interface Status : NSObject

@property(nonatomic, retain)NSString *contents;   // 微博内容
@property(nonatomic, retain)NSString *icon;   // 微博配图
@property(nonatomic, assign)time_t time;   // 发送时间
@property(nonatomic, retain)User *user;   // 微博发送人
@property(nonatomic, retain)Status *retweetStatus;   // 转发的微博
@property(nonatomic, assign)int commentCounts;   // 被评论数
@property(nonatomic, assign)int retweetCounts;   // 被转发数

@end

                                                                     Status.m文件


#import "Status.h"

@implementation Status

- (void)dealloc
{
    [_contents release];
    [_icon release];
    [_user release];
    [_retweetStatus release];
    NSLog(@"微博%@被销毁了", _contents);
    [super dealloc];
}

@end

(3)main.m文件
这里写图片描述

循环retain和循环引用的问题

一、循环retain和@class
(1)@class的作用:仅仅告诉编译器,某个名称是个类
例: @class Person; 仅仅告诉编译器Person是一个类
(2)在开发中引用一个类的规范
1. 在.h文件中@class来声明类
2. 在.m文件中用#import来包含类的所有东西
(3)循环retain
1. 比如A对象retain了B对象,B对象retain了A对象
2. 这样会导致A对象和B对象永远无法释放
(4)两端循环引用的解决方案
1. 一端使用retain
2. 另一端使用assign
(5)#import和@class的区别
1. #import方式会包含被引用类的所用信息,包括被引用类的变量和方法,@class方式只是告诉编译器在A.h文件中,B *b只是类的声明,具体这个类有什么信息,这里不需要知道,等实现文件中真正要用到的时候,才会真正去查看B类中的信息
2. 如果有上百个头文件都#import同一个文件,或者这些文件依次被#import,那么一旦最开是的头文件稍微有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,相对来讲,使用@class方式就不会出现这种问题了
3. 在.m实现文件中,如果需要引用到被引用类的实体变量或方法时,还需要使用#import方式引入被引用类
(6)@class的好处
1. 解决了循环引用问题(例:A文件中 #import “B.h”;B文件中 #import “A.h”)
2. 可以提高性能

二、解决循环retain和循环引用的代码实现
        设计要求:每个人都有一张身份证,每张身份证都代表一个人
(1)Person类的设计
                                                                     Person.h文件


#import <Foundation/Foundation.h>

@class Card;
@interface Person : NSObject

@property(nonatomic, retain)Card *card;   // 身份证

@end

                                                                     Person.m文件


#import "Person.h"
#import "Card.h"

@implementation Person

- (void)dealloc
{
    [_card release];
    NSLog(@"Person被销毁了");
    [super dealloc];
}

@end

(2)Card类的设计
                                                                     Card.h文件


#import <Foundation/Foundation.h>

@class Person;
@interface Card : NSObject

@property(nonatomic, assign)Person *person;   // 人

@end

                                                                     Card.m文件


#import "Card.h"
#import "Person.h"

@implementation Card

- (void)dealloc
{
    NSLog(@"Card被销毁了");
    [super dealloc];
}

@end

(3)main.m文件
这里写图片描述
-设计注意:
1. 一端使用retain,另一端使用assign来解决循环retain问题
2. 使用@class来解决循环包含问题

autorelease

一、autorelease的基本使用
1. 会将对象放到一个自动释放池中
2. 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
3. autorelease方法会返回对象本身
4. 调用完autorelease方法后,对象的计数器不变

二、autorelease的好处
1. 不用再关心对象释放的时间
2. 不用再关心什么时候调用release

三、autorelease使用注意
1. 占用内存较大的对象不要随便使用autorelease(因为autorelease不能精准的控制对象什么时候release,只有在自动释放池销毁的时候,里面的对象才会做一次release操作)对于这种延迟释放机制还是尽量少用
2. 占用内存较小的对象使用autorelease,没有太大的影响
3. 不要把大量循环操作放到同一个@autoreleasepool之间,这样造成内存峰值上升

四、自动释放池
(1)自动释放池的介绍
1. 在ios程序运行过程中,会创建无数个池子,这些池子都是以栈结构存在的(栈结构的特点:先进后出)
2. 当一个对象调用autorelease方法时,会将这个对象放到栈顶的自动释放池中
(2)自动释放池的创建
1. ios5.0前

        // 自动释放池的创建
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        // 自动释放池的销毁方式一:
        [pool drain];
        // 自动释放池的销毁方式一:
        [pool release];
  1. ios5.0后
    这里写图片描述

五、autorelease的使用规律
1. 系统中自带的方法里面没有包含alloc、new、copy的,说明返回的对象都是经过autorelease的
2. 开发中经常会提供一些类方法,用来快速创建一个已经autorelease过的对象

六、autorelease的使用细节
1. 开发中经常会提供一些类方法,用来快速创建一个已经autorelease过的对象
2. 创建对象时不要直接用类名,一般用self

七、autorelease的应用代码演示
        要求:设计一个Person类,设计一个类方法,可以快速创建一个autorelease过的对象,创建的人有不同的年龄;设计一个学生类,调用父类方法,可以快速创建一个autorelease过的对象
(1)Person类的设计
                                                                     Person.h文件


#import <Foundation/Foundation.h>

@interface Person : NSObject

@property(nonatomic, assign)int age;

+ (instancetype)person;
+ (instancetype)personWithAge:(int)age;

@end

                                                                     Person.m文件


#import "Person.h"

@implementation Person

+ (instancetype)person
{
    Person *p = [[[self alloc] init] autorelease];
    return p;
}
+ (instancetype)personWithAge:(int)age
{
    Person *p = [self person];
    p.age = age;
    return p;
}

- (void)dealloc
{
    NSLog(@"%d岁的人被销毁了", _age);
    [super dealloc];
}

@end

(2)Student类的设计
                                                                     Student.h文件


#import "Person.h"

@interface Student : Person

@property(nonatomic, assign)int no;

@end

                                                                     Student.m文件


#import "Student.h"

@implementation Student


@end

(3)main.m文件
这里写图片描述


                                        —— 如果您有不同的见解,请随时指出,期待与您的交流!——


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值