精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

大牛說:這是不可錯過的iOS開發技巧

移動開發 iOS Android
這里面的每個問題,不會太長。或是讀書筆記,或是摘抄,亦或是驗證,每個問題的篇幅爭取在六七百字的樣子。筆記和摘抄的出處會詳細標明。問題的個數不限,湊齊3500字左右就發一篇。爭取每月至少發兩篇吧,權當是對自己學習的一個整理。

一直想做這樣一個小冊子,來記錄自己平時開發、閱讀博客、看書、代碼分析和與人交流中遇到的各種問題。之前有過這樣的嘗試,但都是無疾而終。不過,每天接觸的東西多,有些東西不記下來,忘得也是很快,第二次遇到同樣的問題時,還得再查一遍。好記性不如爛筆頭,所以又決定重拾此事,時不時回頭看看,溫故而知新。

這里面的每個問題,不會太長。或是讀書筆記,或是摘抄,亦或是驗證,每個問題的篇幅爭取在六七百字的樣子。筆記和摘抄的出處會詳細標明。問題的個數不限,湊齊3500字左右就發一篇。爭取每月至少發兩篇吧,權當是對自己學習的一個整理。

本期主要記錄了以下幾個問題:

NSString屬性什么時候用copy,什么時候用strong?

 

Foundation中的斷言處理

 

IBOutletCollection

 

NSRecursiveLock遞歸鎖的使用

 

NSHashTable

 

NSString屬性什么時候用copy,什么時候用strong?

我們在聲明一個NSString屬性時,對于其內存相關特性,通常有兩種選擇(基于ARC環境):strong與copy。那這兩者有什么區別呢?什么時候該用strong,什么時候該用copy呢?讓我們先來看個例子。

 

示例

 

我們定義一個類,并為其聲明兩個字符串屬性,如下所示:

  1. @interface TestStringClass () 
  2.  
  3. @property (nonatomic, strong) NSString *strongString; 
  4. @property (nonatomic, copy) NSString *copyedString; 
  5.  
  6. @end

上面的代碼聲明了兩個字符串屬性,其中一個內存特性是strong,一個是copy。下面我們來看看它們的區別。

首先,我們用一個不可變字符串來為這兩個屬性賦值,

  1. - (void)test { 
  2.  
  3. NSString *string = [NSString stringWithFormat:@"abc"]; 
  4. self.strongString = string; 
  5. self.copyedString = string; 
  6.  
  7. NSLog(@"origin string: %p, %p", string, &string); 
  8. NSLog(@"strong string: %p, %p", _strongString, &_strongString); 
  9. NSLog(@"copy string: %p, %p", _copyedString, &_copyedString); 
  10. }

其輸出結果是:

  1. origin string: 0x7fe441592e200x7fff57519a48 
  2. strong string: 0x7fe441592e200x7fe44159e1f8 
  3. copy string: 0x7fe441592e200x7fe44159e200 

我們要以看到,這種情況下,不管是strong還是copy屬性的對象,其指向的地址都是同一個,即為string指向的地址。如果我們換作MRC環境,打印string的引用計數的話,會看到其引用計數值是3,即strong操作和copy操作都使原字符串對象的引用計數值加了1。

接下來,我們把string由不可變改為可變對象,看看會是什么結果。即將下面這一句

  1. NSString *string = [NSString stringWithFormat:@"abc"]; 

改成:

  1. NSMutableString *string = [NSMutableString stringWithFormat:@"abc"]; 

其輸出結果是:

  1. origin string: 0x7ff5f2e33c900x7fff59937a48 
  2. strong string: 0x7ff5f2e33c900x7ff5f2e2aec8 
  3. copy string: 0x7ff5f2e2aee00x7ff5f2e2aed0 

可以發現,此時copy屬性字符串已不再指向string字符串對象,而是深拷貝了string字符串,并讓_copyedString對象指向這個字符串。在MRC環境下,打印兩者的引用計數,可以看到string對象的引用計數是2,而_copyedString對象的引用計數是1。

此時,我們如果去修改string字符串的話,可以看到:因為_strongString與string是指向同一對象,所以_strongString的值也會跟隨著改變(需要注意的是,此時_strongString的類型實際上是NSMutableString,而不是NSString);而_copyedString是指向另一個對象的,所以并不會改變。

 

結論

 

由于NSMutableString是NSString的子類,所以一個NSString指針可以指向NSMutableString對象,讓我們的strongString指針指向一個可變字符串是OK的。

而上面的例子可以看出,當源字符串是NSString時,由于字符串是不可變的,所以,不管是strong還是copy屬性的對象,都是指向源對象,copy操作只是做了次淺拷貝。

當源字符串是NSMutableString時,strong屬性只是增加了源字符串的引用計數,而copy屬性則是對源字符串做了次深拷貝,產生一個新的對象,且copy屬性對象指向這個新的對象。另外需要注意的是,這個copy屬性對象的類型始終是NSString,而不是NSMutableString,因此其是不可變的。

