精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

这里只有久久精品视频| 国产自偷自偷免费一区| 亚洲欧美激情在线观看| 亚洲国产精品一区| 亚洲乱码一区av黑人高潮| 污片在线免费看| 调教一区二区| 久久久影院官网| 亚洲a在线播放| 国产成人一级片| 亚洲国产一区二区三区在线播放 | 人妻少妇精品久久| 黄色免费在线播放| 国产成人午夜片在线观看高清观看| 97在线看福利| 欧美做爰啪啪xxxⅹ性| 另类尿喷潮videofree| 欧美日韩中文国产| aa在线观看视频| dy888亚洲精品一区二区三区| 91麻豆福利精品推荐| 5566中文字幕一区二区| 成年人视频免费| 激情综合视频| 爱福利视频一区| 在线不卡av电影| eeuss国产一区二区三区四区| 欧美视频一区二区三区在线观看| 亚洲色图一区二区三区| 国产成人精品免高潮在线观看| 国精品无码一区二区三区| 一道在线中文一区二区三区| 精品欧美一区二区在线观看| 久久久久久久高清| 亚洲第一会所| 精品女厕一区二区三区| 男人日女人的bb| аⅴ资源新版在线天堂| 欧美aaaaaa午夜精品| 欧美最近摘花xxxx摘花| 国产污视频在线观看| 影视一区二区| 啊v视频在线一区二区三区 | 黄色片视频在线播放| 成人福利影视| 亚洲自拍欧美精品| 久久久成人精品一区二区三区| 韩国精品视频| 国产亚洲精品久| 欧美日韩日本网| 日本一区高清| 久久午夜电影网| 免费在线成人av| 亚洲 小说区 图片区 都市| 国产成人av电影| 99re6热在线精品视频播放速度| 亚洲在线免费观看视频| 美女一区二区三区在线观看| 国产精品扒开腿做爽爽爽的视频| 国产一级一级国产| 天堂午夜影视日韩欧美一区二区| 热99精品只有里视频精品| www.日本精品| 另类国产ts人妖高潮视频| 热久久这里只有精品| 青娱乐在线免费视频| 青青草国产成人av片免费| 国产精品久久久久久超碰| 最近国语视频在线观看免费播放| 欧美aaaaa成人免费观看视频| 亚洲国产一区二区三区| 精品精品国产国产自在线| 少妇视频在线播放| 99久久亚洲精品| 欧美大尺度在线观看| 久草资源在线视频| 一本色道久久综合一区| 欧美中文字幕视频在线观看| 久久国产乱子伦精品| 秋霞成人午夜伦在线观看| 国产噜噜噜噜噜久久久久久久久| 亚洲一区二区激情| 国产成人久久精品77777最新版本| 成人国产一区二区| 三级在线观看| 国产精品国产馆在线真实露脸 | 人妻激情偷乱频一区二区三区| 8x国产一区二区三区精品推荐| 亚洲精品乱码久久久久久按摩观| 国产成人精品无码免费看夜聊软件| 欧美freesextv| 欧美激情视频网| 国产亚洲欧美在线精品| 国产自产v一区二区三区c| 国产精品乱码一区二区三区| 巨骚激情综合| 有码一区二区三区| 欧美亚洲国产成人| 国产一区二区视频在线看 | 欧洲毛片在线| 最新中文字幕一区二区三区| 久久亚洲中文字幕无码| 欧美一级做a| 亚洲精品成人网| 亚洲少妇xxx| 国产视频一区在线观看一区免费| 国产精品尤物福利片在线观看| 亚洲精品国产一区二| 久久久久国产成人精品亚洲午夜| 400部精品国偷自产在线观看| 欧美a级在线观看| 制服丝袜亚洲精品中文字幕| 亚洲精品成人无码熟妇在线| 欧美不卡一区| 国产人妖伪娘一区91| 精品人妻一区二区三区蜜桃视频| 国产视频www| 国产成人小视频| 亚洲欧美国产不卡| 毛片电影在线| 日韩欧美成人一区二区| 亚洲天堂最新地址| 亚洲专区免费| 国产亚洲欧美一区二区三区| 91在线中文| 欧美日韩亚洲国产综合| av直播在线观看| 狠狠综合久久| 91视频99| av免费在线网站| 欧美日韩国产区一| 国产第一页精品| 日韩精品午夜视频| 欧美污视频久久久| 国产精品一区二区av影院萌芽| 亚洲国产精品99| 久久久久亚洲AV| 国产一区二区不卡在线| 熟妇熟女乱妇乱女网站| 四虎视频在线精品免费网址| 中文字幕精品久久久久| 小泽玛利亚一区二区三区视频| 久久蜜桃一区二区| 蜜臀av午夜一区二区三区| 欧美日日夜夜| 欧美一级在线亚洲天堂| 天天摸夜夜添狠狠添婷婷| 偷窥少妇高潮呻吟av久久免费 | 久久夜色精品一区| 精品国产免费av| 日韩激情啪啪| 日韩av电影在线播放| 你懂的好爽在线观看| 欧美性生交xxxxx久久久| 国产吞精囗交久久久| 亚洲尤物精选| 水蜜桃一区二区三区| 国产91精品在线| 久久精品99久久久香蕉| 精品毛片一区二区三区| 亚洲综合激情另类小说区| 久草免费资源站| 亚洲久久视频| 欧美一区二区综合| 黄色精品视频| 久久精品国产一区| 亚洲男女视频在线观看| 久久婷婷麻豆| 精品91自产拍在线观看一区| 国产在线视频第一页| 26uuu精品一区二区在线观看| 精品视频偷偷看在线观看| 欧美日韩黄色网| 成人永久aaa| 久久久亚洲精品无码| 国产欧美日韩精品一区二区三区| 国产精品国模在线| 成人直播在线| 亚洲精品国产精品国产自| 中文字幕免费高清网站| 国产精品九色蝌蚪自拍| 波多野吉衣在线视频| 麻豆亚洲精品| 亚洲国产精品影视| 午夜a一级毛片亚洲欧洲| 国产精品色婷婷视频| 亚洲丝袜精品| 一区二区三区动漫| 国产不卡精品视频| 色综合久久久网| 手机av在线看| 91视频观看免费| 欧美视频亚洲图片| 香蕉久久久久久久av网站| 午夜久久资源| 好吊妞国产欧美日韩免费观看网站| 国产成人精品av在线| a篇片在线观看网站| 亚洲欧美成人在线| 午夜精品在线播放| 欧洲日韩一区二区三区| 精品小视频在线观看| 国产欧美一区二区精品秋霞影院| 国产探花一区二区三区| 奇米影视7777精品一区二区| 91免费黄视频| 欧美第一精品| 欧美精品在线一区| 国产精品jk白丝蜜臀av小说| 国产日韩在线观看av| 亚洲美女炮图| 国内自拍欧美激情| 国产丝袜在线| 亚洲最新av在线网站| 五月婷婷免费视频| 欧美成人a∨高清免费观看| 中文在线最新版天堂| 午夜欧美大尺度福利影院在线看| 日本 欧美 国产| 亚洲国产精品传媒在线观看| 日批在线观看视频| 高清不卡在线观看av| 冲田杏梨av在线| 亚洲一区二区动漫| 欧美午夜小视频| 久久久久99精品成人片我成大片| 激情综合网最新| 日本新janpanese乱熟| 国产精品永久| 国产无限制自拍| 欧美日韩一区二区高清| 手机在线视频你懂的| 久久视频在线| 日韩一区二区三区资源| 伊人春色之综合网| 欧美福利精品| 三级小说欧洲区亚洲区| 国产精品一区二区三区免费观看| 秋霞一区二区| 亚洲一区久久久| 国产精品日韩精品在线播放 | 国产精品久久久久久影视| 极品视频在线| 韩国美女主播一区| av手机在线观看| 久久久女女女女999久久| 手机在线免费观看av| 欧美人成在线视频| 蜜臀av在线| 久久久久久久久久婷婷| 19禁羞羞电影院在线观看| 久久久视频免费观看| 爱啪啪综合导航| 欧美综合第一页| 日韩色淫视频| 国产在线拍偷自揄拍精品| 福利精品在线| 成人av色在线观看| 精品一区二区三区中文字幕视频| av一区和二区| 欧美成人一区在线观看| 欧美精品一区二区免费| 一区二区三区四区免费| 大尺度一区二区| 性欧美丰满熟妇xxxx性久久久| 99久久久国产精品| 成人片黄网站色大片免费毛片| 亚洲国产精品99久久久久久久久| 超碰人人人人人人人| 亚洲男人天堂av| 69精品久久久| 日本久久一区二区| 国产伦精品一区二区三区视频痴汉 | 一区二区三区久久网| 我不卡影院28| cao在线观看| 日本欧美一区二区| 国产精品久久久久久久99| 成人国产精品免费网站| 中文字幕免费视频| 亚洲欧美在线观看| 一区二区三区视频免费看| 欧美午夜精品一区二区三区 | 亚洲精品美女久久7777777| 日韩免费高清| 亚洲精品天堂成人片av在线播放| 亚洲人成毛片在线播放女女| 国产成人精品无码播放| 国产一区不卡在线| 欧美成人午夜精品免费| 中文字幕一区二区在线观看| 国产一级特黄a高潮片| 在线一区二区三区四区| 午夜美女福利视频| 国产一区二区三区在线观看视频| 亚洲综合影视| 国产成人精品免费久久久久| 日本免费精品| 日韩一区不卡| 99国产精品视频免费观看一公开| 中文字幕第100页| 成人动漫中文字幕| 91视频最新网址| 色婷婷亚洲一区二区三区| 国产成人精品免费看视频| 一区二区三区国产在线观看| 91美女主播在线视频| 成人激情视频网| 久操成人av| 九一国产精品视频| 国产又黄又大久久| 大吊一区二区三区| 色综合久久99| 午夜激情在线视频| 欧美国产日韩精品| 亚洲精品一区二区在线播放∴| 久久综合久久久| 影音先锋中文字幕一区| 欧美视频亚洲图片| 国产精品网站在线观看| 亚洲GV成人无码久久精品| 精品国产乱码久久久久久图片| 男女啪啪在线观看| 国产精品入口日韩视频大尺度| 亚洲a级精品| 欧美视频在线观看网站| 国产不卡高清在线观看视频| 五月天婷婷色综合| 欧美美女一区二区三区| sese一区| 国产精品黄视频| 啪啪亚洲精品| 中国丰满人妻videoshd| 97se亚洲国产综合在线| 国产成人无码精品亚洲| 337p日本欧洲亚洲大胆色噜噜| 97超碰资源站在线观看| 91免费在线视频网站| 99国产**精品****| 青青草久久伊人| 一区视频在线播放| 国产偷拍一区二区| 不用播放器成人网| 精品国产亚洲一区二区在线观看 | 久草资源在线| 91精品在线看| 欧美激情亚洲| 中文字幕无人区二| 亚洲一区二区三区在线播放| 亚洲经典一区二区三区| 亚州国产精品久久久| 欧美人与动xxxxz0oz| 日本精品www| 欧美激情在线观看视频免费| 成人午夜精品视频| 久久精品国产欧美亚洲人人爽| 亚洲欧洲日韩精品在线| www.69av| 91亚洲精华国产精华精华液| 超碰中文字幕在线| 亚洲天堂一区二区三区| 日韩福利影视| 久久av喷吹av高潮av| 国产99久久久国产精品潘金网站| 国产无码精品视频| 亚洲男人的天堂在线| 成人国产精选| 先锋影音男人资源| 成人黄色大片在线观看 | 亚洲国产精品综合| 国产一区二区三区在线观看精品 | 7777精品伊人久久久大香线蕉| 亚洲淫性视频| 鲁丝片一区二区三区| 美女一区二区三区| 精品97人妻无码中文永久在线| 亚洲国产精品系列| 电影一区电影二区| 日韩精品一区二区三区四| 91麻豆精品在线观看| 在线观看亚洲一区二区| 欧美黑人xxx| 狠狠操综合网| 国模大尺度视频| 91黄色小视频| 18加网站在线| 欧美一区二区综合| 国产成a人亚洲| 成人黄色三级视频| 欧美大片在线免费观看| 久久成人av| 亚洲av午夜精品一区二区三区| 欧美视频在线免费| a级影片在线观看| 日本在线成人一区二区| 成人黄页在线观看| 97人妻精品一区二区三区| 26uuu日韩精品一区二区| 国产精品精品| 国产美女永久免费无遮挡|