OC-内存管理

1.什么是内存?

程序运行中临时分配的存储空间,在程序结束后释放;

2.为什么我们要进行内存管理?

很多同学,玩过手机游戏吧,有没有发现你经常玩个4,5或者更长时间的游戏,要么手机变的很卡,要么直接闪退,这里他就体现了内存管理。

一般我们手机的内存是有限的,程序的内存会随着你程序的不断运行,对象不断增加,内存也会成成成的往上涨,一直到系统资源被耗尽。

mac OS和iOS在内存管理上有很大的区别,Mac OS 有垃圾回收机制,但iOS没有,iOS内存远不足于Mac OS系统,iOS将内存管理的任务交给了开发者。

3.内存管理的原理

保证每个对象在使用的时候存在于内存中,不用的对象在最后从内存中清除。
一个对象的生命周期:
1.对象的初始化
2.执行,使用
3.释放
对象使用内存结束后,需要释放自身所占的资源,归还给系统,以保证别的对象可以使用。
注意:内存管理只针对继承NSObject的对象,对其他的基本数据类型(int,float,double...)无效。

本质原因是因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放于栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也被回收,此时对象已经没有指针指向,但依然存在于内存中,造成内存泄露。

4.retainCount

Cocoa采用引用计数的技术来确定对象的生命周期结束
我们通过retainCount来知道对象是否以利用完改内存


在引用计数中,每一个对象负责维护对象所有引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到零时,该对象就将释放占有的资源。

这里写图片描述

1.每个OC对象都有自己的引用计数器,retainCount
2.当对象被创建时,引用计数置为1(alloc、copy或则new),注意是置为1,不是加1
3.使对象的引用计数+1,并且获得对象的所有权;
4.使对象的引用计数-1,并且放弃对象的所有权;
5.当引用计数值为0的时候,对象将被系统销毁。

那OC中,使用什么来控制对象的引用计数呢?

retainCount:获取对象当前的引用计数值;
alloc:返回一个对象,并将其引用计数设置为1,类方法;
retain:将对象的引用计数加1;
release:将对象的引用计数减1;
autorelease:将对象加入自动释放池,对象引用计数滞后减1;
除了上述方法之外,还有对象销毁的方法dealloc,我们不会主动调用,但是需要在类中重写;

接下来我们在非ARC模式下,进行内存管理

  1. 普通情况下的内存管理
      Person * person = [[Person alloc] init];
       //1
      [person retain];
       //2
      NSLog(@"%ld", [person retainCount]);
      [person retain];
       //3
       NSLog(@"%ld", [person retainCount]);
       [person release];
       //2
       NSLog(@"%ld", [person retainCount]);
       [person release];
       //1
       NSLog(@"%ld", [person retainCount]);
       [person release];
 从我们推断,会发现到最后person对象的retainCount因为为0,为什么他的显示结果依然为1呢?

由于悬垂指针的原因,最后为1,内存已经回收,但是你的指针还存在,感觉还在指向一个内存空间。retainCount仅仅作为参考,不能进行逻辑计算

其实它已经为0,系统回收了这块内存,对象进行销毁。
对象销毁::dealloc,释放对象内部的成员变量,该方法不要手动调用,当对象retainCount为0的时候自动调用

*再次注意:OC中内存管理规则仅仅使用与对象类型,简单,复杂数据类型无需管理内存。可以点击取看,所有的relesea方法,都是基于NSObject

2.内存管理原则

 常见的内存区域:

     堆:需要的时候分配内存,不需要的时候手动释放,编译器不负责释放改区域的内存空间
     栈:需要的时候分配内存,不需要的时候自动释放,编译器负责释放改区域的内存空间
     全局/静态存储区:存储全局变量和静态变量
     常量存储区:存储常量,不可变的数据
     自由存储区

    /*1.使用alloc,new,copy,mutableCopy生成的对象 对象初始引用计数值为1,手动释放内存(堆上的对象)
     2.便利初始化生成的对象,对象初始引用计数值为1,并且设置为自动释放,无需手动释放(栈上的对象)
     3.使用retain持有的对象,需要保证retain和release次数相等*/

3.setter,getter方法的内存管理(重点,难点)

main.m

     Book * book = [[Book alloc] init];
     Person * person = [[Person alloc] init];

     [person setBook: book];
      NSLog(@"%@", person);

     [book   release];
     [person release];

 person.m           
- (void)setName:(NSString *)name
{
    //释放旧的,持有新的对象
    //为了避免相同对象赋值之后造成的对象意外释放,需要判断对象是否相同方法1
    if (_name != name) {
        [_name release];
        _name = [name retain];//保证新的存在
    }
    //方法2
//    [name retain];
//    [_name release];
//    _name = name;

    //如果付的值相同,直接走get方法
}


- (void)setBook:(Book *)book
{
    if (_book != book) {
        [_book release];//放弃旧的所有权
        _book = [book retain];
    }
}

- (Book *)book
{
    return _book;
}

