OC高级编程-IOS与OS X多线程和内存管理--1.自动引用计数

1.什么是自动引用计数

自动引用计数( ARC, Automatic Reference Counting )是指内存管理中对引用采
取自动计数的技术

“在LLVM编译器中设置ARC 为有效状态. 就不用再次输入入retain 或者是release 代码。”

条件:

  • 使用Xcode 4.2 或以上版本.
  • 使用LLVM 编译器3.0 或以上版本.
  • 编译器中设置ARC 为有效.

2.内存管理/引用计数

2.1.内存管理的思考方式

  • 自己能持有自己生成的对象或者非自己生成的对象。
  • 自己持有对象不需要时要释放。
  • 自己无法释放非自己持有的对象。
对象操作 OC方法
生成并持有对象 alloc/new/copy/mutableCopy
持有对象 retain
释放对象 release
废弃对象 dealloc
表1 对象操作与Objective-C 方法的对应

copy生成的对象不可变更,mutableCopy生成的对象可变更。
这些有关Objective-C 内存管理的方法,是包含在Cocoa 框架中的Foundation 框架类库的NSObject 类中。
OC 内存管理中的alloc/retain/release/dealloc 方法分别指代NSObject 类的alloc 类方法、retain/release/dealloc 实例方法。

![图1.1 Cocoa 框架、Foundation 框架和NSObject 类的关系](https://github.com/MrConfused/img/blob/master/IOS/OC/OC%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B-IOS%E4%B8%8EOS_X%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%92%8C%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/%E5%9B%BE1.1%20Cocoa%20%E6%A1%86%E6%9E%B6%E3%80%81Foundation%20%E6%A1%86%E6%9E%B6%E5%92%8CNSObject%20%E7%B1%BB%E7%9A%84%E5%85%B3%E7%B3%BB.jpg?raw=true)
图1.1 Cocoa 框架、Foundation 框架和NSObject 类的关系

2.1.1.持有自己生成的对象

alloc, new, copy, mutableCopy开头用驼峰拼写法命名的方法生成的对象只有自己持有

根据上述规则:
1.allocMyObject, newTbatObject, copyThis, mutableCopyYourObject方法生成的对象自己持有。
2.allocate, newer, copying, mutableCopyed开头的方法不会自己持有。

2.1.2.持有非自己生成的对象

通过retain方法可以持有非自己生成的对象

1
2
id obj = [NSMutableArray array]; //生成对象
[obj retain]; //持有对象

某个方法生成对象,并将其返还给调用方:

1
2
3
4
5
- (id) allovObject
{
id obj = [[NSObject alloc] init]; //用alloc生成并持有
return obj; //返回该对象
}

对象存在,但自己并不持有:

1
2
3
4
5
6
- (id) object
{
id obj = [[NSObject alloc] init]; //生成并持有
[obj autorelease]; //obj超出范围自动释放
return obj;
}

2.1.3.自己持有的对象不需要时,要释放

通过release方法可以释放自己持有的对象

1
[obj release];

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

若释放非自己持有的对象,或者某个对象释放之后再次释放,会导致程序崩溃。

2.2.alloc/retain/release/dealloc在GNUstep中的实现

包含NSObject类的Foundation框架不公开。
但是Foundation框架使用的Core Foundation框架,及通过调用Object类进行内存管理部分是公开的。
GNUstep是Cocoa框架的互换框架,两者的行为及实现方式是一样的。

GNUstep的引用计数是通过将引用计数放在内存块头部的变量中实现的。好处是:
1. 代码量少
2. 能统一管理引用计数用的内存块与对象用的内存块

2.2.1.alloc的实现

GNUstep/modules/core/base/Source/NSObject.m alloc
1
2
3
4
5
6
7
8
9
+ (id) alloc 
{
return [self allocWithZone: NSDefaultMallocZone()];
}

+ (id) allocWithZone:(NSZone*)z
{
return NSAllocateObject(self, 0, z);
}
GNUstep/modules/core/base/Source/NSObject.m NSAllocateObject
1
2
3
4
5
6
7
8
9
10
struct obj_layout{
NSUInteger retained;
};
inline id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone)
{
int size = 计算容纳对象所需内存大小;
id new = NSZoneMalloc(zone, size); //分配空间
memset(new, 0, size); //初始化内存
new = (id)&((struct onj_layout *)new)[1]; //转换为对象指针
}
NSZone是为防止内存碎片化引入的结构,对内存分配区域本身进行多重化管理。
但由于运行时系统中的内存管理本身已极具效率,因此使用NSZone可能导致效率降低及代码复杂。

