【iOS】Objective-C简约而不简单的单例模式
2015-07-17 14:30
435 查看
http://leyteris.iteye.com/blog/1669198
前些日子在项目中因为误用了单例而导致了一系列问题。原来在objective-c中的单例并没有java或者C#那么简单的实现,这里记录下;
问题是这样被发现的,在对于一个UIViewController进行pop时并没有被dealloc,导致了内存泄露。问题代码类似于下面的:
C代码
//LWChatViewController.h
@interface LWChatViewController : LWTableViewController <LWObjSelectViewDelegate>{
UINavigationController *root;
}
@property (nonatomic, retain) UINavigationController *root;
@end
//LWChatViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.root = LWNavigationController;
}
这里的LWNavigationController是一个顶级的单例。
问题就出在@property (nonatomic, retain) 这里root居然是一个retain的对象指针,在这里retain一个static的单例将导致内存泄露,MD,这个bug找的我好久。。。
解决这个问题其实很简单,把retain改为assign就行了,但这样如果在协作编程的时候如果别人不在意这个是单例直接进行常规操作的话会带来很大的问题。
继续,我们来从根本上解决这个问题。
我们需要重写一些方法:
C代码
- (id)retain
{
return self;
}
- (NSUInteger) retainCount
{
return NSUIntegerMax;
}
- (void) release
{
// do nothing
}
- (id)autorelease
{
return self;
}
在retain和autorelease什么都不做只是返回自己,release的时候啥都不做,将retainCount设为UInt的极大值。
其次是关于线程安全的实现,这些java都有明确的代码模式:
关于线程安全的单例,这篇外文 http://www.numbergrinder.com/2008/12/patterns-in-objective-c-singleton-pattern/ 有比较详细的解释。
Java代码
@implementation Singleton
static Singleton *instance = nil;
+ (Singleton *)sharedInstance {
@synchronized(self)
{
if(!instance) {
instance = [[super allocWithZone:NULL] init];
}
}
return instance;
}
@end
嗯,这样就可以实现线程安全的单例了,当然这里也可以用NSLock实例去实现。
本类的allocWithZone被改写为:
C代码
+ (id)allocWithZone:(NSZone *)zone
{
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
同时深拷贝也直接重载阻止掉多个实例的出现。上面的allocWithZone的重载使得这个单例也能够直接用alloc或是allocWithZone进行初始化,但返回的一如既往是那个static的实例。
下面我们来看看
当
当
当
当线程首先调用
这样当其他线程再获取
当
下次再调用
这样
前些日子在项目中因为误用了单例而导致了一系列问题。原来在objective-c中的单例并没有java或者C#那么简单的实现,这里记录下;
问题是这样被发现的,在对于一个UIViewController进行pop时并没有被dealloc,导致了内存泄露。问题代码类似于下面的:
C代码
//LWChatViewController.h
@interface LWChatViewController : LWTableViewController <LWObjSelectViewDelegate>{
UINavigationController *root;
}
@property (nonatomic, retain) UINavigationController *root;
@end
//LWChatViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.root = LWNavigationController;
}
这里的LWNavigationController是一个顶级的单例。
问题就出在@property (nonatomic, retain) 这里root居然是一个retain的对象指针,在这里retain一个static的单例将导致内存泄露,MD,这个bug找的我好久。。。
解决这个问题其实很简单,把retain改为assign就行了,但这样如果在协作编程的时候如果别人不在意这个是单例直接进行常规操作的话会带来很大的问题。
继续,我们来从根本上解决这个问题。
我们需要重写一些方法:
C代码
- (id)retain
{
return self;
}
- (NSUInteger) retainCount
{
return NSUIntegerMax;
}
- (void) release
{
// do nothing
}
- (id)autorelease
{
return self;
}
在retain和autorelease什么都不做只是返回自己,release的时候啥都不做,将retainCount设为UInt的极大值。
其次是关于线程安全的实现,这些java都有明确的代码模式:
关于线程安全的单例,这篇外文 http://www.numbergrinder.com/2008/12/patterns-in-objective-c-singleton-pattern/ 有比较详细的解释。
Java代码
@implementation Singleton
static Singleton *instance = nil;
+ (Singleton *)sharedInstance {
@synchronized(self)
{
if(!instance) {
instance = [[super allocWithZone:NULL] init];
}
}
return instance;
}
@end
嗯,这样就可以实现线程安全的单例了,当然这里也可以用NSLock实例去实现。
本类的allocWithZone被改写为:
C代码
+ (id)allocWithZone:(NSZone *)zone
{
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
同时深拷贝也直接重载阻止掉多个实例的出现。上面的allocWithZone的重载使得这个单例也能够直接用alloc或是allocWithZone进行初始化,但返回的一如既往是那个static的实例。
dispatch_once为什么能做到既解决同步多线程问题又不影响性能呢?
下面我们来看看
dispatch_once的原理:
dispatch_once主要是根据
onceToken的值来决定怎么去执行代码。
当
onceToken= 0时,线程执行
dispatch_once的
block中代码
当
onceToken= -1时,线程跳过
dispatch_once的
block中代码不执行
当
onceToken为其他值时,线程被线程被阻塞,等待
onceToken值改变
当线程首先调用
shareInstance,某一线程要执行
block中的代码时,首先需要改变
onceToken的值,再去执行block中的代码。这里
onceToken的值变为了140734731430192。
这样当其他线程再获取
onceToken的值时,值已经变为140734731430192。其他线程被阻塞。
当
block线程执行完
block之后。
onceToken变为-1。其他线程不再阻塞,跳过
block。
下次再调用
shareInstance时,block已经为-1。直接跳过
block。
这样
dispatch_once在首次调用时同步阻塞线程,生成单例之后,不再阻塞线程。
dispatch_once是创建单例的最优方案
相关文章推荐
- Object-C中使用NSKeyedArchiver归档(将各种类型的对象存储到文件中)
- 在servlet中使用json-lib报错--java.lang.NoClassDefFoundError: net/sf/json/JSONObject
- (转)C#4.0的dynamic和var及object关键字辨析
- Objective-C method XXX conflicts with optional requirement method XXX
- Objective-C中的NSValue的详解
- animation之objectAnimator
- SelectObject
- java 后台使用gson 将json字符串转换为List<Map<String,object>> 数组
- Objective-C学习备忘录:Clang编译器编译运行Objective-C代码
- 第5章分布式系统模式 在 .NET 中使用 DataSet 实现 Data Transfer Object
- 第5章分布式系统模式 在 .NET 中使用 DataSet 实现 Data Transfer Object
- Object之克隆对象clone 和__clone()函数
- Object之魔术函数__toString() 直接输出对象引用时自动调用
- Object之魔术函数__call() 处理错误调用
- objc_setAssociatedObject获取cell上button对应所在的行
- 【Object-C】 NSTimer用法,暂停,继续,初始化
- protocol的基本使用与代理设计模式的应用
- 在Window环境下,使用CodeBlocks+GNUStep 配置Objective-C开发环境
- Objective-C学习笔记之SEL和@selector
- oc三种传值方式:通知传值,代理传值,block传值