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

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、内存管理介绍

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 
@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

你可能感兴趣的:(Foundation基础框架)