去掉NSZone后的alloc实现:

GNUstep/modules/core/base/Source/NSObject.m alloc 简化版
1
2
3
4
5
6
7
8
9
10
struct obj_layout{
NSUInteger retained; //保存引用计数
};

+ (id) alloc
{
int size = sizeof(struct obj_layout) + 对象大小;
struct obj_layout *p = (struct obj_layout *)calloc(1, size); //将引用计数放在对象头部
return (id)(p+1);
}

2.2.2.retainCount的实现

引用计数可通过retainCount获取:[obj retainCount]
retainCount的实现:

GNUstep/modules/core/base/Source/NSObject.m retainCount
1
2
3
4
5
6
7
8
9
- (NSUInteger) retainCount
{
return NSExtraRefCount(self) + 1;
}

inline NSUInteger NSExtraRefCount(id anObject)
{
return ((struct obj_layout *)anObject)[-1].retained; //获取对象头部的retained变量
}

2.2.3.retain的实现

若retained不超过int最大值,则使retained加1.

GNUstep/modules/core/base/Source/NSObject.m retain
1
2
3
4
5
6
7
8
9
10
11
12
- (id) retain
{
NSIncrementExtraRefCount(self);
return self;
}

inline void NSIncrementExtraRefCount(id anObject)
{
if(((struct obj_layout *)anObject)[-1].retained == UINT_MAX - 1) //安全性判断
[NSException raise: NSInternalInconsistencyException format: @"NSIncrementExtraRefCount() asked to increment too far"];
((struct obj_layout *)anObject)[-1].retained++;
}

2.2.4.release的实现

若retained为0,调用dealloc;否则使retained减1

GNUstep/modules/core/base/Source/NSObject.m release
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (id) release
{
if(NSDecrementExtraRefCountWasZero(self))
[self dealloc];
}

BOOL NSDecrementExtraRefCountWasZero(id anObject)
{
if(((struct obj_layout *)anObject)[-1].retained == 0){
return YES;
} else {
((struct obj_layout *)anObject)[-1].retained--;
return NO;
}
}

2.2.5.dealloc的实现

self转换为obj_layout,并调用free()

GNUstep/modules/core/base/Source/NSObject.m dealloc
1
2
3
4
5
6
7
8
9
10
- (void) dealloc
{
NSDeallocateObject(self);
}

inline void NSDeallocateObject(id abObject)
{
struct obj_layout *o = &((struct obj_layout *)anObject)[-1];
free(o);
}

2.3.alloc/retain/release/dealloc在苹果中的实现

苹果的引用计数是通过散列表(引用计数表)实现的。好处是:
1. 内存块的分配无需考虑头部。
2. 引用计数表中有内存块的地址,可从每个记录追溯到各对象的内存块。
3. 有助于判断各内存块的持有者是否存在。

2.3.1.alloc的实现

1
2
3
4
+alloc
+allocWithZone
class_createInstance
calloc

2.3.2.retainCount/retain/release的实现

先获取对象对应的散列表,然后按照不同操作进行分发

1
2
3
4
5
6
7
8
9
10
11
- (NSUInteger) retainCount{
return (NSUInteger)__CFDoExternRefOpreation(OPERATION_retainCount, self);
}

- (id) retain{
return (id)__CFDoExternRefOpreation(OPERATION_retain, self);
}

- (void) release{
return __CFDoExternRefOpreation(OPERATION_release, self);
}
CF/CFRuntime.c __CFDoExternRefOperation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __CFDoExternRefOpreation(uintptr_t op, id obj)
{
CFBasicHashRef table = 获取对象对应的散列表(obj);
int count;

switch(op){
case OPERATION_retainCount:
count = CFBasicHashGetCountOfKey(table, obj);
return count;
case OPREATION_retain:
CFBasicHashAddValue(table, obj);
return obj;
case OPREATION_release:
count = CFBasicHashRemoveValue(table, obj);
return 0==count;
}
}

