iOS memory management 1

来源:互联网 发布:四川省统计局大数据 编辑:程序博客网 时间:2024/05/22 06:14

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(@"sMessageretainCount:%u",[sMessage retainCount]);
NSLog(@"sMessageretainCount:%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= [newValueretain];
   
}
// 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 =   [NSArrayobjectbyindex:0];  //错 不安全,没有retain后面release会出错
                                                       如果testObject已有值也会mem leak      
4. 自动管理对象
   IOS 提供了很多static(+)创建对象的类方法,这些方面是静态的,可以直接用类名
调用如:
   NSString *testString =[NSString stringWithFormat:@"test" ];
   testString 是自动管理的对象,你不用relese他,他有一个很大的retain count, release后数字不变。
  
5. 例外
   有一些通过alloc生成的对象相同是自动管理的如:
   NSString *testString  = [[NSStringalloc] initWithString:@"test1"];
   retain count同样是很大的数,没办法release
   但为了代码对应,还是应该加上[ testStringrelease];
   不然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 
                            willload  viewDidLoad when next loadview
      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;
        initretain is 2
       
       didviewLoad:    
                         = 2
       if(memwarning)
              memwaring      - retain count -1
                                  = 1
             viewdidunload:  -1  
                                  =0
                        will load viewDidLoad when next loadview
      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

     

[sMessagerelease]; //1

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


[sMessagerelease]; //0

//not crash  (retainCount alsocan call)

//crash (can not call)

[sMessagerelease];

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

@end

 

转自:http://demo.netfoucs.com/yezibao1991/article/details/12610403

 


原创粉丝点击