您的位置:首页 > 其它

Obj-C内存管理(二)

2015-08-04 21:05 507 查看

autorelease的实现

autorelease的本质就是调用 NSAutoreleasePool的addObject方法

//IMP Caching 方法调用的实现

id autorelease_class = [NSAutoreleasePool class];
SEL autorelease_sel = @selector(addObject:);
IMP autorelease_imp = [autorelease_class methodForSelector:autorelease_sel];

- (id)autorelease
{
(*autorelease_imp)(autorelease_class, autorelease_sel, self);
}


苹果的实现

objc4/runtime/objc-arr.mm class AutoreleasePoolPage — 数组的autorelease实现

class AutoreleasePoolPage
{
static inline void *push()
{
//相当于生成或持有NSAutoReleasePool类实例;
}

static inline void *pop( void *token)
{
//废弃NSAutoreleasePool类实例;
releaseAll();
}

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

id *add(id obj)
{
//对象追加到内部数组中;
}

id *releaseAll()
{
//调用内部数组中所有对象的 release 实例方法;
}
};

void *objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}

void objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}

id *objc_autorelease(id obj)
{
return AutoreleasePoopPage::autorelease(obj);
}

//下面我们对照NSAutoreleasePool的使用及其对应runtime实现

NSAutoreleasePool *pool = [[NSAutoReleasePool alloc] init];
/* 等同于调用 objc_autoreleasePoolPush */
id obj = [[NSObject alloc] init];
[obj autorelease];
/*等同于 obj_autorelease(obj)*/
[pool drain];
/*等同于调用 objc_autoreleasePoolPop(pool)*/


Tips:我们可通过NSAutoreleasePool调试用非公开方法 “showPools”来确认已被autorelease的对象的状况,单此方法只能在iOS中使用,在运行的调试中,我们使用 “_objc_autoreleasePoolPrint()”方法作为替代。

//函数声明

//extern void _objc_autoreleasePoolPrint();

//

//autoreleasepool 调试用输出开始

//_objc_autoreleasePoolPrint();

__autoreleasing 修饰符

__autoreleasing 修饰符与__strong一样,一般不会显式使用。在取得非自己生成并持有的对象时,虽然可以使用 “alloc/new/copy/mutablecopy”但是,该对象已经被注册到了autoreleasepool中,这同在ARC无效时取得了调用了autorelease方法的对象是一样的。这是由于编译器会检查方法名是否以 “alloc/new/copy/mutablecopy”开始,如果不是,则自动将返回值的对象注册到autoreleasepool。另外,init方法创建的对象不会注册到 autoreleasepool 中。

以 “alloc/new/copy/mutablecopy”取得的对象属于自己生成并持有的,其源码如下。

在非ARC中,NSAutoreleasePool是不能使用的,我们使用@autoreleasepool{}来代替。

@autoreleasepool
{
id __strong obj = [NSMutableArray array];
}

+ (id)array
{
id obj = [[NSMutableArry alloc] init];
return obj;
}


//obj修饰符为__strong在超出其作用域后会被自动释放,但是作为该方法的返回值,根据其方法名,编译器会自动将其注册到autoreleasepool中。

对象的指针

NSError * error = nil;
BOOl result = [obj performOperationWithError:&error];


方法声明:

- (BOOL)performOperationWithError(NSError **)error;


实际上对象的指针会自动附加上__autoreleasing修饰符,所以该方法声明等同于

- (BOOL)performOperationWithError(NSError * __autoreleasing *)error
{
/*错误发生*/
*error = [[NSError alloc] initWithDomain:MyAppDomain code:errorCode userinfo:nil];
return NO;
}


因为声明为NSError * __autoreleasing * 类型的 error作为 *error被赋值,所以方法调用结束后,error的持有者依旧存在,所以可以得到正常的返回值.

Tips: “@autoreleasepool”在嵌套使用时,总是使用最内层的“@autoreleasepool”。

Tips:__strong 和 __weak 修饰符的变量类似于 C++ 的智能指针 std::shared_ptr 和 std::weak_ptr。std::shared_ptr 可以通过引用计数来持有C++实例,std::weak_ptr可以避免造成循环引用。

ARC规则

1.不能使用 retain/release/retainCount/autorelease

2.不能使用 NSAllocateObject/NSDeallocateObject

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

4.不要显式调用 dealloc

5.使用 @autoreleasepool 代替 NSAutoreleasePool

6.不能使用区域(NSZone)

7.对象类型不能作为 C 语言结构体 (struct/union) 的成员

8.显式转换 “id”和“void *”

1~6条比较简单,不做过多介绍,着重介绍第七第八条

7.对象类型不能作为 C 语言结构体成员

C语言的规约上没有方法来管理结构体成员变量的生存周期。因为在ARC中是由编译器来管理内存的,所以编译器必须知道并管理对象的生存周期。例如 C 语言的局部变量可使用变量的作用域来管理变量的生存周期。但是对于 C 语言的结构体成员来说,这在标准上就是不可实现的。详情请戳:结构体。所以,当我们要把对象型变量加入结构体中时,可以强制转换为void*(具体参见第八条)或附加 __unsafe_unretained 修饰符。因为附有 __unsafe_unretained 的变量不属于编译器的内存管理对象,所以使用时必须格外注意赋值对象的所有者,稍不注意就会导致内存泄露和程序崩溃。

8.显示转换“id”和“void*”

我们先来观察ARC无效时的转换。

id obj = [[NSObject alloc] init];
void * p = obj;
id o = p;
[o release];//即使调用由“void *”赋值的“id”变量的实例方法也不会出问题。


若转化为ARC有效的话需要使用 __bridge

id obj = [[NSObject alloc] init];
void * p = (__bridge void *) obj;
id o = (__bridge id)p;


但是实际上使用__bridge的变量的安全性类似或者说甚至低于__unsafe_unretained修饰的变量类型,所以只要稍不注意赋值对象的所有者就会产生悬垂指针并导致程序崩溃。

所以我们常用的两种转换分别是 __bridge_retained 和 __bridge_transfer。

__bridge_retained会使要被转换赋值的对象也持有该对象,如果按照上例的话其效果相当于

/* __bridge_retained 转换*/

void * p = (__bridge_retained void *)Obj;

/*ARC无效*/

id obj = [[NSObject alloc] init];
void * p = obj;
[(id)p retain];


__bridge_transfer则提供与__bridge_retained相反的动作,使要被转换赋值的对象持有该对象后,释放该对象转换之前的持有者,如果按照上例的话其效果相当于

/* __bridge_retained 转换*/

void * p = (__bridge_transfer void *)Obj;

/*ARC无效*/

id obj = [[NSObject alloc] init];
void * p = obj;
[(id)p retain];
[obj release];


Tips:以上提到的两种转换多用于转换 Fundation (Obj-C) 框架和 Core Fundation (C) 框架,两者的实际差别很小,并且任何框架生成的对象都可相互使用,持有,释放。且这种转换不需要使用额外的CPU资源,所以也称为“免费桥(Toll-Free Bridge)”(Toll-Free Bridge URL).
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  内存管理