2.4.autorelease

类似于C语言中的局部变量,若变量超出作用域,自动被废弃。

使用方法:

  1. 生成并持有NSAutoreleasePool对象;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]
  2. 调用已分配对象的autorelease方法;
    [obj autorelease]
  3. 废弃NSAutoreleasePool对象。
    [pool drain]

2.4.1.autorelease在GNUstep中的实现

调用NSAutoreleasePool的addObject类方法

GNUstep/modules/core/base/Source/NSObject.m autorelease
1
2
3
4
- (id) autorelease
{
[NSAutoreleasePool addObject:self];
}

2.4.1.1.addObject类方法的实现

GNUstep/modules/core/base/Source/NSAutoreleasePool.m addObject类方法
1
2
3
4
5
6
7
8
9
+ (void) addObject:(id)anObj
{
NSAutoreleasePool *pool = 取得正在使用的NSAutoreleasePool对象;
if(pool != nil){
[pool addObject:anObj];
} else {
NSLog(@"NSAutoreleasePool对象非存在状态下调用autorelease");
}
}
GNUstep/modules/core/base/Source/NSAutoreleasePool.m addObject实例方法
1
2
3
4
- (void) addObject:(id)anObj
{
[array addObject:anObj];
}

2.4.1.2.drain方法的实现

对NSAutoreleasePool中的数组中的对象分别调用release,然后调用数组的release。

GNUstep/modules/core/base/Source/NSAutoreleasePool.m drain
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void) drain
{
[self dealloc];
}

- (void) dealloc
{
[self emptyPool];
[array release];
}

- (void) emptyPool
{
for(id obj in array){
[obj release];
}
}

2.4.2.autorelease在苹果中的实现

使用方法与NSAutoreleasePool类似:
1.先生成AutoreleasePoolPage对象,
2.然后调用autorelease,
3.最后废弃AutoreleasePoolPage对象

_objc_autoreleasePoolPrint()函数可用来调试查看AutoreleasePoolPage中对象的情况

objc4/runtime/objc-arr.mm class AutoreleasePoolPage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//生成AutoreleasePoolPage对象
void *objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}

//废弃AutoreleasePoolPage对象
void objc_autoreleasePoolPage(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}

//设置autorelease
id *objc_autorelease(id obj)
{
return AutoreleasePoolPage::autorelease(obj);
}

class AutoreleasePoolPage{
static inline void *push(){
相当于生成或持有NSAutoreleasePool对象;
}

static inline void *pop(void *token){
相当于废弃NSAutoreleasePool对象;
releaseAll();
}

static inline id autorelease(id obj){
相当于NSAutoreleasePool类的addObject 类方法;
AutoreleasePoolPage *autoreleasePoolPage = 取得正在使用的AutoreleasePoolPage实例;
autoreleasePoolPage->add(obj);
}
}

3.ARC规则

可对每个文件分别指定是否使用ARC,Xcide4.2默认对所有文件使用ARC

设置ARC有效的编译方法:
1. 使用clang(LLVM编译器)3.0或以上版本
2. 指定编译器属性为“-fobjc-arc”

3.1.所有权修饰符

ARC有效时,必须对id类型或其他对象类型附加所有权修饰符:
1. __strong
2. __weak
3. __unsafe_unretained
4. __autoreleasing

3.1.1.__strong修饰符

  1. 默认的修饰符。(不需要加上该修饰符即可实现“不必再次键入retain或者release”的目标)

  2. 附有__strong修饰符的obj,在超出其作用域时(即被废弃时),会释放该obj。

  3. 附有__strong修饰符的变量之间可以相互赋值。

  4. 无需额外工作即可直接用于类成员变量和方法参数。

  5. 能保证将附有该修饰符的变量初始化为nil:”id __strong obj;”等同于”id __strong obj = nil;”

    无法解决“循环引用”的问题,容易导致内存泄漏。

