Translate

2013年3月18日 星期一

Singleton sharedInstance

這次在公司的專案有用到Singleton
當然不是我自己想用的拉
是上網找的範例模組有用到只好強迫自己知道這到底有什麼作用
上網找了許多資料有許多人的講法都不一定相同
但是有找到一個我個人覺得算是講得很詳細的

這是為什麼要這樣用
這個 Design Pattern 是用來確保程式中某些物件實體只能有一個、或是數個,不應該被重複性的建立

介紹參考
iOS 单例模式
怎樣用呢
最近在iOS开发中,需要用到单例模式,于是自己动手写了一个,它看起来是这样的:
1.
+ (id)sharedInstance { static id sharedInstance = nil; if (!sharedInstance) { sharedInstance = [[NSObject alloc] init]; } return sharedInstance; }
后来发现许多书上的做法都使用到了BOOL变量作为标值位,它看起来是这样的:
2.
+ (id)sharedInstance { static id sharedInstance = nil; static BOOL token = NO; if (!token) { token = YES; sharedInstance = [[NSObject alloc] init]; } return sharedInstance; } 但是参考了苹果官方的单例模式代码,发现它看起来是这样的:
3.
+ (id)sharedInstance { static id sharedInstance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[NSObject alloc] init]; }); return sharedInstance; } 那么它们究竟有多大区别呢?
原来,它们的区别在于多线程并发时的表现。

使用了一个指针变量作为标志位,这在多线程并发时是不可取的,因为sharedInstance = [[NSObject alloc] init];这行代码的执行本身是需要时间的。很可能有两个线程同时进入到了这行代码,而这将导致内存泄漏。

使用的标志位是一个BOOL变量,当多线程并发时,会出现1线程判断为NO,开始alloc并做赋值操作,但是2线程进入时判断为YES,而1线程赋值操作还没执行结束,这时候2线程会拿到一个nil。尽管它不会造成内存泄漏,但是它会有相当多的线程获取不到对象。

使用了dispatch_once函数。这个函数来自于Grand Central Dispatch (GCD),Apple自Mac OS 10.6 / iOS 4.0引用了它。
该函数接收一个dispatch_once_t用于检查该代码块是否已经被调度的谓词(是一个长整型,实际上作为BOOL使用)。它还接收一个希望在应用的生命周期内仅被调度一次的代码块。这不仅意味着代码仅会被运行一次,而且还是线程安全的,你不需要使用诸如@synchronized之类的来防止使用多个线程或者队列时不同步的问题。

Apple的GCD Documentation证实了这一点:
如果被多个线程调用,该函数会同步等等直至代码块完成。

所以我又上google查看看其他說法
目前我覺得這是最多人覺得這答案最好

+(id)sharedInstance { static dispatch_once_t pred; static MyClass *sharedInstance = nil; dispatch_once(&pred, ^{ sharedInstance = [[MyClass alloc] init]; }); return sharedInstance; }
釋放掉
- (void)dealloc { // implement -dealloc & remove abort() when refactoring for // non-singleton use. abort(); }
參考
+ (MyClass *)sharedInstance { static MyClass *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[MyClass alloc] init]; // Do any other initialisation stuff here }); return sharedInstance; }
不建議這樣寫
+ (MyClass *)sharedInstance { static MyClass *sharedInstance = nil; @synchronized(self) { if (sharedInstance == nil) { sharedInstance = [[self alloc] init]; } } return sharedInstance; }

沒有留言:

張貼留言