這里還有一個性能問題,即在源字符串是NSMutableString,strong是單純的增加對象的引用計數,而copy操作是執行了一次深拷貝,所以性能上會有所差異。而如果源字符串是NSString時,則沒有這個問題。

所以,在聲明NSString屬性時,到底是選擇strong還是copy,可以根據實際情況來定。不過,一般我們將對象聲明為NSString時,都不希望它改變,所以大多數情況下,我們建議用copy,以免因可變字符串的修改導致的一些非預期問題。

關于字符串的內存管理,還有些有意思的東西,可以參考NSString特性分析學習。

 

參考

 

NSString copy not copying?

 

NSString特性分析學習

 

NSString什么時候用copy,什么時候用strong

 

#p#

Foundation中的斷言處理

經常在看一些第三方庫的代碼時,或者自己在寫一些基礎類時,都會用到斷言。所以在此總結一下Objective-C中關于斷言的一些問題。

Foundation中定義了兩組斷言相關的宏,分別是:

  1. NSAssert / NSCAssert 
  2. NSParameterAssert / NSCParameterAssert 

這兩組宏主要在功能和語義上有所差別,這些區別主要有以下兩點:

如果我們需要確保方法或函數的輸入參數的正確性,則應該在方法(函數)的頂部使用NSParameterAssert / NSCParameterAssert;而在其它情況下,使用NSAssert / NSCAssert。

 

另一個不同是介于C和Objective-C之間。NSAssert / NSParameterAssert應該用于Objective-C的上下文(方法)中,而NSCAssert / NSCParameterAssert應該用于C的上下文(函數)中。

 

當斷言失敗時,通常是會拋出一個如下所示的異常:

  1. *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'true is not equal to false' 

Foundation為了處理斷言,專門定義了一個NSAssertionHandler來處理斷言的失敗情況。NSAssertionHandler對象是自動創建的,用于處理失敗的斷言。當斷言失敗時,會傳遞一個字符串給NSAssertionHandler對象來描述失敗的原因。每個線程都有自己的NSAssertionHandler對象。當調用時,一個斷言處理器會打印包含方法和類(或函數)的錯誤消息,并引發一個NSInternalInconsistencyException異常。就像上面所看到的一樣。

我們很少直接去調用NSAssertionHandler的斷言處理方法,通常都是自動調用的。

NSAssertionHandler提供的方法并不多,就三個,如下所示:

  1. // 返回與當前線程的NSAssertionHandler對象。 
  2. // 如果當前線程沒有相關的斷言處理器,則該方法會創建一個并指定給當前線程 
  3. + (NSAssertionHandler *)currentHandler 
  4.  
  5. // 當NSCAssert或NSCParameterAssert斷言失敗時,會調用這個方法 
  6. - (void)handleFailureInFunction:(NSString *)functionName file:(NSString *)object lineNumber:(NSInteger)fileName description:(NSString *)line, format,... 
  7.  
  8. // 當NSAssert或NSParameterAssert斷言失敗時,會調用這個方法 
  9. - (void)handleFailureInMethod:(SEL)selector object:(id)object file:(NSString *)fileName lineNumber:(NSInteger)line description:(NSString *)format, ...

另外,還定義了一個常量字符串,

  1. NSString * const NSAssertionHandlerKey; 

主要是用于在線程的threadDictionary字典中獲取或設置斷言處理器。

關于斷言,還需要注意的一點是在Xcode 4.2以后,在release版本中斷言是默認關閉的,這是由宏NS_BLOCK_ASSERTIONS來處理的。也就是說,當編譯release版本時,所有的斷言調用都是無效的。

我們可以自定義一個繼承自NSAssertionHandler的斷言處理類,來實現一些我們自己的需求。如Mattt Thompson的NSAssertion​Handler實例一樣:

  1. @interface LoggingAssertionHandler : NSAssertionHandler 
  2. @end 
  3.  
  4. @implementation LoggingAssertionHandler 
  5.  
  6. - (void)handleFailureInMethod:(SEL)selector 
  7. object:(id)object 
  8. file:(NSString *)fileName 
  9. lineNumber:(NSInteger)line 
  10. description:(NSString *)format, ... 
  11. NSLog(@"NSAssert Failure: Method %@ for object %@ in %@#%i", NSStringFromSelector(selector), object, fileName, line); 
  12.  
  13. - (void)handleFailureInFunction:(NSString *)functionName 
  14. file:(NSString *)fileName 
  15. lineNumber:(NSInteger)line 
  16. description:(NSString *)format, ... 
  17. NSLog(@"NSCAssert Failure: Function (%@) in %@#%i", functionName, fileName, line); 
  18.  
  19. @end

上面說過,每個線程都有自己的斷言處理器。我們可以通過為線程的threadDictionary字典中的NSAssertionHandlerKey指定一個新值,來改變線程的斷言處理器。