3.1.2.__weak修饰符

  1. 弱引用不持有对象实例
  2. 用法:将strong修饰的变量赋值给weak修饰的变量
  3. 优点:
    a. 弱引用指向的对象被废弃时,弱引用自动失效并被赋nil
    b. 使用附有__weak 修饰符的变量,即是使用注册到autoreleasepool 中的对象
  4. 弱引用仅支持IOS5及OS X Lion以上版本

3.1.3.__unsafe_unretained修饰符

  1. 不安全的所有权修饰符,不能保证修饰的变量指向的对象是有效的。在指向的对象被废弃时,该变量仍指向原地址,造成悬垂指针。因此在使用该修饰符修饰的变量时,必须保证指向的对象确实存在
  2. 该修饰符修饰的变量,不属于编译器的内存管理对象
  3. 在不支持弱引用的版本中可用该修饰符

3.1.4.__autoreleasing修饰符

  1. 在ARC无效时,要使用autorelease功能,必须使用NSAutoreleasePool或者AutoreleasePoolPage;在ARC有效时,要使用autorelease功能,可直接用__autoreleaseing修饰变量,然后用@autoreleasepool{···}块包裹该变量的使用。
![图1.2 @autorel easepool 和附有_autoreleasing 修饰符的变量](https://github.com/MrConfused/img/blob/master/IOS/OC/OC%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B-IOS%E4%B8%8EOS_X%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%92%8C%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/%E5%9B%BE1.2%20@autorel%20easepool%20%E5%92%8C%E9%99%84%E6%9C%89%EF%BC%BFautoreleasing%20%E4%BF%AE%E9%A5%B0%E7%AC%A6%E7%9A%84%E5%8F%98%E9%87%8F.jpg?raw=true)
图1.2 @autorel easepool 和附有_autoreleasing 修饰符的变量
  1. 通常不需要显式地使用该修饰符:在@autoreleasepool块中,
    • 编译器会将不是以alloc/new/copy/mutableCopy开头的方法所返回的对象自动注册到autoreleasePool中
    • __weak修饰的对象会被注册到autoreleasePool中
    • id的指针(id *obj)或者其他对象的指针(NSObject **obj)会被隐式的加上__autoreleasing

      id:是动态类型,相当于一个指针变量。id obj ==>> NSObject *obj
      指针变量:指向其他对象的对象。类似与一个两节的盒子,第一节是该指针变量的地址,第二节是指向对象的内存地址。例如Person *p = [[Person alloc] init]p就是指针变量,*p获得p指向的对象。

3.1.5所有权修饰符总结

对象指针赋值时,要保证所有权修饰符一致
__unsafe_unretained修饰符之外的__strong/__weak/__autoreleasing修饰符均能保证其指定的变量初始化为nil

隐式的所有权推导:
id obj ==>> id __strong obj
id *obj ==>> id __autoreleasing obj
NSObject *obj ==>> NSObject __strong *obj
NSObject **obj ==>> NSObject * __autoreleasing *obj
下述代码会编译报错:

1
2
NSError *err = nil; //err是__strong引用
NSError **pErr = &err; //*pErr是指针对象,是__autoreleasing类型

下述代码不会报错:

1
2
NSError *err = nil;
NSError *tmp = err; //tmp是强引用

@autoreleasepool可嵌套使用
ARC无效时也可使用@autoreleasepool,因此无论ARC是否有效,均应使用@autoreleasepool
无论ARC是否有效,objc_autoreleasePoolPrint()均可使用

3.2.规则

在ARC有效时必须遵守以下规则

  1. 不使用retain/release/retainCount/autorelease
  2. 不使用NSAllocateObject/NSDeallocateObject
  3. 不使用NSZone
  4. 对象型变量不能作为结构体(struct/union)的成员
  5. 显示转换idvoid*
  6. 遵守内存管理的方法命名规则
  7. 使用@autoreleasepool代替NSAutoreleasePool
  8. 不显式调用dealloc

3.2.1.不使用retain/release/retainCount/autorelease

3.2.2.不使用NSAllocateObject/NSDeallocateObject

这两个是OC底层实现对象的创建与销毁

3.2.3.不使用NSZone

3.2.4.对象型变量不能作为结构体(struct/union)的成员

C语言中没有方法来管理结构体成员的生存周期。
可将对象型变量强转为void*放入结构体中。

3.2.5.显示转换id与void*

  1. 使用__bridge转换:单纯地赋值(__bridge void *)obj
  2. 使用__bridge_retained:可使要转换的变量也持有所赋值的对象
  3. 使用__bridge_transfer:被转换的变量所持有的对象在赋值之后及被释放

这些转换多发生在OC与Core Foundation对象之间的转换。
OC与Core Foundation对象之间的转换可用以下函数来处理:

1
2
3
4
5
6
7
CFTypeRef CFBridgeingRetain(id id_obj){ //oc对象转cf对象
return (__bridge_retained CFTypeRef)id_obj;
}

id CFBridgeRelease(CFTypeRef cf_obj){ //cf对象转oc对象
return (__bridge_transfer id)cf_obj;
}

3.2.6.遵守内存管理的方法命名规则

  1. 以alloc/new/copy/mutableCopy开头的方法在返回对象时,必须给调用方持有该对象。
  2. init开头的方法必须是实例方法,且必须返回对象,且返回的对象不注册到autoreleasePool中。只是用来对alloc方法返回的对象进行初始化并返回该对象。

3.2.7.使用@autoreleasepool代替NSAutoreleasePool

3.2.8.不显式调用dealloc

  1. 无论ARC是否有效,只有对象的所有者都不持有该对象,编译器就要调用dealloc将该对象销毁。
  2. dealloc中通过free来释放内存
  3. dealloc大多数情况适用于删除已注册的代理或观察对象。
  4. ARC无效时要用[super dealloc],ARC有效时无法显示调用。

3.3.属性

  1. 定义:属性是类中描述对象的抽象概念
  2. 使用:@property (nonatomic, strong) NSString *name;
    此时name就是一个属性,编译器会自动创建其对应的实例变量(成员变量)_name,之后在操作self.name时就相当于操作_name.
  3. 引入属性的原因:编译器遇到@property就会为这个属性添加setter/getter方法。
属性声明的属性 所有权修饰符
assign __unsafe_unretained
copy __strong(但赋值的是被赋值的对象)
retain __strong
strong __strong
unsafe_unretained __unsafe_unretained
weak __weak
表2 属性声明的属性与所有权修饰符的对应关系

3.4.数组

  1. 将修饰符修饰的变量作为静态数组时,与对象是一样的,超出作用域时会被释放
  2. __strong和__weak修饰符修饰的变量作为动态数组时,要用指针,需要手动分配内存并初始化(建议使用calloc或者malloc + memset),需要手动释放所有元素。
  3. __autoreleasing不要使用动态数组。
  4. __unsafe_unretained只能作为指针类型使用。

4.ARC的实现

4.1.__strong修饰符

对于alloc/new/copy/mutableCopy开头的自己生成并持有的方法,在创建对象时,会调用allocinit方法,然后在作用域结束时插入release

1
2
3
4
5
6
7
{
id __strong obj = [[NSObject alloc] init];
}
=======>>>>>>/*编译器的模拟代码*/
id obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(obj, @selector(init));
objc_release(obj);

对于非自己生成并持有的方法创建对象时,编译器会调用objc_retainAuroreleaseReturnValue函数来持有注册到autoreleasepool中的对象,然后在作用域结束时调用release。

1
2
3
4
5
6
7
{
id __strong obj = [NSMutableArray array];
}
=======>>>>>>/*编译器的模拟代码*/
id obj = objc_msgSend(NSMutableArray, @selector(alloc));
objc_retainAuroreleaseReturnValue(obj); //持有pool中的对象
objc_release(obj);

objc_auroreleaseReturnValue函数用来返回注册到autoreleasepool中的对象,一般与objc_retainAuroreleaseReturnValue是成对出现的。
若在objc_auroreleaseReturnValue之后紧接着出现objc_retainAuroreleaseReturnValue函数,那么就不将返回的对象注册到autoreleasepool中,而是直接传递到方法调用方,达到了最优化

![图1.3 省略autoreleasepool注册](https://github.com/MrConfused/img/blob/master/IOS/OC/OC%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B-IOS%E4%B8%8EOS_X%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%92%8C%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/%E5%9B%BE1.3%20%E7%9C%81%E7%95%A5autoreleasepool%E6%B3%A8%E5%86%8C.jpg?raw=true)
图1.3 省略autoreleasepool注册

4.2.__weak修饰符

__weak在被废弃时会赋值为nil
使用__weak变量即使用autoreleasepool 中的对象

objc_initWeak初始化,在作用域结束时调用objc_destroyWeak

1
2
3
4
5
6
7
{
id __weak obj1 = obj;
}
=======>>>>>>/*编译器的模拟代码*/
id obj1;
objc_initWeak(&obj1, obj);
objc_destroyWeak(&obj1);

objc_initWeak(&obj1, obj); ==>> obj1 = 0; objc_storeWeak(&obj1, obj);
objc_destroyWeak(&obj1, obj); ==>> objc_storeWeak(&obj1, 0);

objc_storeWeak将第二参数的地址作为键值,把第一参数的地址注册到weak表(用散列表实现)中。

4.2.1.__weak在被废弃时会赋值为nil

  1. objc_release
  2. 因为引用计数为0,所以执行dealloc
  3. _objc_rootDealloc
  4. object_dispose
  5. objc destructlnstance
  6. objc clear_ deallocating
    1)从weak 表中获取废弃对象的地址为键值的记录。
    2)将包含在记录中的所有附有__weak 修饰符变最的地址,赋值为nil 。(这一功能会使得当有大量弱引用变量时,会消耗CPU资源)
    3)从weak 表中删除该记录。
    4)从引用计数表中删除废弃对象的地址为键值的记录。

