美文网首页
内存管理

内存管理

作者: 爱派森 | 来源:发表于2015-11-16 13:46 被阅读959次

    一.基本介绍(先创建一个空工程待用)

    1)为什么要管理内存?

       因为手机内存大小有限,如果有内存分配但是不释放它,哪怕这块内存已经不用了.导致你的应用程序占用越来越多的内存,并导致整体性能下降,或者直接在真机上闪退.因此需要把不必要的内存空间给释放掉.
    

    2)OC内存管理的三种方式

    Objective-C提供了三种内存管理方式:
        1)manual reference counting(MRC,手动管理)
        2)automatic reference counting(ARC,自动引用计数)
        3)garbage collection(GC,垃圾回收).OS X 10.8(山狮)被废弃,使用ARC
       IOS不支持垃圾回收;ARC作为苹果,在XCode4.2以后(IOS5)新提供的技术,苹果推荐开发者使用ARC技术来管理内存;但是我们从手动管理开始学起.
    

    3)新创建的工程默认都是ARC(自动管理),修改为手动管理

    1)选中蓝色的项目工程文件
    2)中间部分找到build settings选项
    3)搜索gar,把那个选项改为NO
    

    4). 怎么知道创建的对象被释放?(画图来说明:人和牛的例子)

       每个OC的对象都具备一个属性叫做retainCount(该对象被多少个人套住,有多少人在使用),如果这个属性>=1,意味着至少有一个人再使用,只有当retainCount变为0,苹果自动释放
    
    testOne:
        People * zhangSan = [[People alloc]init];
        NSLog(@"1、引用计数 = %d",zhangSan.retainCount);
        [zhangSan release];
        NSLog(@“2、引用计数 = %d",zhangSan.retainCount);
        
        //重写People的dealloc方法,证明被释放
        -(void)dealloc{
            NSLog(@"Invoke Person's dealloc method.”);
            [super dealloc];
            //注意最后一定要调用父类的dealloc方法(两个目的:一是父类可能有其他引用对象需要释放;二是:当前对象真正的释放操作是在super的dealloc中完成的)
        }
    
    总结
        1.alloc/new会导致引用计数+1
        2.release  会导致引用计数-1
        3.对象在使用完成之后不会自动销毁,如果不释放会内存泄露
        4.已经释放的数据再次使用/释放(过度释放)会崩溃,通常会出现Thread 1:EXC_BAD_ACCESS
        '(code=EXC_I386_GPFLT)错误,这是野指针错误,因为你访问了一块已经不属于你的内存
            解决办法:[obj release]; obj = nil;因为给空对象发送消息是不会引起错误的
        5.对象被释放的时候会调用自己类的delloc方法
    
    testTwo:
        People * zhangSan = [[People alloc]init];
        NSLog(@"1、引用计数 = %d",zhangSan.retainCount);
        [zhangSan retain];
        NSLog(@"2、引用计数 = %d",zhangSan.retainCount);
        [zhangSan retain];
        NSLog(@"3、引用计数 = %d",zhangSan.retainCount);
        [zhangSan release];//alloc
        [zhangSan release];//retain
        [zhangSan release];//retain
    
    总结:
        1.retain会导致引用计数+1
        2.谁创建谁释放,谁使用谁管理
        3.引用计数+1的操作称为”拥有take ownership of”,-1的操作称为”放弃relinquish”,只有在拥有对象的时候才可以放心的使用,当它被所有人放弃的时候Objective-C运行环境会自动回收这个对象
    
    testThree(给People类扩充一个myLabel属性,一个age属性)
        People * p = [People new];
        UILabel * a = [UILabel new];
        NSLog(@"a 的引用计数为”,a.retainCount);
        p.myLabel = a;
        NSLog(@"a 的引用计数为”,a.retainCount);
        [a release];
        NSLog(@"a 的引用计数为”,a.retainCount);
        [p release];
        
    总结:
        1.给属性赋值,会导致引用计数+1
        2.对于类中声明的属性一般在delloc方法中释放,释放后记得=nil
        3.对于assign修饰的属性不需要内存管理,因为内存管理是针对于对象的.
    
    testFour:
        People * p1 = [People new];
        People * p2 = p1;
        NSLog(@"p1的引用计数为”,p1.retainCount);
        NSLog(@"p2的引用计数为”,p2.retainCount);
        [p1 retain];
        NSLog(@"p1的引用计数为”,p1.retainCount);
        NSLog(@"p2的引用计数为”,p2.retainCount);
        
    结论:
        1.指针赋值不会让引用计数+1
        2.p2的引用计数等于p1的,p1改p2也改
    
    
    testOne
        UILabel * label = [UILabel new];
        NSLog(@"label的引用计数为”, label.retainCount);
        [self.window addSubView:label];
        NSLog(@"label的引用计数为”, label.retainCount);
        [label release];
    
    结论:
        1.addSubView会导致引用计数+1,因为它被self.window拥有,遵循谁拥有谁管理原则,它内部会自己处理内存问题,不用我们释放
        2.什么时候release?当你确定该对象不会被使用了,就可以release,一般写在最后.
    
    testTwo(脱两个xib控件并且绑定对象)
        NSLog(@"a %d”,a.retainCount);
        NSLog(@"b %d”,b.retainCount);
        
    结论:
        1.通过拖拽xib,引用计数+1,原理同结论1,而且系统会在delloc中自己释放
        2.全局变量一般在delloc中释放,局部变量用完就release
        3.一般来说,区间1和区间2的对象属性都在delloc方法中释放
    
    testThree
        -(void)textFout {
            UILabel * label = [[self createLabel] retain];
            [label release];
            label = nil;
        }
        -(UILabel *)createUILabel {
            UILabel * label = [UILabel new];
            ******做一些其他事情*****
            return [label autorelease];
        }
    
    结论
        1.autorelease自动释放
            * 对象执行autorelease方法时会将对象添加到自动释放池(autorelease pool)中
            * 当自动释放池销毁时自动释放池中所有对象作release操作,如果引用计数变为0就释放
        2.一般来说,如果方法最后要返回一个对象,一般需要使用autorelease去释放
        3.release 和 autorelease 的区别?
            * 如果能确定,使用release
            * 不能确定,使用autorelease
        注:autorelease不是随便用的
            * 操作占用内存比较大的对象的时候不要随便使用,担心对象释放的时间太迟,可能会泄露
            * 操作占用内存比较小的对象可以使用
            * 自动释放池存储于内存中的栈中遵循"先进后出”原则
            * 自动释放池实质是当自动释放池销毁后调用对象的release方法,不一定就能销毁对象(例如如果一个对象的引用计数器>1则此时就无法销毁)
    
    testFour
        UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
        NSLog(@“btn",btn.retainCount);
        NSString * name = @"zhanSan";
        NSLog(@" name",name.retainCount);
        NSString * name1 = [NSString stringWithFormat:@“zhangSan"];
        NSLog(@" name",name.retainCount);
    结论:
        1.ObjC中类库中的+号方法(静态方法)创建对象一般都不需要手动释放,内部已经调用了autorelease方法;
        2.字符串是一个比较特殊的对象,但是只要人为+1,就要人为-1
    
    testFive
        UILabel * label = [UILabel new];
        NSLog(@"%d",label.retainCount);
        NSArray * array = [NSArray arrayWithObjects:label,label, nil];
        NSLog(@"array:%d",array.retainCount);
        NSLog(@"%d",label.retainCount);
        [label release];//new
    结论:
        1.数组的addObject会让对象的引用计数+1.
        数组在释放的时候,会对里面的对象执行release,所以不用我们管.
    
    testSix
        导航传值导致引用计数+1
        界面传值容易出错(记得全局变量到delloc中释放)
    
    * _oneArray     = [array retain];
    * self.oneArray = array;
    
    test.png
       @property的参数分为三类,也就是说参数最多可以有三个,中间用逗号分隔,每类参数可以从上表三类参数中人选一个。如果不进行设置或者只设置其中一类参数,程序会使用三类中的各个默认参数,默认参数:(atomic,readwrite,assign)
    一般情况下如果在多线程开发中一个属性可能会被两个及两个以上的线程同时访问,此时可以考虑atomic属性,否则建议使用nonatomic,不加锁,效率较高;readwirte方法会生成getter、setter两个方法,如果使用readonly则只生成getter方法;关于set方法处理需要特别说明,假设我们定义一个属性a,这里列出三种方式的生成代码:
    
    assign,用于基本数据类型
    -(void)setA:(int)a{
        _a=a;
    }
    
    retain,通常用于非字符串对象
    -(void)setA:(Car *)a{
        if(_a!=a){
            [_a release];
            _a=[a retain];
        }
    }
    
    copy,通常用于字符串对象
    -(void)setA:(NSString *)a{
        if(_a!=a){
            [_a release];
            _a=[a copy];
        }
    }
    
    内存测试
        NSArray * array1 = [NSArray arrayWithObjects:@"1", nil];
        self.array4 = array1;
        NSArray * array2 = [[NSArray alloc] initWithObjects:@"array1",array1, nil];
        self.array4 = array2;
        NSArray * array3 = [NSArray arrayWithObjects:array1,array2, nil];
        self.array4 = array3;
        //3--2--2--2
    
    内存管理的知识点
        1.让对象引用计数增加的9个操作
        alloc,new,copy,retain,addSubView,addObject,push,对象打点属性赋值,控件关联xib
        2.引用计数减少的两个操作
        release  autorelease
        3.要保证让对象引用计数增加的关键字跟让对象引用计数减少的关键字 配对
        4.全局变量或者属性在dealloc中释放,局部变量在确定不使用以后就要释放
        5.使用+号方法(静态方法)创建的对象不需要管理内存,内部自动-1
        6.在方法中创建的对象如果要返回需要使用autorelease,在使用的时候手动retain
        7.全局对象变量在初始化的时候一般使用-号方法创建,使用+号方法创建需要手动retain,使用字面量创建需要手动retain
        8.分线程中的所有代码都用@autorelease括起来,防止内存泄露
        9.对象在释放的时候除了[obj release];以外,为了安全起见,加上obj=nil;
        10.属性直接self.obj = nil;即可,相当于[obj release];obj=nil;  
    ARC和MRC的关系
        ARC是编译器特性,不是运行时特性.简单说就是代码在编译的时候自动加入了retain/release/autorelease,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了.因此ARC和MRC性能是一样的,有些时候还能更快,因为编译器还可以执行某些优化
    内存管理的小技巧
        1)利用你静态分析工具让系统自动检测内存泄露的代码(只能检测一部分)
        1.选中项目工程文件
        2.中间部分,选择蓝色的project
        3.选中build settings,搜索Analyze During
        4.把Analyze During ’Build’修改为YES
    
        2)
          strong           weak
          retain,copy      assign
          strong:强引用,只要对象有人在用就不会被释放掉
          weak:弱引用,并不是对象的真正的拥有者,如果对象被释放,指针自动置为nil
          为了方便释放对象,定义一个简单的宏定义
          #define Release(obj) [obj release];obj=nil;
    
    循环引用
        1.定时器导致的循环引用
        定时器在内部会引用self,导致self在释放的时候并不会释放掉,因此在释放对象之前需要先把定时器给关闭掉.释放的代码[_timer invalidate];_timer = nil;在delloc方法中调用无效,一般在viewDidDisappear中关闭定时器.
        2.代理导致的循环引用 A,B,C
        在A中创建B和C,引用计数分别为:A1,B1,A.delegate = B,引用计数变为:A1,B2.如果A释放,B释放,那么B变为A,导致A.delegate也是1,所以A释放不掉,B也没释放掉.
        解决办法就是:delegate用assign修饰
        3.block导致的循环引用

    相关文章

      网友评论

          本文标题:内存管理

          本文链接:https://www.haomeiwen.com/subject/zbvkhttx.html