zoukankan      html  css  js  c++  java
  • 【转】iOS内存管理

    转自:http://blog.csdn.net/weiqubo/article/details/7376189

    1.  内总管理原则(引用计数)
        IOS的对象都继承于NSObject,   该对象有一个方法:retainCount ,内存引用计数。 引用计数在很多技术都用到: window下的COM组件,多线程的信号量,读写锁,思想都一样。
        
       (一般情况下: 后面会讨论例外情况)
        alloc      对象分配后引用计数为1
        retain    对象的引用计数+1
        copy      copy 一个对象变成新的对象(新内存地址) 引用计数为1 原来对象计数不变
       
        release            对象引用计数-1 如果为0释放内存
        autorelease      对象引用计数-1 如果为0不马上释放,最近一个个pool时释放    
    NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
    [sMessage retain]; //2
    NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
    NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
    NSLog(@"sMessage retainCount:%u",[sMessage retainCount]);
        内存管理的原则就是最终的引用计数要平衡,
       如果最后引用计数大于0  则会内存泄露
       如果引用 计数等于0还对该对象进行操作,则会出现内存访问失败,crash    所以尽量设置为nil
       
       这两个问题都很严重,所以请一定注意内存释放和不用过后设置为nil
    2. autoReleasePool
       每个工程都有一个 main.m 文件: 内容如下:
       int main(int argc, char *argv[]) {
       
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
        int retVal = UIApplicationMain(argc, argv, nil, nil);
        [pool release];
        return retVal;
    }
       很明显C语言的main 函数: 
      NSAutoreleasePool 是用来做autorelease 变量释放的,前面说了, autorelease不会马上释放,当他到了最近的pool release 时会检查reatin count 是不是为0, 为0就释放。 
    当我们在一段代码时加入了大量的autorelease变量时,我们应该为这段代码加上
    Autoreleasepool,其它时候不用
    在返回一个对象时常常要返回一个autorelease 对象,因为objectC 对象都是动态内存,没有
    栈的概念,所以不能像C/C++一样返回一个对象到栈,只能用autorelease对象。
    3. 成员变量与属性
        实际情况并非上面那么简单,你可能需要在一个函数里调用另一个函数分配的变量这时候
    有两个选择:  类成员变量和使用属性
       @interface TestMem: NSObject {
                TestObject   *m_testObject ;            // 成员变量
                TestObject   *testObject;                 //成员变量
        }
     成员变量与上面的内存管理是一致的,只是在不同的函数里要保持引用计数加减的平衡
    所以要你要每次分配的时候检查是否上次已经分配了。是否还能调用 
    什么时候用属性?
      1. 把成员做为public.
      2. outlet 一般声明为属性( 这个内存于系统控制,但我们还是应该做一样操作,后面会讲)
      3. 如果很多函数都需要改变这个对象 ,或这个函数会触发很多次,建议使用属性。我们看看属性函数展开后是什么样子:

    // assign
    -(void)setTestObject :(id)newValue{
        testObject= newValue;
    }
    // retain
    -(void)setTestObject  :(id)newValue{
        if (testObject!= newValue) {
            [testObject release];
            testObject= [newValue retain];
        } 
    }
    // copy
    -(void)setTestObject :(id)newValue{
        if (testObject != newValue) {
            [testObject release];
            testObject = [newValue copy];
        }
    }
    asssign 相于于指针赋值,不对引用计数进行操作,注意原对象不用了,一定要把这个设置为nil
    retain 相当于对原对象的引用计数加1
    copy 不对原对象的引用计数改变,生成一个新对象引用计数为1
    注意:
      self.testObject 左值调用的是setTestObject 方法. 右值为get方法,get 方法比较简单不用说了
      而 真接testObject 使用的是成员变量
      self.testObject = [[testObject alloc] init];   // 错  reatin 两次
      testObject =   [NSArray objectbyindex:0];  //错 不安全,没有retain 后面release会出错 

       上面这句,正确的写法是?
                                                                      如果testObject已有值也会mem leak       
    4. 自动管理对象
       IOS 提供了很多static(+) 创建对象的类方法,这些方面是静态的,可以直接用类名
    调用如:
       NSString *testString = [NSString stringWithFormat:@"test" ];
       testString 是自动管理的对象,你不用relese 他,它有一个很大的retain count, release后数字不变。

    用类方法创建的对象,都有上述属性吗?
      
    5. 例外
       有一些通过alloc 生成的对象相同是自动管理的如:
       NSString  *testString  = [[NSString alloc] initWithString:@"test1"];
       retain count 同样是很大的数,没办法release
       但为了代码对应,还是应该加上[ testString release];
       不然xcode的Analyze 会认识内存leak, 但Instruments leak 工具检测是没有的
    6.view  内存管理
       通常我们编程会用到view,   在view 中的方法:
       
      viewDidload 
      didReceiveMemoryWarning 
     viewDidUnload 
    @property (retain) NSArray *iarrTestMem
            
            viewDidLoad
                init retain is         0
                (alloc)               +1 =1
                                        
            if(memwarning)
                   didrecivememwarning  -0 =1
                                                   
                   didviewunload        -1
                                               =0  
                                      will load  viewDidLoad when next load view
            esle
                   dealloc:             -1
                                          =0
        我们来看一个变量的生命周期 
     
       当view被alloc
         A. 框架会自动调用viewDidLoad
             一般来说在viewDidLoad 中分配变量: 
             假设为alloc 现在变量的reatin 为 0 +1 = 1;
         
         第一种情况:
          B. 如果这时候view被释放,会调用 dealloc, 
            这时候我们应该在dealloc里release 这个变量现在为0
         
         第二种情况:
           B. 这时候view被换到下层,如navigation的上级,不可显示状态:如果
              系统内存吃紧: 系统会发 消息,我们的didrecivememwarning 函数
              被调用 ,该函数是提醒我们应该释放一些现在用不上的东西,特别是一些较大的
              如图片,cache 数据等, 注意如果在这里释放了,代码在用的地方要进行
              判断,是否需要重新加载
           C. 他会调用didviewunload
              这时候我们要注意了,因为他调用了didviewunload,在下次这个view被显示的时候
             会再次调用didviewload, 我们在didviewload里分配的娈童就会被分配两次,所以我
             们在这里一定要释放didview里分配的变量,不管直接分配的还是间接分配的,
             如发消息给其它函数分配的。 那我们在下次分配之前释放一次行不行, 不行,因为这
             时候内存吃紧了,你应该多释放内存,不然你的程序可能会被系统kill. 还有虽然对
             [xxx release]  对nil 发 release 是没问题的,但逻辑上让人觉得很奇怪,以为在别的
             地方分配过。 所以这里应该释放内存,
                如果你是一个属性,用:
                      self.xxx = nil 比较好,他相当于帮你释放了原来的,还把xxx设置为了nil.
               如果不是一个属性 :也最好把xxx= nil.  这样比较安全。如果这个view不再被换入
              下一步直接调用 dealloc, 这时候你的dealloc里的[xxx release], 如果这里的xxx不为nil你在
              didviewunload里release 了,就非常危险
            D. 如果这时候view被释放,会调用 dealloc, 
               这时候我们应该在dealloc里release 
        
    前面我们说了outlet的内存没办法管理(看下生命周期)
       
       
      @property (nonatomic,retain) IBOutlet UILabel *ilblTestMem;
             init retain is 2

             
             didviewLoad:     
                                  = 2
             if(memwarning)
                     memwaring       - retain count -1
                                             = 1
                    viewdidunload:   -1  
                                             =0
                                  will load  viewDidLoad when next load view
            else
                 dealloc:         -1
                                    =1
          第一种情况
           A. didviewLoad:
                    retain count 为2 
           B.  dealloc:
                     -1   retain count 为1
          第二种情况:
           B.   memwarning 
                   系统会把 retain count 减到1
           C.   viewdidunload
                   我们应该release 一次 这样 retain count 到0,变量会马上dealloc,更快的释放内存
                   注意用 self.ilblTestMem = nil  设置为nil,  为 dealloc 做准备
           D.  dealloc:
                  -1 

    NSObject *sMessage = [[NSObjectalloc]init]; //1

         

    [sMessage release]; //1

    NSLog(@"sMessage retainCount:%u",[sMessage retainCount]); 

     

    [sMessagerelease]; //0

    //not crash  (retainCount also can call)

    //crash  (can not call)

    [sMessage release];

    @property (nonatomic, retain) TestObject  testObject*; //为testObject成员变量生成属性方法

    @end

    补充说明

    在研究retain count的时候,我不建议用NSString。因为在下面的语句中,

    NSString *str1 = @”constant string”;

    str1retain count是个很大的数字。Objective-C对常量字符串做了特殊处理。

    当然,如果你这样创建NSString,得到的retain count依然为1

    NSString *str2 = [NSString stringWithFormat:@”123”];

    涉及的示例程序代码(已去除隐藏,觉得有用的话请顶一下此文):https://files.cnblogs.com/VinceYuan/objective-c-memman.zip

  • 相关阅读:
    【简单易懂】JPA概念解析:CascadeType(各种级联操作)详解
    [转] @JoinColumn 详解 (javax.persistence.JoinColumn)
    Feign二: @FeignClient 接口调用
    @Basic表示一个简单的属性 懒加载,急加载
    RPC接口测试(一)什么是 RPC 框架
    Mysql中 查询慢的 Sql语句的记录查找
    Mysql 查看连接数,状态 最大并发数
    ntp时间一致对与设备心跳的影响
    jmeter常用四种断言
    jmeter BeanShell断言(四)
  • 原文地址:https://www.cnblogs.com/ygm900/p/2880886.html
Copyright © 2011-2022 走看看