OC中引用计数的理解

基于OC中内存管理的一些理解
下面是本人的一些理解

  • 自己生成的对象,自己所持有
  • 非自己生成的对象,自己也能持有
  • 不再需要自己持有的对象时释放
  • 非自己持有的对象无法释放
对象操作 OC方法
生成并持有对象 alloc/new/copy/mutableCopy
持有对象 retain方法
释放对象 release方法
废弃对象 dealloc方法
这里解释一下release方法和dealloc方法的区别

release方法主要是在MRC下释放Foundation框架下面创建的对象,在ARC下面是系统自动的释放
dealloc方法是释放UIKit框架创建的UI界面的内存
(小弟的理解就是这样的,如果有错请大佬指点出来)

自己生成的对象,自己所持有

使用以下名称开头的方法名意味着自己生成的对象只有自己持有

  • alloc
  • new
  • copy
  • mutableCopy
/*
*自己生成并持有的对象
*/
id obj = [[NSobject alloc]init];
/*
*这样创建的对象引用计数+1
*/

非自己生成的对象,自己也能持有

用上述方法以外取得的对象,即alloc/new/copy/mutableCopy以外的方法获取的对象,因为非自己生成并持有,所以自己不是该对象的持有者,比如NSMutableArray类的array类方法创建的对象

/*
*取得非自己生成并持有的对象
*/
id obj = [NSMutableArray array];
/*
*源码中,NSMutableArray类对象被赋给变量obj,但变量obj自己并没有持有该对象。
*/
[obj retain];//obj对象的引用计数+1,调用retain方法之后就变为自己所持有

不再需要自己持有的对象时释放

/*
*自己创建并持有的方法
*/
 id obj = [NSObject new];
/*
*当自己不需要的时候立即释放
*/
[obj release];

非自己持有的对象无法释放

对于自身创建的对象或者持有的对象,在不使用的时候必须释放掉
倘若在程序中释放了非自己所持有的对象程序就会崩溃

id obj = [NSObject new];
[obj release];
/*
*当自身释放了再次释放之后系统就会崩溃
*/
[obj release];

ARC规则

在便以单位上,可设置ARC有效或者无效
一个应用程序可以混合ARC有效或无效的二进制形势
设置ARC有效的编译方式如下所示

  • 使用clang(LLVM编译器)3.0或以上版本
  • 指定编译器属性为“-fobjc-arc”
    当然在Xcode4.2及以上的版本默认设置的对所有的文件ARC有效
    当然MRC的内存管理方式对于ARC一样的适用,即:
  • 自己生成的对象,自己所持有
  • 非自己生成的对象,自己也能持有
  • 不再需要自己持有的对象时释放
  • 非自己持有的对象无法释放

权限修饰符

  • __strong
  • __weak
  • __unsafc_unretained
  • __autoreleasing

__strong

__strong修饰符是id类型和对象类型默认的所有权限修饰符。也就是说,以下源代码中的id变量,实际上被附加了所有权限修饰符
id obj = [[NSObject alloc]init];
id和对象类型在没有明确指定所有权限修饰符时,默认__strong修饰符,上面的源代码与以下相同
id __strong obj = [[NSObject alloc]init];
该源代码在ARC无效时的表述

/*ARC无效*/
id obj = [[NSObject alloc]init];
{
   /*
    *自己生成并且自己所持有
   */
    id __strong obj = [[NSObject alloc]init];
}
//此源码明确的指定了c语言的变量的作用域,ARC无效时,该源代码可描述如下:
/*ARC无效*/
{
   id obj = [[NSObject alloc]init];
   [obj release];
}
/*
*因为变量默认为__strong修饰,当obj超出了其作用域,强引用就会失效
*并且对象所有者不存在,因此废弃该对象
*/

以上是__strong修饰符修饰的自己创建并且自己持有的对象的源码分析
下面简单的说一下非自己生成并持有对象的源码

{
  id __strong obj = [NSMutableArray array];
}
//在NSMutableArray类的array类方法的源码中取得非自己生成并持有的对象
{
/*取得非自己生成并持有*/
 id __strong obj = [NSMutableArray array];
/*
*因为变量obj是强引用,所以自己持有
*/
}
/*
*变量obj超出了作用域,强引用失效,自己释放自己持有的对象
*/

其实附有__strong修饰符的变量之间可以相互赋值

id __strong obj0 = [[NSObject alloc]init];
id __strong obj1 = [[NSObject alloc]init];
id __strong obj2 = nill;
obj0 = obj1;
obj2 = obj0;
obj0 = nil;
obj1 = nil;
obj2 = nil;