自己生成并持有的对象赋值给__weak变量时,自己无法持有该对象,编译器会发出警告,并将其赋值为nil。

4.2.2.使用__weak变量即使用autoreleasepool 中的对象

1
2
3
4
5
6
7
8
9
10
11
{
id __weak obj1 = obj;
NSLog(@"%@", obj1);
}
=======>>>>>>/*编译器的模拟代码*/
id obj1;
objc_initWeak(&obj1, obj);
id tmp = objc_loadWeakRetained(&obj1); //取出weak变量,并retain
objc_autorelease(tmp); //注册到autoreleasepool中
NSLog(@"%@", tmp);
objc_destroyWeak(&obj1);

如果大量地使用weak变量,注册到autoreleasepool 的对象也会大量增加, 因此在使用附有weak 修饰符的变量时,最好先暂时将weak变量赋值给strong变量,最后再使用strong变量

allowsWeakReference/retainWeakReference实例方法返回NO时,不能使用__weak修饰符。

4.3.__autoreleasing修饰符

将对象赋值给附有autoreleasiag 变量等向于ARC 无效时调用对象的autorelease方法

1
2
3
4
5
6
7
8
9
{
id __autoreleasing obj = [[NSObject alloc] init];
}
=======>>>>>>/*编译器的模拟代码*/
id pool = objc_autoreleasePoolPush();
id obj = objc_msgSend(NSObject, @selector(alloc)) ;
objc_msgSend(obj, @selector(init));
objc_autorelease(obj);
objc_autoreleasePoolPop(pool) ;

在alloc/new/copy/mutableCopy 方法群之外的方法中使用注册到autoreleasepool 中的对象:

1
2
3
4
5
6
7
8
9
{
id __autoreleasing obj = [NSMutableArray array];
}
=======>>>>>>/*编译器的模拟代码*/
id pool = objc_autoreleasePoolPush();
id obj = objc_msgSend(NSMutableArray, @selector(array)) ;
objc_retainAutoreleasedReturnValue(obj);
objc_autorelease(obj);
objc_autoreleasePoolPop(pool) ;

4.4.引用计数

获取引用计数数值:uintptr_t _objc_rootRetainCount(id obj)

实际上并不能够完全信任该函数取得的数值:
1.对于己释放的对象以及不正确的对象地址,有时也返回“1”。
2.在多线程中使用对象的引用计数数值, 因为存有竞态条件的问题,所以取得的数值不一定完全可信。

0%