Objective-C中不同方式實現(xiàn)鎖(二)
Objective-C中不同方式實現(xiàn)鎖(二)
NSHashTable
在看KVOController的代碼時,又看到了NSHashTable這個類,所以就此整理一下。
NSHashTable效仿了NSSet(NSMutableSet),但提供了比NSSet更多的操作選項,尤其是在對弱引用關(guān)系的支持上,NSHashTable在對象/內(nèi)存處理時更加的靈活。相較于NSSet,NSHashTable具有以下特性:
NSSet(NSMutableSet)持有其元素的強(qiáng)引用,同時這些元素是使用hash值及isEqual:方法來做hash檢測及判斷是否相等的。
NSHashTable是可變的,它沒有不可變版本。
它可以持有元素的弱引用,而且在對象被銷毀后能正確地將其移除。而這一點在NSSet是做不到的。
它的成員可以在添加時被拷貝。
它的成員可以使用指針來標(biāo)識是否相等及做hash檢測。
它可以包含任意指針,其成員沒有限制為對象。我們可以配置一個NSHashTable實例來操作任意的指針,而不僅僅是對象。
初始化NSHashTable時,我們可以設(shè)置一個初始選項,這個選項確定了這個NSHashTable對象后面所有的行為。這個選項是由NSHashTableOptions枚舉來定義的,如下所示:
- enum {
- // 默認(rèn)行為,強(qiáng)引用集合中的對象,等同于NSSet
- NSHashTableStrongMemory = 0,
- // 在將對象添加到集合之前,會拷貝對象
- NSHashTableCopyIn = NSPointerFunctionsCopyIn,
- // 使用移位指針(shifted pointer)來做hash檢測及確定兩個對象是否相等;
- // 同時使用description方法來做描述字符串
- NSHashTableObjectPointerPersonality = NSPointerFunctionsObjectPointerPersonality,
- // 弱引用集合中的對象,且在對象被釋放后,會被正確的移除。
- NSHashTableWeakMemory = NSPointerFunctionsWeakMemory
- };
- typedef NSUInteger NSHashTableOptions;
當(dāng)然,我們還可以使用NSPointerFunctions來初始化,但只有使用NSHashTableOptions定義的這些值,才能確保NSHashTable的各個API可以正確的工作—包括拷貝、歸檔及快速枚舉。
個人認(rèn)為NSHashTable吸引人的地方在于可以持有元素的弱引用,而且在對象被銷毀后能正確地將其移除。我們來寫個示例:
- // 具體調(diào)用如下
- @implementation TestHashAndMapTableClass {
- NSMutableDictionary *_dic;
- NSSet *_set;
- NSHashTable *_hashTable;
- }
- - (instancetype)init {
- self = [super init];
- if (self) {
- [self testWeakMemory];
- NSLog(@"hash table [init]: %@", _hashTable);
- }
- return self;
- }
- - (void)testWeakMemory {
- if (!_hashTable) {
- _hashTable = [NSHashTable weakObjectsHashTable];
- }
- NSObject *obj = [[NSObject alloc] init];
- [_hashTable addObject:obj];
- NSLog(@"hash table [testWeakMemory] : %@", _hashTable);
- }
- 這段代碼的輸出結(jié)果如下:
- hash table [testWeakMemory] : NSHashTable {
- [6] <NSObject: 0x7fa2b1562670>
- }
- hash table [init]: NSHashTable {
- }
可以看到,在離開testWeakMemory方法,obj對象被釋放,同時對象在集合中的引用也被安全的刪除。
這樣看來,NSHashTable似乎比NSSet(NSMutableSet)要好啊。那是不是我們就應(yīng)用都使用NSHashTable呢?Peter Steinberger在The Foundation Collection Classes給了我們一組數(shù)據(jù),顯示在添加對象的操作中,NSHashTable所有的時間差不多是NSMutableSet的2倍,而在其它操作中,性能大體相近。所以,如果我們只需要NSSet的特性,就盡量用NSSet。
另外,Mattt Thompson在NSHashTable & NSMapTable的結(jié)尾也寫了段挺有意思的話,在此直接摘抄過來:
As always, it's important to remember that programming is not about being clever: always approach a problem from the highest viable level of abstraction. NSSet and NSDictionary are great classes. For 99% of problems, they are undoubtedly the correct tool for the job. If, however, your problem has any of the particular memory management constraints described above, then NSHashTable & NSMapTable may be worth a look.
參考
NSHashTable Class Reference
NSHashTable & NSMapTable
NSHashTable & NSMapTable
The Foundation Collection Classes
零碎
(一) “Unknown class XXViewController in Interface Builder file.”“ 問題處
最近在靜態(tài)庫中寫了一個XXViewController類,然后在主工程的xib中,將xib的類指定為XXViewController,程序運行時,報了如下錯誤:
- Unknown class XXViewController in Interface Builder file.
之前也遇到這個問題,但已記得不太清楚,所以又開始在stackoverflow上找答案。
其實這個問題與Interface Builder無關(guān),最直接的原因還是相關(guān)的symbol沒有從靜態(tài)庫中加載進(jìn)來。這種問題的處理就是在Target的”Build Setting”–>“Other Link Flags”中加上”-all_load -ObjC”這兩標(biāo)識位,這樣就OK了。
(二)關(guān)于Unbalanced calls to begin/end appearance transitions for …問題的處理
我們的某個業(yè)務(wù)有這么一個需求,進(jìn)入一個列表后需要立馬又push一個web頁面,做一些活動的推廣。在iOS 8上,我們的實現(xiàn)是一切OK的;但到了iOS 7上,就發(fā)現(xiàn)這個web頁面push不出來了,同時控制臺給了一條警告消息,即如下:
- Unbalanced calls to begin/end appearance transitions for ...
在這種情況下,點擊導(dǎo)航欄中的返回按鈕時,直接顯示一個黑屏。
我們到stackoverflow上查了一下,有這么一段提示:
- occurs when you try and display a new viewcontroller before the current view controller is finished displaying.
意思是說在當(dāng)前視圖控制器完成顯示之前,又試圖去顯示一個新的視圖控制器。
于是我們?nèi)ヅ挪榇a,果然發(fā)現(xiàn),在viewDidLoad里面去做了次網(wǎng)絡(luò)請求操作,且請求返回后就去push這個web活動推廣頁。此時,當(dāng)前的視圖控制器可能并未顯示完成(即未完成push操作)。
- Basically you are trying to push two view controllers onto the stack at almost the same time.
當(dāng)幾乎同時將兩個視圖控制器push到當(dāng)前的導(dǎo)航控制器棧中時,或者同時pop兩個不同的視圖控制器,就會出現(xiàn)不確定的結(jié)果。所以我們應(yīng)該確保同一時間,對同一個導(dǎo)航控制器棧只有一個操作,即便當(dāng)前的視圖控制器正在動畫過程中,也不應(yīng)該再去push或pop一個新的視圖控制器。
所以***我們把web活動的數(shù)據(jù)請求放到了viewDidAppear里面,并做了些處理,這樣問題就解決了。