黑马程序员——OC中的内存管理

来源:互联网 发布:卖家怎么玩好淘宝客 编辑:程序博客网 时间:2024/06/06 03:14
------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

一、内存管理介绍

1、内存管理范围
任何继承了NSObject的对象,对基本数据类型无效
2、内粗管理原理
1)每个对象内部都保存了一个与之相关联的整数,称为引用计数器
2)当使用alloc、new或者copy创建一个对象时,对象的引用计数器被设置为1
3)给对象发送一条retain消息,可以使引用计数器值+1
4)给对象发送一条release消息,可以使引用计数器值-1
5)当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收,系统也会自动向对象发送一条dealloc消息
6)一般会重写dealloc方法,在这里释放相关资源。一定不要直接调用dealloc方法
7)可以给对象发送retainCount消息获得当前的引用计数器值
@implementation Person// 当一个Person对象被回收的时候,就会自动调用这个方法- (void)dealloc{    NSLog(@"Person对象被回收");// super的dealloc一定要调用,而且放在最后面    [super dealloc];}@end

二、MRC管理机制

1、内存管理原则

1)谁创建,谁释放。如果你通过alloc、new或(mutable)copy来创建一个对象,那么你必须调用release或autorelease。换句话说,不是你创建的,就不用你去释放
2)一般来说,除了alloc、new或copy之外的方法创建的对象都被声明了autorelease
3)谁retain,谁release。只要你调用了retain,无论这个对象是如何生成的,你都要调用release

示例:

int main(){    //计数器为 1    Person *p = [[Person alloc] init];    //NSUInteger c = [p retainCount];    //NSLog(@"计数器:%ld", c);    // 计数器为2 retain方法返回的是对象本身    [p retain];    //计数器为 1    [p release];        // 计数器为0 野指针:指向僵尸对象(不可用内存)的指针    [p release];    return 0;}

2、@property参数

1)、默认格式@property(参数1,参数2)类型名字
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@property int age;// retain : 生成的set方法里面,release旧值,retain新值@property (retain) Book *book;@property (retain) NSString *name;@end

2)默认参数为atomic,即提供多线程安全
atomic是OC使用的一种线程保护技术,防止在写入未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择
所以一般的声明格式为
//对象类型的成员变量@property (nonatomic, strong) Person *p;//一般数据类型@property (nonatomic, assign) int age;

3、循环引用
分析下面一种情景:(假设都是retain引用)象A引用了对象B,对象B引用了对象C,对象C引用了对象B,这时候B和C的引用计数分别是2和1,当A不再使用B,调用release释放对B的所有权,因为C还引用了B,所以B的引用计数为1,B不会被释放。B不释放,C的引用计数就是1,C也不会被释放。从此,B和C永远留在内存中。

解决方法:B和C互相引用时,应该一端使用ratain,另一端使用assign


三、@class的使用


1、通常引用一个类有两种办法
  • 一种是通过#import方式引入
  • 一种是通过@class引入

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

3、@class的优点
使用@class由于只需要知道被引用的类的名称就可以了,而再实现类由于要用到被引用类中的实体变量和方法,所有在.m文件中需要使用#import来包含被引用的头文件
如果有上百个头文件都#import了同一个文件,或者这些文件一次被#import,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来讲,使用@class方式就不会出现这种问题了。因此,使用@class可以大大提高文件编译的效率。

举例

//.h文件#import <Foundation/Foundation.h>@class Book;@interface Student@property (retain) Book *book;@end

#import "Student.h"#import "Book.h"//当调用Book类中的方法时就需要调用Book类@implementation Student @synthesize book = _book;- (void)dealloc {[_book release];[super dealloc];}@end

四、autorelease和autorelease pool(自动释放池)

1、autorelease pool
1)自动释放池是OC里面的一种内存自动回收机制,一般可以将一些临时变量添加到自动释放池中,统一回收释放
2)当自动释放池销毁时,池里面的所有对象都会调用一次release方法
3)OC对象只需要发送一条autorelease消息,就会把这个对象添加到最近的自动释放池中(栈顶的释放池)
4)autorelease实际上只是把对release的调用延迟了,对于每一次autorelease,系统只是把该对象放入了当前的autoreleasepool中,当该pool被释放时,该pool中的所有对象会被调用Release
@autoreleasepool    {// { 开始代表创建了释放池                // autorelease方法会返回对象本身        // 调用完autorelease方法后,对象的计数器不变        // autorelease会将对象放到一个自动释放池中        // 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作        Person *p = [[[Person alloc] init] autorelease];                p.age = 10;        @autoreleasepool        {            // 1            Person *p2 = [[[Person alloc] init] autorelease];            p2.age = 10;        }        Person *p3 = [[[Person alloc] init] autorelease];    } // } 结束代表销毁释放池

2、使用注意
1)在ARC下,不能使用[[NSAutoreleasePool alloc] init],而应当使用@autoreleasepool
2)不要把大量循环操作放到同一个NSAutoreleasePool之间,这样会造成内存峰值的上升
3)尽量避免对大内存使用该方法,对于这种延迟释放机制,还是尽量少用
4)sdk中一般利用静态方法创建并返回的对象都是已经autorelease的,不需要再进行release操作

