Objective-C内存管理

来源:互联网 发布:网络运营商密码是什么 编辑:程序博客网 时间:2024/06/05 03:35

1.引用计数

Objective-C使用引用计数来管理内存,对象有个计数器,用以表示当前有多少个事物想令此对象继续存活下去。NSObject协议声明了下面三个方法用于操作计数器,以递增或递减其值:
retain 递增保留计数
release 递减保留计数
autorelease 稍后清理“自动释放池”时,再递减保留计数。

对象创建出来之后,对象的保留计数至少为1。若想令某对象继续存活,则调用retain方法。要是不想令其继续存活,就调用release或autorelease。当保留计数归零时,对象就回收了(deallocated),系统会将其占用的内存标记为“可重用”。

注意:对象创建出来之后,并不是说对象此时的保留计数必定是1.在alloc或initWith方法的实现代码中,也许还有其他对象也保留了此对象,所以,其保留计数可能会大于1.绝不应该说保留计数一定是某个值,只能说你执行的操作是递增了引用计数或递减了引用计数。

与内存管理有关的属性语义

strong:此特质表明该属性定义了一种“拥有关系”。为这种属性设置新值时,设置方法会先保留新值,并释放旧值,然后再将新值设置上去。同一块内存,引用计数加1。
若属性为“strong”关系,属性的setter方法。
- (void)setFoo:(id)foo {
    [foo
retain];
    [_foo
release];
    _foo = foo;
}

weak:此特质表明该属性定义了一种“非拥有关系”。为这种属性设置新值时,设置方法会既不保留新值,也不释放旧值,在属性所指的对象遭到摧毁时,属性值会清空(nil)。
unsafe_unretained:此特质与weak相同,但在属性所指的对象遭到摧毁时,属性值不会自动清空。
copy:此特质所表达的所属关系与strong类似。然而设置方法并不保留新值,将其“拷贝”。只要实现属性是“可变的”(mutable),就应该在设置新属性时拷贝一份。

weak与unsafe_unretained的区别:当weak变量指向的对象被释放后,该变量值被置为nil,向nil发送消息不会导致崩溃。而当unsafe_unretained变量指向的对象被释放后,该变量指向了一个无效的指针,向其发送消息会导致崩溃。

自动释放池

调用autorelease时,对象的引用计数不会马上递减,而是先对象放进自动释放池,通常是在下一次“事件循环”时递减。此特性很有用,尤其是在方法中返回对象。
- (NSString*)stringValue {
   
NSString *str = [[NSStringalloc]initWithFormat:@"I am this:%@",self];
   
return [str autorelease];
}

NSString*str = [selfstringValue];
NSLog(@"The string is : %@", str);
因为自动释放池中的释放操作要等到下一次事件循环时才会执行,所以NSLog语句在使用str对象前不需要手工保留。但是,假如要持有此对象的话(比如将其设置给实例变量),那就需要保留。

2.ARC

2.1ARC自动添加保留与释放操作

使用ARC时,引用计数实际还是要执行的,只不过保留与释放操作现在是由ARC自动添加,所以直接在ARC下调用这些内存管理方法是非法的。具体来说,不能调用下列方法:
retain
release
autorelease
dealloc
实际上,ARC在调用这些方法时,并不通过普通的Objective-C消息派发机制,而是直接调用其底层C语言版本。这样做性能更好,因为保留及释放操作需要频繁执行,所以直接调用底层函数能节省很多CPU周期。比方说,ARC会调用与retain等价的底层函数objc_retain。这也是不能覆写retain、release、autorelease的原因。

2.2使用ARC时必须遵循的方法命名规则

(1)若方法以下列词语开头,则其返回的对象归调用者所有:
alloc
new
copy
mutableCopy
调用上述四种方法的那段代码要负责释放方法所返回的对象。

(2)若方法不以上述四个词语开头,则表示其返回的对象并不归调用者所有。在这种情况下,返回的对象会自动释放,所以其值在跨越方法调用边界后依然有效。要想使对象多存活一段时间,必须令调用者保留它才行。
ARC之所以能够自动添加保留和释放的方法,主要是因为有这些命名规则。

2.3变量的内存管理语义

ARC也会处理局部变量与实例变量的内存管理。默认情况下,每个变量都是指向对象的强引用。实例变量和局部变量在ARC环境下会自动保留其值。
执行语句_object = object时,先保留新值,再释放旧值,最后设置实例变量。但_object = object与self.object = object的区别在self.object调用set方法来实现,而这个方法有可能被覆写了。
    在应用程序中,可用下列修饰符来改变局部变量与实例变量的语义:
__strong:默认语义,保留此值
__unsafe_unretained:不保留此值,这么做可能不安全,因为等到再次使用变量时,其对象可能已经收回了。
__weak:不保留此值,但是变量可以安全使用。
__autorelease:把对象“按引用传递”给方法。此此值在方法返回时自动释放。


2.4ARC如何清理实例变量

ARC必须在“回收分配给对象的内存”时生成必要的清理代码。凡是具备强引用的变量,都必须释放,ARC会在delloc方法中插入这些代码。用了ARC之后,就不需要编写delloc了。
不过,如果有非Objective-C对象,比如CoreFoundation中的对象或是由malloc( )分配在堆中的内存,那么就仍然要清理。
- (void)dealloc {
    CFRelease(_coreFoundationObject);
    free(_heepAllocateMemoryBlob)j;
}

2.5使用weak避免保留环

只要将其中一个对象声明为weak即可破除保留环。

2.6使用ARC优化性能

在编译期,ARC会把能够互相抵消的retain、release、autorelease操作简约。
EOCPerson*tmp = [EOCPersonpersonWithName:@"Bob Smith"];
_myPerson = [tmp retain];
“personWithName:”方法里的autorelease与上段代码中的retain都是多余的。为了简化代码,在方法返回自动释放的对象时,要执行一个特殊函数。此时不直接调用对象的autorelease方法,而是改为调用objc_autoreleaseReturnValue。此函数会检视当前方法返回之后即将要执行的那段代码。若发现那段要在返回的对象上执行retain操作autorelease操作。与之相似,如果方法返回了一个自动释放的对象,而调用方法的代码要保留此对象,那么此时不直接执行retain,而是改为执行objc_retainAutoreleaseReturnValue函数。此函数要检测刚才提到的标志位,若已经置位,则不执行retain操作。设置并检测标志位,要比调用autorelease和retain更快。
下面这段代码演示了ARC是如何通过这些特殊函数来优化程序的:
+ (EOCPerson*)personWithName:(NSString*)name {
   
EOCPerson *person = [[EOCPersonalloc]init];
    person.name = name;
    objc_autoreleaseReturnValue(person);
}

EOCPerson*tmp = [EOCPersonpersonWithName:@"Matt Galloway"];
_myPerson = objc_retainAutoreleaseReturnValue(tmp);
为了求得最佳效率,这些特殊函数的实现代码因处理器而异。下面这段伪代码描述了其中的步骤:
idobjc_autoreleaseReturnValue(idobject) {
   
if (/* caller will retain object */) {
        set_flag(object);
       
return object;
    }
else {
       
return [object autorelease];
    }
}

idobjc_retainAutoreleaseReturnValue(idobject) {
   
if (get_flag(object)) {
        clear_flag(object);
       
return object;
    }
else {
       
return [object retain];
    }
}




0 0