面试题答案
一键面试保证线程安全
- 使用
dispatch_once
:在Objective-C中,dispatch_once
是保证单例线程安全的常用方法。dispatch_once
会确保代码块只被执行一次,无论有多少线程同时调用。示例代码如下:
+ (instancetype)sharedInstance {
static MySingleton *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- 加锁机制:虽然
dispatch_once
是更推荐的方式,但也可以使用锁来实现线程安全。例如使用@synchronized
关键字:
+ (instancetype)sharedInstance {
static MySingleton *sharedInstance = nil;
@synchronized(self) {
if (!sharedInstance) {
sharedInstance = [[self alloc] init];
}
}
return sharedInstance;
}
不过@synchronized
相对dispatch_once
性能会稍差,因为每次访问都需要加锁解锁。
防止多次实例化
- 重写
allocWithZone:
:为了防止通过alloc
和init
方法多次实例化单例,需要重写allocWithZone:
方法,确保始终返回同一个实例。
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [self sharedInstance];
}
- 防止拷贝:如果单例对象可能会被拷贝,还需要重写
copyWithZone:
和mutableCopyWithZone:
方法,防止创建新的实例。
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return self;
}
内存管理
- ARC环境:在ARC(自动引用计数)环境下,单例对象的内存管理相对简单,因为ARC会自动处理对象的引用计数。只要单例对象有强引用存在(通常在整个应用程序生命周期内都会有对单例的强引用),它就不会被释放。
- MRC环境:在MRC(手动引用计数)环境下,需要注意单例对象的引用计数。由于单例对象在应用程序中通常是全局存在的,不需要手动释放它,除非在应用程序退出时明确需要释放资源。可以在单例类中添加一个类方法来释放单例实例(比如
+ (void)releaseSharedInstance
),在应用程序即将退出时调用。不过这种情况比较少见,因为单例在应用程序生命周期内通常是持续存在的。同时,重写retain
、release
、autorelease
和retainCount
方法,确保单例的引用计数行为符合预期。
- (instancetype)retain {
return self;
}
- (oneway void)release {
// 不执行任何操作,单例不应被释放
}
- (instancetype)autorelease {
return self;
}
- (NSUInteger)retainCount {
return NSUIntegerMax;
}