OC内存管理

OC的内存管理分为ARC和MRR两种机制,前者是系统自动去管理内存的,后者是我们主动去管理内存的。在这里我们主要讨论的后者。

 引用计数 retainCount

无论是ARC还是MRR都是通过OC的引用计数机制来管理内存的(也就是retainCount) ,每当一个对象被创建起来时,它的引用计数就加一,当某个对象的引用计数为0时,说明这个对象就没有被使用了,也就是说这个对象的拥有者就为0了,这时编译器就会摧毁这个对象。当外部需要使用这个对象时,一般都是通过传递参数的形式来传递这个对象,此时这个对象在传递时应声明为strong或者是retain来表示对这个对象的拥有权,此时retainCount会加一。如果外部不在使用这个对象了,就使用release来将外部对这个对象的引用释放掉,此时retainCount减一。当retainCount等于0时,编译器就会摧毁掉这个对象。

内存管理的两种错误

1.由于没有声明为strong对使用对象的强引用,使用对象被直接释放掉了,或者重写了对象。

 2.没有释放不再使用的对象,而造成内存泄漏。

 对象的拥有权(什么时候该释放一个对象)

1.自己创建的对象 就有拥有权,通过alloc copy mutablecopy new创建的对象就有拥有权,实际上,只要见到这几个关键字,就应该要想到要释放对象。



@interface Test : NSObject
@property (nonatomic, strong) NSString *address;
@property (nonatomic, assign) int  age;//C语言的基本数据类型
@end

@implementation Test
- (void)dealloc{
    [self.address release];//property属性
    
    [super dealloc];
}

- (NSString *)test1{
    
    NSString *name = [[NSString alloc] init];
    [name autorelease];//延迟释放
    return name;
}

- (void)test2{
    NSString *temp = [[self test1] retain];
    NSLog(@"%@", temp);
    [temp release];
}
@end

在Test类的方法test1实现中,我们创建了一个oc字符串对象name,并且调用了init方法进行了初始化,但是这个对象的生命周期仅在当前代码块中。由于是MRR模式,我们需要手动释放这个对象所占的内存,但是我们又要返回一个字符串对象,因此我们调用了一个autorelease方法,来延迟释放name。autorelease的工作机制就是把调用了这个方法的对象放入自动释放池中,然后系统每隔一段时间去检查自动释放池中该对象的引用计数,如果该对象的引用计数为0了,那么系统就会摧毁掉这个对象。如果不释放对象就会造成内存泄漏的问题。

在方法test2的实现中,我们又创建了一个OC的临时对象--temp,虽然我们并没有看到任何的以上所说的标志性的关键字,但是我们看到在 test1 方法的返回值的右边我们用了一个属性关键字--retain (可以使用retain声明对一个不是自己创建的对象的拥有权)  ,这个关键字说明了这个temp对 test1 返回对象的拥有权,换句话说,现在test1 返回对象的引用 计数就是 2 了,所以尽管没有看到那些关键字,我们还是要在这个代码块结束之前释放掉temp对那个对象的拥有权,因此在 [temp release] 后,那个对象的引用计数就是 1 (2-1)了。

使用dealloc去释放对象自己拥有的资源

我们需要重写NSObject的dealloc方法,但是我们不会主动去调用某个对象的dealloc方法,这个方法是系统自己主动调用的。当一个对象的retainCount = 0 系统就会摧毁这个对象,但是在释放这个对象之前 系统会去这个对象里面查找是否实现了dealloc方法,如果实现了,就主动调用然后将这个对象释放掉。

释放对象的步骤:

1. 调用对象的dealloc方法   

      1.释放掉自己拥有的资源 

      2.调用super dealloc

2. 释放这个对象



@interface Test : NSObject
@property (nonatomic, strong) NSString *address;
@property (nonatomic, assign) int  age;
@end

@implementation Test

- (void)dealloc{
    [self.address release];//property属性
    
    [super dealloc];
}

- (NSString *)test1{
  
    NSString *name = [[NSString alloc] init];
    [name autorelease];//延迟释放
    return name;
}

- (void)test2{
    NSString *temp = [[self test1] retain];
    NSLog(@"%@", temp);
    [temp release];
}
@end

还是上面的Test类的实现,我们看到dealloc的具体实现,但却没有看见它的声明,因为这个方法是系统自己调用的,我们是不会主动去调用这个方法的。在dealloc方法中,我们看到了对当前对象的address调用了release方法,由于这里我们确定是在这里此刻释放掉address了(dealloc方法里),所以我们没有使用autorelease,由于age是C语言基本类型定义的对象,所以我们不用主动释放掉它,之后我们调用父类的dealloc方法来释放掉子类的这个对象。由于当前的Test对象是无法自己释放自己的,由他的初始化就可以看出来,它是由他的父类的init方法来初始化的(我们没有重写它的父类的方法),所以释放也还是由让他的父类的dealloc方法来释放掉Test对象。

#import <Foundation/Foundation.h>
#import "Test.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString *add = [[[NSString alloc] initWithString:@"rc"] autorelease];
        //retainCount 1
        
        Test *t = [Test new];
        t.address = add;//retainCount 2
        
        [t.address length];
    }
    return 0;
}

我们到主程序中来看看,我们创建了一个OC的字符串对象 add ,并且自定义了它的init方法,然后我们为了防止后面忘记释放这个对象,所以我们就把他暂时放到了自动释放池中,然后我们创建了一个Test 对象 t ,然后调用了 t 中的address属性变量的set方法,由于address是strong的,所以引用计数要加一。

猜你喜欢

转载自blog.csdn.net/qq_41856760/article/details/81253623