面试题答案
一键面试在Objective-C中,为了打破类A和类B之间的循环引用,可以使用weak
(iOS 5.0及以上)和unowned
(ARC环境下类似unsafe_unretained
,不会自动置为nil
)属性。
- 使用
weak
属性打破循环引用:weak
属性不会增加对象的引用计数,当被引用的对象释放时,weak
属性会自动被设置为nil
,从而避免野指针。
假设类A和类B定义如下:
#import <Foundation/Foundation.h>
@interface B; // 前向声明
@interface A : NSObject
@property (nonatomic, strong) B *b;
@end
@interface B : NSObject
@property (nonatomic, weak) A *a; // 使用weak属性避免循环引用
@end
@implementation A
@end
@implementation B
@end
在使用时:
int main(int argc, const char * argv[]) {
@autoreleasepool {
A *a = [[A alloc] init];
B *b = [[B alloc] init];
a.b = b;
b.a = a;
} // 当离开自动释放池时,a和b会被正确释放,不会造成内存泄漏
return 0;
}
- 使用
unowned
属性打破循环引用:unowned
属性同样不会增加对象的引用计数,但与weak
不同的是,当被引用的对象释放后,unowned
属性不会自动置为nil
,所以使用unowned
时要确保被引用对象的生命周期长于引用它的对象,否则会产生野指针。
假设类A和类B定义如下:
#import <Foundation/Foundation.h>
@interface B; // 前向声明
@interface A : NSObject
@property (nonatomic, strong) B *b;
@end
@interface B : NSObject
@property (nonatomic, unowned) A *a; // 使用unowned属性
@end
@implementation A
@end
@implementation B
@end
在使用时,要保证对象的生命周期合理性:
int main(int argc, const char * argv[]) {
@autoreleasepool {
A *a = [[A alloc] init];
B *b = [[B alloc] init];
a.b = b;
b.a = a;
} // 当离开自动释放池时,如果A先于B释放,b.a会成为野指针,所以使用unowned需谨慎确保A的生命周期长于B
return 0;
}
一般来说,weak
更安全,因为它会自动置nil
,而unowned
适用于能确保对象生命周期关系的场景,以避免weak
带来的额外开销。