OutMan——Objective-C内存管理

来源:互联网 发布:秀萝捏脸数据 编辑:程序博客网 时间:2024/05/22 09:43

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文件
这里写图片描述


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


0 0
原创粉丝点击