内存管理(一):栈区、堆区,成员变量和局部变量,alloc,retain,自动释放池,成员变量及属性的内存管理

内存问题体现在两个方面:内存泄露,野指针异常。

了解内存管理,能帮我们提升程序性能,大大减少程序bug时间。


1. 内存管理原则

使用者必须保证在使用的周期内是安全的


2. 栈区、堆区    

栈区,所有的变量都在栈区,栈区的变量指向堆区,栈区由系统管理内存(出生命周期被自动释放掉)

堆区,对象在堆区,堆区的每一块内存不能起名,堆区的内存需手动释放   

 int a = 10;
 Person *p = [[Person alloc] init];

a 在栈区,p 在栈区(指向Person所产生的对象),Person 在堆区

栈区 p  --> 堆区 对象

p 释放掉,对象没有释放 --> 内存泄露

对象释放掉,p没有释放掉 --> 野指针异常


3. 成员变量、局部变量

成员变量和局部变量的最大区别在于生命周期

局部变量最大的特点是它只是在方法内部定义的变量(变量名的生命周期)

引用计数的平衡:局部变量保证在它所在的{}中平衡,成员变量保证在类中平衡

// 只是在方法内部定义的变量(变量名的生命周期)
- (void)change
{
    NSString *a = @"hello";
    
    // {...} 是单独的可以存在的
    {
        // b 只在它所在的{}中有效,{}是一个生命周期
        NSString *b;
        NSLog(@"a = %@", a);
    }
    
    // b 在它所在的{}外无效
//    NSLog(@"b = %@", b);
}


4. alloc

alloc 代表:创建一个引用计数为1的对象

release:引用计数-1

引用计数添加在堆上

Car *c = [[Car alloc] init];
c.name = @"奥迪";
[c release];

保证在使用的时候安全,保证在不使用的时候消失

谁让引用计数+1,谁就负责引用计数-1

通过类方法创建的对象是自动释放的,它已经添加到自动释放池


5. retain

Car *c2 = [[Car alloc] init];
[c2 retain];
[c2 release];
[c2 release];

retain:引用计数+1

- (void)getUrl:(NSString *)url dic:(NSDictionary *)dic
{
    NSDictionary *myDic = [dic retain];
    
//    ......
    
    [myDic autorelease];
}

- (void)getUrl:(NSString *)url myDic:(NSMutableDictionary *)dic
{
    NSMutableDictionary *mDic  = [NSMutableDictionary dictionaryWithDictionary:dic];
    
//    ......
}
传入的参数是不可变字典(NSDictionary)时,需将dic先retain一下再给myDic赋值,再使用

传入的参数是可变字典(NSMutableDictionary)时,用dictionaryWithDictionary:方法


6. 自动释放池

自动释放池的创建有两种:

(1) 创建一个自动释放池(只能在MRC中使用)

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSAutoreleasePool *pool1 = [[NSAutoreleasePool alloc] init];

// 代码在自动释放池里写
Car *c1 = [[Car alloc] init];
[c1 autorelease];

// 自动释放池释放时,会将自动释放池中的对象一一释放
[pool1 release];
[pool release];

注意:

1) 只有autorelease所修饰的对象才能添加到自动释放池中

2) 对象所添加的自动释放池是离它最近(离[c1 autorelease];最近)的自动释放池中

3) 代码在自动释放池里写

4) 如果想要在自动释放池中创建对象并使用(将对象放到自动释放池中),必须用autorelease进行声明,添加到离它最近的自动释放池中,c1添加到pool1中

5) 自动释放池释放时,会将自动释放池中的对象一一释放

(2) 简易的(既可以在MRC又可以在ARC中使用)

<pre name="code" class="objc">@autoreleasepool {
            
    Car *c = [[Car alloc] init];
    [c release];
}
 

7. 成员变量及属性的内存管理

保证引用计数为1,并在dealloc中释放

1) 如果在类中声明了成员变量,那么成员变量的创建必须在init中并且必须保证引用计数为1,并在dealloc中释放

2) 如果在类中声明了属性,那么对属性的赋值时必须通过 点语法(调用setter geetter) 来进行赋值(为了保证引用计数为1,且避免内存泄露),并在dealloc中释放