如下代碼所示:

  1. - (BOOL)application:(UIApplication *)application 
  2. didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
  3. NSAssertionHandler *assertionHandler = [[LoggingAssertionHandler alloc] init]; 
  4. [[[NSThread currentThread] threadDictionary] setValue:assertionHandler 
  5. forKey:NSAssertionHandlerKey]; 
  6. // ... 
  7.  
  8. return YES; 
  9. }

而什么時候應該使用斷言呢?通常我們期望程序按照我們的預期去運行時,如調用的參數為空時流程就無法繼續下去時,可以使用斷言。但另一方面,我們也需要考慮,在這加斷言確實是需要的么?我們是否可以通過更多的容錯處理來使程序正常運行呢?

Mattt Thompson在NSAssertion​Handler中的倒數第二段說得挺有意思,在此摘抄一下:

But if we look deeper into NSAssertionHandler—and indeed, into our own hearts, there are lessons to be learned about our capacity for kindness and compassion; about our ability to forgive others, and to recover from our own missteps. We can't be right all of the time. We all make mistakes. By accepting limitations in ourselves and others, only then are we able to grow as individuals.

參考

NSAssertion​Handler

 

NSAssertionHandler Class Reference

 

#p#

IBOutletCollection

在IB與相關文件做連接時,我們經常會用到兩個關鍵字:IBOutlet和IBAction。經常用xib或storyboard的童鞋應該用這兩上關鍵字非常熟悉了。不過UIKit還提供了另一個偽關鍵字IBOutletCollection,我們使用這個關鍵字,可以將界面上一組相同的控件連接到同一個數組中

我們先來看看這個偽關鍵字的定義,可以從UIKit.framework的頭文件UINibDeclarations.h找到如下定義:

  1. #ifndef IBOutletCollection 
  2. #define IBOutletCollection(ClassName) 
  3. #endif 

另外,在Clang源碼中,有更安全的定義方式,如下所示:

  1. #define IBOutletCollection(ClassName) __attribute__((iboutletcollection(ClassName))) 

從上面的定義可以看到,與IBOutlet不同的是,IBOutletCollection帶有一個參數,該參數是一個類名。

通常情況下,我們使用一個IBOutletCollection屬性時,屬性必須是strong的,且類型是NSArray,如下所示:

  1. @property (strong, nonatomic) IBOutletCollection(UIScrollView) NSArray *scrollViews; 

假定我們的xib文件中有三個橫向的scrollView,我們便可以將這三個scrollView都連接至scrollViews屬性,然后在我們的代碼中便可以做一些統一處理,如下所示:

  1. - (void)setupScrollViewImages 
  2. for (UIScrollView *scrollView in self.scrollViews) { 
  3. [self.imagesData enumerateObjectsUsingBlock:^(NSString *imageName, NSUInteger idx, BOOL *stop) { 
  4. UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(CGRectGetWidth(scrollView.frame) * idx, 0, CGRectGetWidth(scrollView.frame), CGRectGetHeight(scrollView.frame))]; 
  5. imageView.contentMode = UIViewContentModeScaleAspectFill; 
  6. imageView.image = [UIImage imageNamed:imageName]; 
  7. [scrollView addSubview:imageView]; 
  8. }]; 

這段代碼會影響到三個scrollView。這樣做的好處是我們不需要手動通過addObject:方法將scrollView添加到scrollViews中。

不過在使用IBOutletCollection時,需要注意兩點:

IBOutletCollection集合中對象的順序是不確定的。我們通過調試方法可以看到集合中對象的順序跟我們連接的順序是一樣的。但是這個順序可能會因為不同版本的Xcode而有所不同。所以我們不應該試圖在代碼中去假定這種順序。

 

不管IBOutletCollection(ClassName)中的控件是什么,屬性的類型始終是NSArray。實際上,我們可以聲明是任何類型,如NSSet,NSMutableArray,甚至可以是UIColor,但不管我們在此設置的是什么類,IBOutletCollection屬性總是指向一個NSArray數組。

 

關于第二點,我們以上面的scrollViews為例,作如下修改:

  1. @property (strong, nonatomic) IBOutletCollection(UIScrollView) NSSet *scrollViews; 

