学习笔记之内存管理

1.内存管理/引用计数
(1)Cocoa框架中Foundation框架类库的NSObject类担负内存管理的职责。Objective-C内存管理中的alloc/retain/release/dealloc方法分别指代NSObject类的alloc类方法、retain实例方法、release实例方法和deallco实例方法。
(2)内存管理的思考方式:<1>自己生成的对象自己持有:通过alloc/new/copy/mutablecopy生成的对象都是自己持有的。<2>非自己生成的对象自己也可以持有:可以通过retain方法来获得持有权。<3>不再需要自己持有的对象时释放:用alloc/new/copy/mutablecopy/retain获得的对象,当不需要的时候一定要release。autorelease方法可以使取得的对象存在,但自己不持有对象。因为此时这个对象被加入到autoreleasepool中,当这个pool结束时,会释放里面所有的对象,所以自己不能决定该对象的存亡。因此自己并不持有这个对象。<4>无法释放非自己持有的对象:释放非自己持有的对象会造成程序的崩溃。
(3)alloc方法用struct obj_layout中的retained整数来保存引用计数,引用计数保存在引用计数表的记录中。引用计数表各记录中存有内存块地址,可从各个记录追溯到各对象的内存块。

2.ARC
所有权修饰符:
(1)__strong:当该修饰符的变量超出变量作用域,对对象的强引用就会失效,因此自动释放该对象。若对象的所有者也不存在了,就会废弃该对象。同样适用在赋值的情况下,只要对对象的强引用失效(如设为nil)就会释放该对象,若同时对象的所有者不存在了,就会废弃该对象。__strong修饰符的作用机理类似于不使用ARC时,先定义变量的持有者,在变量作用域末尾再对其进行释放。(释放对象和废弃对象是不同的概念。只要强引用失效就会释放对象,此时对应的是将该对象的引用计数减一。若此时该对象的引用计数为0,就会接着废弃对象,但是若还有其他变量引用该对象,则不会对该对象进行废弃)。该修饰符还可以用于类成员变量以及方法参数中。
(2)__weak: 该修饰符是为了防止循环引用导致内存泄露所提供的解决方法。循环引用容易发生内存泄露。所谓内存泄露就是应当废弃的对象在超出其生命周期后继续存在。对自身的强引用也会引发内存泄露。__weak修饰符的变量不持有对象,所以在超出其变量作用域范围时,对象即被释放。且若该对象被废弃,此弱引用将自动失效且处于nil被赋值的状态。
(3)_unsafe_unretained:由于_weak修饰符职能在iOS、osx lion以上的版本中使用。在之前的版本中使用_undafe_unretained修饰符来代替_weak。尽管ARC式的内存管理时编译器来做的,但是改修饰符的变量不属于编译器的内存管理对象。此修饰符变量同样不可以持有任何对象。当强引用超出其作用域失效时,释放该对象之后可以将其废弃掉。和_weak修饰符不同的是,若此时再访问_unsafe_unretained修饰符变量,他代表的对象已废弃,程序就会崩溃。
(4)_autoreleasing:@autoreleasepool块可以用来代替NSAutoreleasePool类对象的生成,持有以及废弃。对象赋值给附有__autoreleasing修饰符的变量等价于在ARC无效时调用对象的autorelease方法,即对象被注册到autoreleasepool中。可非显示使用__autoreleasing修饰符的例子:<1>取得非自己生成但持有的对象时,该对象会自动注册到autoreleasepool中。这是因为编译器会检查方法名是否以alloc/new/copy/mutablecopy开始,如果不是则自动将返回值的对象注册到autoreleasepool中。<2>在访问附有__weak修饰符的变量时,实际上必定要访问注册到autoreleasepool的对象。这是为了防止访问弱引用时,所引用的对象已经废弃。若将访问对象注册到autoreleasepool中,就可以保证在@autoreleasepool块结束之前该对象都是存在的。<3>id指针(id *obj)或 对象的指针(NSObject **obj   对象变量:NSObject *obj)在没有显式指定时会被附上__autoreleasing修饰符,如id __autoreleasing*obj和NSObject* __autoreleasing*obj。即obj指向的对象是指向NSObject对象的引用,而编译器会自动将指向NSObject对象的引用被autoreleasing修饰。在赋值给对象指针时,所有权修饰符必须保持一致。
(5)__strong、__weak、__autoreleasing修饰符都可以保证将附有这些修饰符的自动变量初始化为nil。
(6)牢记:只有作为alloc/new/copy/mutableCopy方法的返回值而取得的对象时,能够自己生成并持有对象。其他情况即为“取得非自己生成并持有的对象”。为了在使用参数取得对象时,贯彻内存管理的思考方式,我们要将参数声明为附有__autoreleasing修饰符的对象指针类型。另外,虽然可以非显式的指定__autoreleasing修饰符,但是在显式的指定__autoreleasing修饰符时,必须注意对象变量要是 自动变量(包括局部变量、函数以及方法参数)。
(7)NSRunloop等实现不论ARC有效还是无效,均能够随时释放注册到autoreleasepool中的对象。
(8)无论ARC是否有效,调试用的非公开函数_objc_autoreleasePoolPrint()都可以使用,该函数可以有效地帮助我们调试注册到自动释放池上的对象。
ARC有效时编码规则:
(1)不能使用retain/release/retainCount/autorelease.
(2)不能使用NSAllocateObject/NSDeallocateObject:生成对象,调用alloc方法的实质就是调用NSAllocateObject方法,所以这个也不能使用。
(3)须遵守内存管理的方法命名规则:以alloc/new/copy/mutableCopy名称开头的方法在返回对象时,必须返回给调用方所应当持有的对象。ARC有效时,init开始的方法必须是实例方法,并且必须要返回对象。返回的对象应为id类型或该方法声明类的对象类型,或是该类的超类或子类型。该返回对象并不会注册到autoreleasepool中,基本上只是对alloc方法返回值的对象进行初始化处理并返回该对象。
(4)不要显示调用dealloc函数:可以重写该函数,定义废弃对象时所需要的操作,但是不能显示的调用,例如[super dealloc]。
(5)使用@autoreleasepool块替代NSAutoreleasePool
(6)不能使用区域NSZone.
(7)对象型变量不能作为C语言结构体的成员。
(8)显示转换id和void*:__bridge:指向单纯的赋值,不持有对对象的引用。__bridge_retained:可使要转换赋值的变量也持有所赋值的对象(类似retain,两者都持有对象)。__bridge_transfer:被转换的变量所持有的对象在该变量被赋值给目标变量后随之释放(类似release,新的持有,旧的不再持有)。
 (9)id obj = [[NSMutableArray alloc] init]; CFMutableArrayRef cfObject = (_bridge CFMutableArrayRef)obj;   将oc对象转换为cf对象时,_bridge转换不改变对象的持有状况。所以通过变量obj的强引用,引用计数为1。
         CFMutableArrayRef cfObject = CFArrayCreateMutable(kCFAllocatorDefault,0,NULL);  id obj = (_bridge id)cfObject;  将cf对象转换成oc对象时,因为是赋值给_strong修饰符的变量中,所以会发生强引用,obj和cfObject都持有对象的引用,引用计数为2;
