ARC中的生命周期限定符

2017-07-14  本文已影响0人  liwenxue
  • __strong is the default. An object remains “alive” as long as there is a strong pointer to it.

__strong是默认的生命周期限定符,当所修饰的对象被一个强指针引用时,该对象将在内存中保持活跃状态
__weak表明了一种引用关系,这种引用关系并不能保证所修饰的对象存活下来。当没有强引用指向这个对象的时候,弱引用变量将会置为nil(变为空指针)
__unsafe_unretained表明了一种引用关系,这种引用关系同样不能保证所修饰的对象存活下来。但是当没有强引用指向这个对象的时候,引用变量并不会被置为nil。所以当引用指向的对象被销毁时,指针将成为野指针。(首先unretain即不会改变引用计数,unsafe指的是该引用变量有可能成为野指针)
__autoreleasing用来修饰函数声明中的双指针参数(id * 或者 **)

__autoreleasing的使用有下面一个例子帮助理解:

NSError *error;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK) {
    // Report the error.
    // ...

编译器会对变量error做如下操作

NSError * __strong e;

而对函数performOperationWithError:接收的是一个对象引用的引用,也就是说函数声明应该是这样的

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

编译器会有如下操作

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

对例子中的第2行代码的理解是这样的:函数需要接收的参数是__autoreleasing修饰的,而实际接收到的参数是__strong的,所以编译器会进行如下的操作

NSError * __strong error;
NSError * __autoreleasing tmp = error;// 临时变量
BOOL OK = [myObject performOperationWithError:&tmp];
error = tmp;// 将临时变量的值再回传

为了加深对__autoreleaseing的理解,再举一个例子


屏幕快照 2017-07-14 下午2.58.35.png

出现了野指针调用的问题:这是因为函数形参是__autoreleasing修饰的变量,而巧合的是dict的枚举遍历方法中,有一个内部添加的autoreleasepool,所以代码相当于

- (BOOL)validateDictionary:(NSDictionary *)dict error:(NSError * __autoreleasing *)error {
    ...
    for (NSUInteger idx=0; idx<dict.allKeys.count && !stop; idx++) {
        @autoreleasepool {
            ...
            if (error) {
                *error = [NSError errorWithDomain:@"" code:0 userInfo:nil];
            }
        }// 在这个地方error就被提前释放了
    }
    ...
}

所以一个__autoreleasing修饰的NSError对象在离开autoreleasepool时被释放了,这样外部访问error对象时,就会出现EXC_BAD_ACCESS错误。如果要解决这个问题,可以在遍历前创建一个临时的NSError,然后在遍历完成后将临时变量的值再回传给传入的error

- (BOOL)validateDictionary:(NSDictionary *)dict error:(NSError **)error {
    __block BOOL isValid = YES;
    __block NSError *err;
    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        *stop = YES; isValid = NO;
        if (err) {
            err = [NSError errorWithDomain:@"" code:0 userInfo:nil];
        }
    }];
    *error = err;
    return isValid;
}
上一篇下一篇

猜你喜欢

热点阅读