例子:

Car.h文件

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

@interface Car : NSObject

{
    @public
    // 成员变量
    NSMutableArray *_array;
}

// 成员变量的setter方法
- (void)setArray:(NSMutableArray *)array;
// 成员变量的getter方法
- (NSMutableArray *)array;

// 属性
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSMutableArray *mArr;
@property (nonatomic, retain) Person *per;

// 便利构造器
+ (Car *)car;
+ (Car *)carWithName:(NSString *)name mArr:(NSMutableArray *)mArr per:(Person *)per;

@end
保证引用计数为1,并在dealloc中释放

1) 如果在类中声明了成员变量,那么成员变量的创建必须在init中并且必须保证引用计数为1,并在dealloc中释放

2) 如果在类中声明了属性,那么对属性的赋值时必须通过 点语法 来进行赋值(为了保证引用计数为1,且避免内存泄露),并在dealloc中释放

Car.m文件

#import "Car.h"

@implementation Car

- (void)dealloc
{
    [_array release];
    [_mArr release];
    [_per release];
    [super dealloc];
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        
        // 成员变量初始化
        // 为了使_array使用安全,有两种方法
        // 1. 用retain
        // 类方法
        // _array已经添加到自动释放池中,假设引用计数为0,为了使引用计数为1,需要retain一下
//        _array = [[NSMutableArray array] retain];
        
        // 2. 用alloc
        _array = [[NSMutableArray alloc] init];// 为了使引用计数为1
        
        // 属性初始化
        // NSString不需要初始化(特殊,引用计数为-1)
        self.mArr = [NSMutableArray array];// 为了使引用计数为1,且避免内存泄露
        // 自定义的对象需要初始化
        self.per = [Person person];
    }
    return self;
}

// 成员变量的setter方法(必须这样写)
// 赋值时要保证赋给的值是安全的
- (void)setArray:(NSMutableArray *)array
{
    // _array和array是指针,指向地址
    if (_array != array) {// 判断_array和array所指向的地址是否一样,不一样时调用这个方法,地址一样的时候存放的值相同,不调用这个方法
        
        // 在OC的开发中空对象调用任何方法是没有意义的,空对象指向的地址是nil
        // 第一次调用时为空,为空时再释放无影响
        // 第二次及以后调用时先将上一次增加的一次释放,再指向下一个地址
        [_array release];// 防止内存泄露
        
        // array(指针) -> 地址  =>  _array -> 地址(与array同一地址)
        _array = [array retain];// 防止成为野指针
    }
}

// 成员变量的getter方法(不用非这样写,可以把retain和autorelease去掉)
- (NSMutableArray *)array
{
    [_array retain];
    
    return [_array autorelease];
}

// 便利构造器所返回的对象添加到自动释放池中
+ (Car *)car
{
    Car *c = [[Car alloc] init];
    
    // 保证在使用的时候安全,保证在不使用的时候消失
    // 谁让引用计数+1,谁负责引用计数-1
    // 返回一个可以使用的值,用autorelease释放
    return [c autorelease];
}

+ (Car *)carWithName:(NSString *)name mArr:(NSMutableArray *)mArr per:(Person *)per 
{
    Car *c = [[Car alloc] init];
    
    // 在便利构造器中对成员变量及属性赋值,不能对成员变量赋值
//    c ->_array = array;
    c.name = name;
    c.mArr = mArr;
    c.per = per;
    return [c autorelease];
}

@end

main 中

Car *c3 = [[Car alloc] init];

//        c3 -> _array = [NSMutableArray array];
//        c3.mArr = [NSMutableArray array];

[c3 release];

重写init方法,在init方法中初始化 成员变量_array 和 属性mArr,在这里就不用初始化了

NSMutableArray *mArr = [NSMutableArray array];
Person *p1 = [[Person alloc] init];
        
Car *c4 = [Car carWithName:@"name" mArr:mArr per:p1];
NSLog(@"%@", c4.name);

[p1 release];

通过便利构造器对属性赋值

打印结果:

name







堆区,对象在堆区,堆区的每一块内存不能起名,堆区的内存需手动释放   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值