数组:
(1)_unsafe_retained修饰符以外的_strong/_weak/_autoreleasing修饰符保证其指定的变量初始化为nil.同样这些修饰符变量的数组也保证其初始化为nil.
(2)在静态数组中,编译器能够根据变量的作用域自动插入释放赋值对象的代码,而在动态数组中,编译器不能确定数组的生存周期,所以无从下手,只能自己释放所有元素。释放元素时,必须将nil赋值给所有数组元素之后再废弃内存块,否则会引起内存泄漏。
(3)声明动态数组要用指针。
(4)使用calloc函数确保想分配的附有_strong修饰符变量的容量占有的内存块。因为附有_strong修饰符的变量前必须先将其初始化为nil。该函数可以使分配区域初始化为0.
可能会发生内存泄漏的情况:
(1)strong类型变量的循环自引用  (2)_bridge修饰符将cf对象转换成strong类型的oc对象时,若没有进行CFRelease,容易发生内存泄漏。(3)在动态数组中,若数组中的对象都是strong类型,由于编译器不能确定数组的生存周期,所以不恩那个自动释放对象,只能手动释放。释放对象时必须将数组中的所有元素都释放之后再废弃内存块,如果直接废弃内存块,就会引起内存泄漏。

3.ARC实现
(1)__strong:
id _strong obj = [[NSObject alloc] init];==>id obj = objc_msgSend(NSObject,@selector(alloc));
                                            objc_msgSend(obj,@selector(init));
                                            objc_release(obj);//变量作用域结束时,编译器自动插入release方法,释放对象
<pre name="code" class="objc" style="font-size: 14px;">id _strong obj = [NSMutableArray array];==>id obj = objc_msgSend(NSMutableArray,@selector(array)); 
                                         //array函数内部调用objc_autoreleaseReturnValue(obj)将函数返回注册到autoreleasepool中的对象。
                                            objc_retainAutoreleasedReturnValue(obj);
                                            //若objc_autoreleaseReturnValue(obj)函数之后紧接着调用了该方法,那么就不讲返回的对象注
                                              册到autoreleasepool中,而是直接传递到方法或函数的调用方,达到最优化。
                                            objc_release(obj);

 
  
(2)__weak:若附有_weak修饰符的变量所引用的对象被废弃,则将nil赋值给该变量。使用附有_weak修饰符的变量,即是使用注册到autoreleasepool中的对象。
id _weak obj1 = obj;(其中obj是strong类型持有对象的变量)==>id obj1;
objc_initWeak(&obj1,obj);==>obj1 = 0; objc_storeWeak(&obj1,obj);将所引用的对象地址作为键值,将weak引用变量注册在该键值对应的散列表里。
objc_destroyWeak(&obj1);==>objc_storeWeak(&obj1,0);把变量的地址从weak表里删除。
大量使用-weak修饰符的变量会消耗相应的CPU资源。所以最好只在避免循环引用的时候使用。
       {
          id _weak obj1 = obj;
         NSLog(@"%@",obj1);    =====>
      }

        id obj1;
       objc_initWeak(&obj1,obj);     //注册到weak表
       id temp = objc_loadWeakRetaind(&obj1); //从表中取出所引用的对象并retain
       objc_autorelease(tmp); //将取出的对象注册到autoreleasepool中
      NSLog(@"%@",tmp);
      objc_destroyWeak(&obj1);//把变量的地址从weak表中删除。

(3){
              id _strong obj = [[NSObject alloc] init];
               id _weak o = obj;
               NSLog(@"retain count = %d",_objc_rootRetainCount(obj));
        } NSObject对象的引用计数值为1.
         {
               id _strong obj = [[NSObject alloc]init];
               id _weak o = obj;
               NSLog(@"%@",o);
             NSLog(@"retain count = %d",_objc_rootRetainCount(obj));
        }NSObject对象的引用计数值为2.且此时autoreleasepool中有对变量o的注册。
       这两者的不同就在于是否使用了附有_weak修饰符的变量。第一个没有使用,第二个使用了。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值