当然,即便是OC类成员变量,也可以在方法参数上,使用附有__strong修饰符的变量

@interface Test : NSObject
{
    id __strong obj_;
}
- (void)setObject:(id __strong)obj;
@end
@implementation Test
- (instancetype)init{
    self = [super init];
    if (self) {
        
    }
    return self;
}
- (void)setObject:(id __strong)obj{
    obj_ = obj;
}
@end

正如苹果粑粑说的那样,通过__strong修饰符,不必再次键入retain和release,满足了“引用计数式管理的思考方式”

__weak

看起来好像通过__strong修饰符编译器就能完美的进行内存管理,但是遗憾的是,仅通过__strong修饰符是不能解决有些重大的问题
这里所有的重大的问题就是引用计数式内存管理中必然会发生的“循环引用”的问题


D4922A20-A7D5-4DDF-8DA5-44823F395BC3.png

例如:前面我们的带有__strong 的例子就容易发生循环引用

@interface Test : NSObject
{
    id __strong obj_;
}
- (void)setObject:(id __strong)obj;
@end
@implementation Test
- (instancetype)init{
    self = [super init];
    if (self) {
        
    }
    return self;
}
- (void)setObject:(id __strong)obj{
    obj_ = obj;
}
@end
//以下为循环引用
{
 id test0 = [Test new];//对象A
 id test1 = [Test new];//对象B
 [test0 setObject: test1];//对象A的obj_成员变量持有对象B的强引用
 [test1 setObject:test0];//对象B的obj_成员变量持有对象A的强引用
}

循环引用很容易造成内存泄漏


0C22C36C-812B-4821-8A00-973B886A7D3C.png

上面这种情况是两个相互强引用发生的内存泄漏,下面这种是自身强引用自身发生的内存泄漏

id test = [Test new];
[test setObject: test];

既然出现了这种强引用的内存泄漏,那么苹果粑粑就会解决这种内存泄漏,和__strong对应的修饰符__weak,如:
id __weak obj = [NSObject new];
变量obj上附加__weak修饰符,但是这样编译之后,编译器会发出警告
Assigning retained object to weak variable; object will be released after assignment
很显然,编译器的意思就是很容易造成提前释放,有问题就要解决
id __strong obj0 = [NSObject new];
id __weak obj1 = obj0;
这样赋值就不会出现警告
当然__weak还有一个优点,在持有某个对象的弱引用时,若该对象被废弃,则此弱引用将自动失效且处于nil被赋值的状态,代码如下:

        id __weak obj1 = nil;
        {
            id __strong obj0 = [NSObject new];
            obj1 = obj0;
            NSLog(@"A:obj1:%@",obj1);
        }
        NSLog(@"B:obj1:%@",obj1);

此源码执行的结果:
A:obj1:<NSObject: 0x103000db0>
B:obj1:(null)
但是有个注意事项:__weak修饰符只能用于iOS5以上及OS X Lion以上版本的应用,在iOS4以下及OS X Snow Leopard 的应用程序中可使用__unsafe _unretained 来修饰,但是对于现在来说也没有多少人在使用iOS4以下及OS X Snow Leopard版本了

__unsafe _unretained

__unsafe _unretained 和__weak是相同的,这里就不过多的讲解,但是要注意的是__unsafe _unretained是不安全的权限修饰符

__autoreleasing

ARC有效时不能使用autorelease和NSAutoreleasePool类,虽然autorelease无法直接使用,但是在ARC有效时autorelease功能是起作用的

/*ARC无效*/
NSAutoreleasePool *pool = [NSAutoreleasePool new];
id obj = [NSObject new];
[obj autorelease];
[pool drain];
  /*ARC有效*/    
@autoreleasepool {
          id __autoreleasing obj = [NSObject new];
}

指定“@autoreleasepool代码块”来替代“ NSAutoreleasePool”
另外,在ARC有效时,要通过将对象赋值给附加了__autoreleasing修饰符的变量来替代调用autorelease方法,对象赋值给附有__autoreleasing修饰符的变量等价于ARC无效时调用对象的autorelease方法,即对象被注册到autoreleasepool
换一种说话就是在ARC有效时,用@autoreleasepool块替代NSAutoreleasePool类,用附有__autoreleasing修饰符的变量替代autorelease方法,图解:


86FB4C33-7785-4D36-BFFB-605C4042E57B.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容