@autorelease的使用示例

int main(){    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];        Person *pp = [[[Person alloc] init] autorelease];        [pool release]; // [pool drain];        @autoreleasepool    {        // 计数为1        Person *p = [[[[Person alloc] init] autorelease] autorelease];                // 计数为0        // [p release];    }        return 0;}

@autorelease的应用

/* 1.系统自带的方法里面没有包含alloc、new、copy,说明返回的对象都是autorelease的  2.开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象 1> 创建对象时不要直接用类名,一般用self + (id)person {    return [[[self alloc] init] autorelease]; } */int main(){    @autoreleasepool {        Person *p = [Person personWithAge:100];                GoodPerson *p2 = [GoodPerson personWithAge:10];                p2.money = 100;    }    return 0;}

五、ARC管理机制

1、介绍
1)ARC,就是由编译器代码中自动加入了retain/release。要注意的是,ARC并不是GC,它只是一种代码静态分析工具
从iOS 5/Mac OS X 10.7开始导入,利用Xcode4.2可以使用该机制
2)优点:
1>不需要担心烦人的内存管理和内存泄露
2>代码的总量变少了
3>代码效率高,由于使用编译器管理引用计数,减少了低效代码的可能性
3)缺点:
1>要记住新的ARC规则、关键字、特性
2>使用一些旧代码、第三方代码的时候比较麻烦,可能要修改编译开关,XCode4.2中默认ARC是YES的状态
4)总结:系统引入一套自动管理内存的机制,回收并销毁对象

2、基本原理
1)规则
ARC 的规则非常简单:只要还有一个强指针变量指向对象,对象就会保持在内存中
2)强指针和弱指针
1>默认所有实例变量和局部变量都是Strong指针
2>弱指针指向的对象被回收后,弱指针会自动变为nil指针,不会引发野指针错误

3、使用注意
1)不能调用release、retain、autorelease、retainCount
2)可以重写dealloc,但是不能调用[super dealloc]
3)@property : 想长期拥有某个对象,应该用strong,其他对象用weak
4)其他基本数据类型依然用assign
5)两端互相引用时,一端用strong、一端用weak

4、ARC特点
1)不允许调用release、retain、retainCount
2)允许重写dealloc,但是不允许调用[super dealloc]
3)@property的参数

  • strong :成员变量是强指针(适用于OC对象类型)
  • weak :成员变量是弱指针(适用于OC对象类型)
  • assign : 适用于非OC对象类型
4)以前的retain改为用strong

5)指针分2种:
1>强指针:默认情况下,所有的指针都是强指针 __strong
2> 弱指针:__weak

示例:效果很明显,怎么写都是对的!再也不用为内存而烦恼了!

//main.m文件int main(){    // 新建2个用户    User *u = [[User alloc] init];    u.name = @"2B";        User *u2 = [[User alloc] init];    u2.name = @"傻B";        // 新建2条微博    Status *s = [[Status alloc] init];    s.text = @"今天天气真好!";    s.user = u;        Status *s2 = [[Status alloc] init];    s2.text = @"今天天气真的很好!";    s2.retweetStatus = s;    s2.user = u2;        return 0;}
// 微博内容、微博配图、发送时间、微博发送人、转发的微博、被评论数、被转发数@interface Status : NSObject@property (nonatomic, strong) NSString *text;//retain改为strong@property (nonatomic, strong) NSString *icon;//默认使用atomic多线程,但是ios开发使用nonatomic即可// 从1970-01-01 00:00:00 开始,一共度过了多少毫秒@property (nonatomic, assign) long time;//@property (nonatomic) time_t time;@property (nonatomic, strong) User *user;@property (nonatomic, strong) Status *retweetStatus;@property (nonatomic, assign) int commentsCount;@property (nonatomic, assign) int retweetsCount;@end

0 0
原创粉丝点击