實際上我們在控制臺打印這個scrollViews時,結果如下所示:

  1. (lldb) po self.scrollViews 
  2. <__NSArrayI 0x1740573d0>( 
  3. <UIScrollView: 0x12d60d770; frame = (0 0320 162); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x1740574f0>; layer = <CALayer: 0x174229480>; contentOffset: {00}; contentSize: {00}>, 
  4. <UIScrollView: 0x12d60dee0; frame = (0 0320 161); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x174057790>; layer = <CALayer: 0x1742297c0>; contentOffset: {00}; contentSize: {00}>, 
  5. <UIScrollView: 0x12d60e650; frame = (0 0320 163); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x1740579a0>; layer = <CALayer: 0x1742298e0>; contentOffset: {00}; contentSize: {00}> 

可以看到,它指向的是一個NSArray數組。

另外,IBOutletCollection實際上在iOS 4版本中就有了。不過,現在的Objective-C已經支持object literals了,所以定義數組可以直接用@[],方便了許多。而且object literals方式可以添加不在xib中的用代碼定義的視圖,所以顯得更加靈活。當然,兩種方式選擇哪一種,就看我們自己的實際需要和喜好了。

 

參考

 

IBAction / IBOutlet / IBOutlet​Collection

 

IBOutletCollection.m

 

NSRecursiveLock遞歸鎖的使用

NSRecursiveLock實際上定義的是一個遞歸鎖,這個鎖可以被同一線程多次請求,而不會引起死鎖。這主要是用在循環或遞歸操作中。我們先來看一個示例:

  1. NSLock *lock = [[NSLock alloc] init]; 
  2.  
  3. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
  4.  
  5. static void (^RecursiveMethod)(int); 
  6.  
  7. RecursiveMethod = ^(int value) { 
  8.  
  9. [lock lock]; 
  10. if (value > 0) { 
  11.  
  12. NSLog(@"value = %d", value); 
  13. sleep(2); 
  14. RecursiveMethod(value - 1); 
  15. [lock unlock]; 
  16. }; 
  17.  
  18. RecursiveMethod(5); 
  19. });

這段代碼是一個典型的死鎖情況。在我們的線程中,RecursiveMethod是遞歸調用的。所以每次進入這個block時,都會去加一次鎖,而從第二次開始,由于鎖已經被使用了且沒有解鎖,所以它需要等待鎖被解除,這樣就導致了死鎖,線程被阻塞住了。調試器中會輸出如下信息:

  1. value = 5 
  2. *** -[NSLock lock]: deadlock (<NSLock: 0x1700ceee0'(null)') *** Break on _NSLockError() to debug.  

在這種情況下,我們就可以使用NSRecursiveLock。它可以允許同一線程多次加鎖,而不會造成死鎖。遞歸鎖會跟蹤它被lock的次數。每次成功的lock都必須平衡調用unlock操作。只有所有達到這種平衡,鎖***才能被釋放,以供其它線程使用。

所以,對上面的代碼進行一下改造,

NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];

這樣,程序就能正常運行了,其輸出如下所示:

  1. value = 5 
  2. value = 4 
  3. value = 3 
  4. value = 2 
  5. value = 1 

NSRecursiveLock除了實現NSLocking協議的方法外,還提供了兩個方法,分別如下:

  1. // 在給定的時間之前去嘗試請求一個鎖 
  2. - (BOOL)lockBeforeDate:(NSDate *)limit 
  3.  
  4. // 嘗試去請求一個鎖,并會立即返回一個布爾值,表示嘗試是否成功 
  5. - (BOOL)tryLock 
  6.  
  7. 這兩個方法都可以用于在多線程的情況下,去嘗試請求一個遞歸鎖,然后根據返回的布爾值,來做相應的處理。如下代碼所示: 
  8.  
  9. NSRecursiveLock *lock = [[NSRecursiveLock alloc] init]; 
  10.  
  11. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
  12.  
  13. static void (^RecursiveMethod)(int); 
  14.  
  15. RecursiveMethod = ^(int value) { 
  16.  
  17. [lock lock]; 
  18. if (value > 0) { 
  19.  
  20. NSLog(@"value = %d", value); 
  21. sleep(2); 
  22. RecursiveMethod(value - 1); 
  23. [lock unlock]; 
  24. }; 
  25.  
  26. RecursiveMethod(5); 
  27. }); 
  28.  
  29. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
  30.  
  31. sleep(2); 
  32. BOOL flag = [lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:1]]; 
  33. if (flag) { 
  34. NSLog(@"lock before date"); 
  35.  
  36. [lock unlock]; 
  37. else { 
  38. NSLog(@"fail to lock before date"); 
  39. });

在前面的代碼中,我們又添加了一段代碼,增加一個線程來獲取遞歸鎖。我們在第二個線程中嘗試去獲取遞歸鎖,當然這種情況下是會失敗的,輸出結果如下:

  1. value = 5 
  2. value = 4 
  3. fail to lock before date 
  4. value = 3 
  5. value = 2 
  6. value = 1 

另外,NSRecursiveLock還聲明了一個name屬性,如下:

@property(copy) NSString *name

我們可以使用這個字符串來標識一個鎖。Cocoa也會使用這個name作為錯誤描述信息的一部分。

 

參考

 

NSRecursiveLock Class Reference

 

Objective-C中不同方式實現鎖(二)

 

NSHashTable

在看KVOController的代碼時,又看到了NSHashTable這個類,所以就此整理一下。

NSHashTable效仿了NSSet(NSMutableSet),但提供了比NSSet更多的操作選項,尤其是在對弱引用關系的支持上,NSHashTable在對象/內存處理時更加的靈活。相較于NSSet,NSHashTable具有以下特性:

NSSet(NSMutableSet)持有其元素的強引用,同時這些元素是使用hash值及isEqual:方法來做hash檢測及判斷是否相等的。

 