4.自定义初始化

main.m

NSArray *score = [[NSArray alloc] initWithObjects:@"1", nil];
NSLog(@"score = %ld",score.retainCount);//1

 Person *person = [[Person alloc] initWithName:@"lili" score:score age:12];
NSLog(@"score = %ld",score.retainCount);//2,初始化的时候,持有了score
[score release];
NSLog(@"score = %ld",score.retainCount);
person.m
//下划线只是一个成员变量,指针会指向一个内存地址,每次换,每次都会指向一个新的内存地址,不断的换名字,我们要进行管理
- (instancetype)initWithName:(NSString *)name
                       score:(NSArray *)score
                         age:(NSInteger)age{

    self = [super init];
    if (self) {
//对应的release在dealloc方法里面
        _score = [score retain];
        _name = [name retain];
        _age = age;

    }
    return self;
}

5.集合内存管理:字典,数组

 1.对象加入集合对象,+1
 2.对象从集合对象中移除,-1
 3.集合对象的retaincount改变,其中元素的retaincount不便
 4.集合对象销毁时,其中所有的元素release一次
     Person *person = [[Person alloc] init];
     NSLog(@"person = %ld",[person retainCount]);//1

     NSArray *array = [[NSArray alloc] initWithObjects:person, nil];
      NSLog(@"person = %ld,array = %ld",
      [person retainCount],//2
      [array retainCount]);//1
      [array retain];
      NSLog(@"person = %ld,array = %ld",
      [person retainCount],//2
      [array retainCount]);//2
      [array release];
      [array release];
     NSLog(@"person = %ld,array = %ld",
      [person retainCount],//1因为数组没有了,所有的元素都会relea一次
      [array retainCount]);//0(1)

        //字典也使一样的
  1. 自动释放池 NSAutoreleasePool
//NSAutoreleasePool:自动释放池类或对象
//类似于一个容器,所有加入容器中的对象都被他管理,在自动释放池,自身销毁的时候,池将会释放(release)所有的对象
//autorelease 方法把对象加入池中,一个对象可以被多次autorelease
//便利初始化的对象默认已经加入当前池中
       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
       //pool 1
      Person *person = [[Person alloc] init];//1
      [person retain];//2
      NSLog(@"person = %ld",[person retainCount]);
      [person retain];//3
      NSLog(@"person = %ld",[person retainCount]);
      [person autorelease];//3
      NSLog(@"person = %ld",[person retainCount]);
      [person autorelease];//3
      NSLog(@"person = %ld",[person retainCount]);
      [person autorelease];//3
      NSLog(@"person = %ld",[person retainCount]);
      [pool release]; //person = 0(1)
      NSLog(@"person = %ld",[person retainCount]);

7.便利初始化方法

    NSArray *score = [[[NSArray alloc] initWithObjects:@"1", nil] autorelease];
    [person setScore:score];//2
    [pool release];//1
    [score release];
   //可以用来代替release,最后在pool release -1


上面这个和下面的等价
   NSArray *score1 = [NSArray arrayWithObjects:@"1", nil];

8.自定义便利初始化方法

main.m

   Person *person1 = [Person PersonWithName:@"kiki" score:score age:11];
person.m

+ (Person *)PersonWithName:(NSString *)name
                     score:(NSArray *)score
                       age:(NSInteger)age{

    Person *person = [[Person alloc] initWithName:name score:score age:age];
    return  [person autorelease];
}

9.快速生成

  NSArray *array3 = @[@"1",@"2",@"3"];
   //数组,字典的快速生成:便利初始化,不可变数据

10 异常情况

       //1、混乱释放
        Person * person = [[Person alloc] init];
        Person * person1 = person;
        [person1 release];//不遵守内存管理原则      
        [person release] 较标准

    //2、内存泄露
        Person * person1 = [[Person alloc] init];
         Person * person2 = [[Person alloc] init];
         person2 = person1;//指针指向发生改变,person2原有内存 泄露, 解决方案 autorelease

        //3、过度释放
      Person * person = [Person personWithName:@"tom" age:12];
     [person release];//便利构造器初始化的对象,过度释放了,不需要release

        //4、nil对象的引用计数为0
     Person * person = nil;
     NSLog(@"%lu", [person retainCount]);

        //5、常量对象的引用计数为无穷
    NSString * name = @"name";
    NSLog(@"%lu", [name retainCount]);
       //name对象放在常量区 

//6.针对NSString对象
   NSString *string = [[NSString alloc] initWithFormat:@"213"];
        NSLog(@"string = %ld",[string retainCount]);
        //在堆中分配内存,都用引用计数模式,该string对象是常量区,用%d打印是-1,%lu就是正无穷
        NSMutableString *mutableString = [[NSMutableString alloc] initWithFormat:@"111"];
        NSLog(@"mutalbe = %ld",[mutableString retainCount]);
        //nsmutableString创建的对象,是在堆中分配内存的,遵循引用计数模式
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值