面试题答案
一键面试线程安全的单例模式实现
在Objective-C中,实现线程安全的单例模式可以使用GCD(Grand Central Dispatch)的dispatch_once函数。以下是示例代码:
#import <Foundation/Foundation.h>
@interface Singleton : NSObject
@property (nonatomic, strong, readonly) NSString *sharedValue;
+ (instancetype)sharedInstance;
@end
@implementation Singleton
static Singleton *sharedSingleton = nil;
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedSingleton = [[self alloc] init];
sharedSingleton.sharedValue = @"Initial value";
});
return sharedSingleton;
}
@end
锁机制的应用原理和作用
- 原理:
dispatch_once
函数内部使用了一种高效的原子操作来确保代码块只被执行一次。它通过一个dispatch_once_t
类型的变量(在上述代码中是onceToken
)来跟踪代码块是否已经执行过。当第一次调用dispatch_once
时,它会执行传入的代码块,并将onceToken
标记为已执行。后续调用dispatch_once
时,只要onceToken
已标记,就不会再次执行代码块。 - 作用:这种机制确保了单例对象在多线程环境下的唯一性。无论有多少个线程同时调用
sharedInstance
方法,单例对象只会被创建一次,避免了多个线程同时创建单例对象的竞争条件,从而保证了线程安全。
不同锁机制对单例实现的性能影响
- 互斥锁(Mutex):如果不使用
dispatch_once
,而使用互斥锁来实现单例,每次调用获取单例的方法时都需要加锁和解锁操作。虽然可以保证线程安全,但加锁和解锁操作是有开销的,特别是在高并发环境下,频繁的加锁解锁会严重影响性能。例如:
#import <Foundation/Foundation.h>
#import <pthread.h>
@interface Singleton : NSObject
@property (nonatomic, strong, readonly) NSString *sharedValue;
+ (instancetype)sharedInstance;
@end
@implementation Singleton
static Singleton *sharedSingleton = nil;
static pthread_mutex_t mutex;
+ (void)initialize {
pthread_mutex_init(&mutex, NULL);
}
+ (instancetype)sharedInstance {
pthread_mutex_lock(&mutex);
if (!sharedSingleton) {
sharedSingleton = [[self alloc] init];
sharedSingleton.sharedValue = @"Initial value";
}
pthread_mutex_unlock(&mutex);
return sharedSingleton;
}
+ (void)dealloc {
pthread_mutex_destroy(&mutex);
}
@end
- 自旋锁(Spin Lock):自旋锁在等待锁时不会使线程进入睡眠状态,而是在原地不断尝试获取锁。对于单例模式,如果获取单例的操作非常频繁且锁的持有时间很短,自旋锁可能会有较好的性能,因为避免了线程上下文切换的开销。但如果锁的持有时间较长,自旋锁会浪费CPU资源,导致性能下降。
dispatch_once
:dispatch_once
使用的机制比普通的锁更高效。它在第一次执行代码块后,后续调用几乎没有额外开销,因为不需要每次都进行加锁解锁操作。在单例模式下,由于单例对象通常只创建一次,dispatch_once
能很好地满足需求,同时保持高性能。
综上所述,dispatch_once
是实现Objective-C线程安全单例模式的推荐方式,因为它既保证了线程安全,又具有良好的性能。