NSHashTable是可變的,它沒有不可變版本。

 

它可以持有元素的弱引用,而且在對象被銷毀后能正確地將其移除。而這一點在NSSet是做不到的。

 

它的成員可以在添加時被拷貝。

 

它的成員可以使用指針來標識是否相等及做hash檢測。

 

它可以包含任意指針,其成員沒有限制為對象。我們可以配置一個NSHashTable實例來操作任意的指針,而不僅僅是對象。

 

初始化NSHashTable時,我們可以設置一個初始選項,這個選項確定了這個NSHashTable對象后面所有的行為。這個選項是由NSHashTableOptions枚舉來定義的,如下所示:

  1. enum { 
  2.  
  3. // 默認行為,強引用集合中的對象,等同于NSSet 
  4. NSHashTableStrongMemory = 0
  5.  
  6. // 在將對象添加到集合之前,會拷貝對象 
  7. NSHashTableCopyIn = NSPointerFunctionsCopyIn, 
  8.  
  9. // 使用移位指針(shifted pointer)來做hash檢測及確定兩個對象是否相等; 
  10. // 同時使用description方法來做描述字符串 
  11. NSHashTableObjectPointerPersonality = NSPointerFunctionsObjectPointerPersonality, 
  12.  
  13. // 弱引用集合中的對象,且在對象被釋放后,會被正確的移除。 
  14. NSHashTableWeakMemory = NSPointerFunctionsWeakMemory 
  15. }; 
  16. typedef NSUInteger NSHashTableOptions;

當然,我們還可以使用NSPointerFunctions來初始化,但只有使用NSHashTableOptions定義的這些值,才能確保NSHashTable的各個API可以正確的工作—包括拷貝、歸檔及快速枚舉。

