iOS 内存管理

来源:互联网 发布:趣味编程100例 编辑:程序博客网 时间:2024/05/29 17:15

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];

       //2NSLog(@"%ld", [person retainCount]);

      [person retain];

       //3NSLog(@"%ld", [person retainCount]);

       [person release];

       //2NSLog(@"%ld", [person retainCount]);

       [person release];

       //1NSLog(@"%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

{

    //释放旧的,持有新的对象//为了避免相同对象赋值之后造成的对象意外释放,需要判断对象是否相同方法1if (_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;


    }

    returnself;

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];//2NSLog(@"person = %ld",[person retainCount]);

      [person retain];//3NSLog(@"person = %ld",[person retainCount]);

      [person autorelease];//3NSLog(@"person = %ld",[person retainCount]);

      [person autorelease];//3NSLog(@"person = %ld",[person retainCount]);

      [person autorelease];//3NSLog(@"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创建的对象,是在堆中分配内存的,遵循引用计数模式

0 0
原创粉丝点击