個人認為NSHashTable吸引人的地方在于可以持有元素的弱引用,而且在對象被銷毀后能正確地將其移除。我們來寫個示例:

  1. // 具體調用如下 
  2. @implementation TestHashAndMapTableClass { 
  3.  
  4. NSMutableDictionary *_dic; 
  5. NSSet *_set; 
  6.  
  7. NSHashTable *_hashTable; 
  8.  
  9. - (instancetype)init { 
  10.  
  11. self = [super init]; 
  12.  
  13. if (self) { 
  14.  
  15. [self testWeakMemory]; 
  16.  
  17. NSLog(@"hash table [init]: %@", _hashTable); 
  18.  
  19. return self; 
  20.  
  21. - (void)testWeakMemory { 
  22.  
  23. if (!_hashTable) { 
  24. _hashTable = [NSHashTable weakObjectsHashTable]; 
  25.  
  26. NSObject *obj = [[NSObject alloc] init]; 
  27.  
  28. [_hashTable addObject:obj]; 
  29.  
  30. NSLog(@"hash table [testWeakMemory] : %@", _hashTable); 
  31.  
  32. 這段代碼的輸出結果如下: 
  33.  
  34. hash table [testWeakMemory] : NSHashTable { 
  35. [6] <NSObject: 0x7fa2b1562670
  36. hash table [init]: NSHashTable { 
  37. }

可以看到,在離開testWeakMemory方法,obj對象被釋放,同時對象在集合中的引用也被安全的刪除。

這樣看來,NSHashTable似乎比NSSet(NSMutableSet)要好啊。那是不是我們就應用都使用NSHashTable呢?Peter Steinberger在The Foundation Collection Classes給了我們一組數據,顯示在添加對象的操作中,NSHashTable所有的時間差不多是NSMutableSet的2倍,而在其它操作中,性能大體相近。所以,如果我們只需要NSSet的特性,就盡量用NSSet。

另外,Mattt Thompson在NSHash​Table & NSMap​Table的結尾也寫了段挺有意思的話,在此直接摘抄過來:

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

 

NSHash​Table & NSMap​Table

 

NSHashTable & NSMapTable

 

The Foundation Collection Classes

 

零碎

 

(一) “Unknown class XXViewController in Interface Builder file.”“ 問題處理

 

最近在靜態庫中寫了一個XXViewController類,然后在主工程的xib中,將xib的類指定為XXViewController,程序運行時,報了如下錯誤:

 

  1. Unknown class XXViewController in Interface Builder file. 

之前也遇到這個問題,但已記得不太清楚,所以又開始在stackoverflow上找答案。

其實這個問題與Interface Builder無關,最直接的原因還是相關的symbol沒有從靜態庫中加載進來。這種問題的處理就是在Target的”Build Setting”–>“Other Link Flags”中加上”-all_load -ObjC”這兩個標識位,這樣就OK了。

 

(二)關于Unbalanced calls to begin/end appearance transitions for …問題的處理

 

我們的某個業務有這么一個需求,進入一個列表后需要立馬又push一個web頁面,做一些活動的推廣。在iOS 8上,我們的實現是一切OK的;但到了iOS 7上,就發現這個web頁面push不出來了,同時控制臺給了一條警告消息,即如下:

  1. Unbalanced calls to begin/end appearance transitions for ... 

在這種情況下,點擊導航欄中的返回按鈕時,直接顯示一個黑屏。

我們到stackoverflow上查了一下,有這么一段提示:

  1. occurs when you try and display a new viewcontroller before the current view controller is finished displaying. 

意思是說在當前視圖控制器完成顯示之前,又試圖去顯示一個新的視圖控制器。

于是我們去排查代碼,果然發現,在viewDidLoad里面去做了次網絡請求操作,且請求返回后就去push這個web活動推廣頁。此時,當前的視圖控制器可能并未顯示完成(即未完成push操作)。

  1. Basically you are trying to push two view controllers onto the stack at almost the same time.  

當幾乎同時將兩個視圖控制器push到當前的導航控制器棧中時,或者同時pop兩個不同的視圖控制器,就會出現不確定的結果。所以我們應該確保同一時間,對同一個導航控制器棧只有一個操作,即便當前的視圖控制器正在動畫過程中,也不應該再去push或pop一個新的視圖控制器。

所以***我們把web活動的數據請求放到了viewDidAppear里面,并做了些處理,這樣問題就解決了。

責任編輯:chenqingxiang 來源: 南峰子的技術博客
相關推薦

2015-07-07 14:05:22

iOS技巧

2015-07-07 10:15:56

iOSUIVisualEffweak

2019-07-23 09:00:00

vuejavascript前端

2015-10-21 13:42:54

iOS開發watch OS2

2014-07-23 10:08:34

Angular前端項目

2015-06-10 10:56:50

iOS開發技巧

2024-08-13 08:00:00

2024-01-09 18:01:38

2021-10-27 08:00:00

DevSecOps開發安全

2015-07-28 20:34:01

Android開發框架

2016-10-25 14:27:16

開源Ruby on RaiWeb框架

2016-12-01 08:36:18

編程云環境云戰略

2015-10-14 10:54:20

iOS開發讀書

2020-03-05 12:12:54

數據Python開發

2025-03-24 15:00:13

鴻蒙HarmonyOS

2015-04-01 10:55:55

2022-04-15 09:01:18

前端工具UTF8編碼

2021-01-05 05:15:02

Github 前端倉庫

2018-10-23 10:35:20

react.jsReact面試題前端

2021-04-21 07:51:06

Vue 開發VS CodeVetur
點贊
收藏

51CTO技術棧公眾號

红杏成人性视频免费看| 操你啦视频在线| 视频一区视频二区中文字幕| 最好看的2019年中文视频| 99九九99九九九99九他书对| 免费网站在线观看人| 91在线视频网址| 国产一区二区在线播放| 久久免费视频播放| 日韩1区在线| 精品久久久久久久久久久久包黑料| 久久免费视频3| 免费日本一区二区三区视频| 成人免费视频播放| 国产精品久久久久久久午夜| 久久人人爽人人爽人人| 欧美亚洲精品在线| 亚洲精品一区二区三区影院| 欧美精品性生活| 七七成人影院| 中文字幕视频一区| 欧美精品二区三区四区免费看视频 | 亚洲综合20p| 偷拍中文亚洲欧美动漫| 亚洲一区二区三区四区五区黄 | 免费黄色网页在线观看| av中文字幕一区| 91社区国产高清| 中文字幕精品无| 亚洲国产国产亚洲一二三| 久久久国产精品亚洲一区| 中文字幕一二三四区| 亚洲精品观看| 欧美一区二区三区四区在线观看 | 国产成人亚洲欧洲在线| 中文字幕亚洲综合久久五月天色无吗''| 精品小视频在线| 美女搡bbb又爽又猛又黄www| 日日夜夜综合| 欧美午夜电影一区| 亚洲国产精品久久久久爰色欲| 欧美午夜大胆人体| 亚洲视频在线一区二区| 亚洲欧美精品| 99riav在线| 国产欧美日韩精品在线| 热re99久久精品国产99热| 天天操天天操天天| 99久久免费国产| 精品一区二区三区免费毛片| 黄色av一区二区三区| 国产老肥熟一区二区三区| 成人亚洲激情网| 一本大道伊人av久久综合| 日韩高清欧美激情| 国产精品久久久久久久久久免费 | 国产一区二区三区免费| 91精品在线看| 国产色综合视频| 国产一区二区h| 亚洲在线视频观看| 精品人妻伦一区二区三区久久| 国产一区二区三区在线观看精品 | 福利视频第一区| 丝袜老师办公室里做好紧好爽| 制服丝袜专区在线| 高清在线一区二区| 成人午夜短视频| 91精品国产综合久久久久久丝袜 | 亚洲电影免费观看| 波多野结衣影院| 久操国产精品| 在线视频一区二区| 三级黄色在线观看| 欧美在线高清| 久久理论片午夜琪琪电影网| 国产精品视频免费播放| 老司机精品视频网站| 国产精品午夜一区二区欲梦| 911精品国产一区二区在线| 夜夜添无码一区二区三区| 欧美xxxx性xxxxx高清| 精品日韩美女的视频高清| 成人黄色片视频| 日韩三级一区| 精品电影一区二区| 性欧美丰满熟妇xxxx性仙踪林| 国产一区二区三区四区二区| 久久国产一区二区三区| 久久久久人妻一区精品色欧美| 日韩香蕉视频| 国产精品入口免费视频一| 国产三级第一页| 91一区一区三区| 一区二区三区精品国产| 牛牛电影国产一区二区| 一本久久a久久免费精品不卡| 免费看污黄网站| 99这里只有精品视频| 亚洲午夜av久久乱码| 久久久久亚洲av片无码| 国产欧美一级| 亚洲自拍高清视频网站| 欧美亚洲日本| 亚洲男人天堂av网| 免费国产成人av| 北条麻妃一区二区三区在线| 中文字幕在线看视频国产欧美| 久久激情免费视频| 老司机精品视频一区二区三区| 国产精品午夜av在线| 91亚洲欧美| 欧美日韩国产激情| gogo亚洲国模私拍人体| 精品一区二区三区的国产在线观看| 欧美成人黄色小视频| 夜夜躁日日躁狠狠久久av| 夫妻av一区二区| 一区在线电影| 日韩成人高清| 日韩国产在线看| 中文字幕av免费在线观看| 免费看黄色91| 欧美日韩无遮挡| 99thz桃花论族在线播放| 欧美日韩高清影院| 国产免费看av| 日韩一级大片| 99热99热| 97影院秋霞午夜在线观看| 在线免费观看一区| 丝袜美腿中文字幕| 一区二区91| 国产精华一区| 青草av在线| 日韩精品一区在线观看| 久草视频手机在线| 麻豆成人免费电影| 亚洲精品日韩成人| 色成人免费网站| 亚洲少妇中文在线| 久久久久在线视频| 久久久美女毛片| 92看片淫黄大片一级| 日韩激情啪啪| 91精品国产高清久久久久久久久| 免费激情视频网站| 亚洲国产精品尤物yw在线观看| 绯色av蜜臀vs少妇| 国模吧视频一区| 国产一区二区无遮挡| 国产传媒在线观看| 日韩精品极品在线观看| 日本亚洲欧美在线| 91免费看片在线观看| 久久美女福利视频| 成人激情诱惑| 91九色国产在线| 久久久pmvav| 亚洲午夜久久久久| 亚洲成人日韩在线| 日韩电影在线免费看| 亚洲精品久久久久久一区二区| 日韩在线激情| 欧美丰满老妇厨房牲生活| 亚洲精品视频专区| 福利一区福利二区微拍刺激| 30一40一50老女人毛片| 日本色综合中文字幕| 一区二区视频在线观看| 亚洲午夜免费| 55夜色66夜色国产精品视频| 国产经典自拍视频在线观看| 欧美欧美欧美欧美首页| 免费在线视频观看| 91丝袜美腿高跟国产极品老师 | 91porny在线| 国产亚洲精品bt天堂精选| 免费一区二区三区在线观看| 久久精品亚洲欧美日韩精品中文字幕| 成人av片网址| 日本综合视频| 精品中文字幕乱| 欧美日韩伦理片| 欧美日韩aaaaaa| 久久久久久久久久久久国产| 久久综合精品国产一区二区三区| 在线观看av网页| 国产精品s色| 欧美亚洲爱爱另类综合| 国产亚洲观看| 国产成人久久久| 日韩精品亚洲人成在线观看| 亚洲精品一区二三区不卡| 国产又黄又猛又爽| 亚洲444eee在线观看| 国产第一页精品| www.亚洲在线| 欧美激情国内自拍| 亚洲综合不卡| 97av中文字幕| 不卡在线一区二区| 国产在线精品二区| 91成人小视频| 国产成人精品视| 国产经典三级在线| 中文字幕亚洲无线码在线一区| 丰满肉嫩西川结衣av| 欧美日韩在线综合| 韩国av中文字幕| 一区二区三区不卡视频| 黄色av免费播放| 99国产精品一区| a级大片免费看| 琪琪一区二区三区| 久久精品免费一区二区| 亚洲婷婷在线| 美女在线免费视频| 日韩国产在线| 欧美一区二区在线| 激情视频极品美女日韩| 91日本在线观看| 久久精品资源| 国产成人拍精品视频午夜网站| 91九色美女在线视频| 久久精品国产2020观看福利| 国产福利在线| 亚洲欧美色婷婷| 手机av免费在线观看| 日韩无一区二区| 国产女人高潮时对白| 欧美性xxxxxxxx| 丰满少妇xoxoxo视频| 精品久久久久国产| 日本三级理论片| 亚洲一区二三区| 久久久久免费看| 夜夜精品浪潮av一区二区三区| 亚洲天堂一级片| 中文字幕日韩欧美一区二区三区| 亚洲v国产v欧美v久久久久久| 26uuu亚洲| 亚洲第一页av| 91麻豆蜜桃一区二区三区| 久久久久亚洲AV成人无码国产| 成熟亚洲日本毛茸茸凸凹| 又色又爽又黄18网站| 国产激情视频一区二区在线观看| 国产欧美激情视频| 精品一区二区免费视频| 久久精品视频在线观看免费| 六月婷婷色综合| 911福利视频| 国产一区在线观看视频| 色黄视频免费看| 丁香婷婷综合五月| 亚洲熟女一区二区| 91蝌蚪porny| 性欧美精品中出| 国产精品色哟哟网站| 欧美另类69xxxx| 亚洲嫩草精品久久| 国产无套在线观看| 色婷婷综合久久久中文一区二区 | 琪琪一区二区三区| 做a视频在线观看| 国产不卡视频在线播放| 香蕉视频污视频| 2021久久国产精品不只是精品| 中国毛片在线观看| 国产精品欧美一区喷水| 婷婷久久综合网| 无码av免费一区二区三区试看 | 337p亚洲精品色噜噜| www五月婷婷| 精品丝袜一区二区三区| 第三区美女视频在线| 久久亚洲综合国产精品99麻豆精品福利 | 日韩在线观看成人| 成全电影大全在线观看| 8x拔播拔播x8国产精品| 亚洲精品555| 不卡一区二区三区视频| 国产精选一区| 欧洲xxxxx| 亚洲女优在线| 天天久久综合网| 波多野结衣中文字幕一区| 少妇av片在线观看| 亚洲综合色噜噜狠狠| 区一区二在线观看| 日韩欧美一区二区不卡| 毛片免费在线| 欧美日韩ab片| av成人免费看| 狠狠爱一区二区三区| 久久精品国产68国产精品亚洲| 日本阿v视频在线观看| 日韩av中文在线观看| 波多野结衣办公室双飞| 国产欧美日韩精品a在线观看| 久久亚洲成人av| 欧美日韩一区三区| 天堂中文字幕在线| 久久av在线播放| 欧美影视资讯| 精品网站在线看| 欧美伊人影院| 一区二区三区网址| 91在线观看污| 免费在线一级片| 69堂国产成人免费视频| 国产小视频免费在线观看| 欧美日本黄视频| 96sao精品免费视频观看| 欧美精品亚洲精品| 亚洲电影在线| 一区二区三区四区影院| 国产精品久久精品日日| 日韩中文字幕在线观看视频| 日韩女优毛片在线| 久久日韩视频| 国产精品视频白浆免费视频| 免费欧美激情| 成人综合视频在线| 北条麻妃国产九九精品视频| 永久免费看片视频教学| 精品视频免费看| seseavlu视频在线| 国产精品扒开腿爽爽爽视频| 欧美电影完整版在线观看| 国产乱子伦精品视频| 精品一区二区三区香蕉蜜桃| 日本人亚洲人jjzzjjz| 色女孩综合影院| 欧美少妇另类| 欧美在线www| 欧美一区二区三区红桃小说| 蜜臀av色欲a片无码精品一区| 国产经典欧美精品| 青娱乐国产在线| 日韩小视频在线观看专区| 国产在线观看a视频| 亚洲一区中文字幕| 一区二区中文字| 原创真实夫妻啪啪av| 亚洲精品国产精华液| 99精品视频免费看| 欧美人成在线视频| 丁香五月缴情综合网| 人体内射精一区二区三区| 粉嫩嫩av羞羞动漫久久久 | 亚洲精品欧美| 日韩aaaaa| 欧美日韩中文在线| 毛片免费在线观看| 国产精品自产拍在线观看中文| 日韩一级毛片| 天天久久综合网| 伊人色综合久久天天| 免费观看a视频| 欧美一区视频在线| 成人影院天天5g天天爽无毒影院| 亚洲第一狼人区| 亚洲精品菠萝久久久久久久| 好吊视频一区二区三区| 97av在线视频| 欧美亚洲国产激情| 97免费公开视频| 亚洲成av人片在线| 国产区av在线| 成人久久久久久久| 激情综合中文娱乐网| 日韩网站在线播放| 欧美高清性hdvideosex| 女人天堂av在线播放| 欧美中文娱乐网| 国产精品自拍网站| 国产成人在线播放视频| 伊人精品在线观看| 日韩精品中文字幕吗一区二区| 玩弄中年熟妇正在播放| 欧美激情一区在线观看| 性生活视频软件| 日本一区二区不卡| 久久精品青草| 蜜桃传媒一区二区亚洲av| 欧美日韩一区二区在线观看 | 在线观看日韩精品| www在线免费观看视频| 久草热久草热线频97精品| 男人的j进女人的j一区| 久久久久亚洲av成人片| 伊人男人综合视频网| 激情综合五月| 精品久久久久久久免费人妻| 亚洲色图清纯唯美| 免费a在线观看| 粉嫩av一区二区三区免费观看| 蜜臀av一级做a爰片久久|