2011-09-29 34 views
1

我最近在我的應用程序中發現了一個非常大的性能問題,這是由於在[UIImage imagenamed:]中找不到圖像。UIImage imageNamed擴展

我想知道是否有一種「插入」的解決方案,以某種方式記錄這種'錯誤'?我開始寫一個擴展的UIImage類,像這樣:

@implementation UIImage (Debug) 
#ifdef DEBUG 
+ (UIImage*) imageNamed:(NSString*) name{ 
    UIImage* img = [UIImage imageNamed:name]; 

    if(!img){ 
     NSLog(@"Error: referencing non-exiting image: %@", name); 
    } 

    return img; 
} 
#endif 
@end 

但是,這會導致一個無限循環,因爲[UIImage的imageNamed:名字]當然會使擴展方法再次調用...

有什麼建議嗎?

感謝 托馬斯

+0

你有以下幾個像樣的答案,對捕捉這些類型的錯誤(我建議BTW混寫的實現),但我在這裏傳福音不使用'imageNamed:'。它緩存並永遠不會清除您加載的每個圖像,因此如果您加載大量圖像,那麼您將很快通過內存燒錄。你可以在StackOverflow上找到替代緩存方案。 – AndrewS

+0

@AndrewS:雖然'imageNamed:'緩存(這在很多情況下是一個性能增益),但它也會*在應用程序收到內存警告時清除內存中的圖像。請參閱WWDC 2011 Session 318.早期的iOS版本中存在一個阻止此操作的錯誤,但它很長時間沒有修復。 – DarkDust

回答

6

你應該從未使用類別來覆蓋現有的方法。這將導致意想不到的結果(所使用的實現取決於運行時加載二進制映像/模塊的順序)

相反,您可以使用objc運行時的可能性將一個選擇器的實現與另一個選擇器交換(有時稱爲方法swizzling)。但是如果你不知道的含義,我會勸阻你這樣做。 (調用交換方法,如果你想調用原來以避免調用循環,管理,當該方法在父類中實現,但使用情況不是孩子,和更多的細微之處)


如果您只想調試並在未找到UIImage時收到警報使用符號斷點! (斷點不限於被放置在代碼一個給定的線!)

斷點是更強大的比你能想象(我建議您觀看關於「在Xcode掌握調試WWDC'11視頻會議「),尤其是你可以地方斷點沒有在你的代碼在一個特定的方法調用(在你的情況下該方法-imageNamed:)特定的路線,但添加條件斷點所以它只會在一定的條件下被擊中(返回圖像無?)。你甚至可以通過請求斷點記錄一些信息(或者播放聲音,或者其他任何東西)和/或繼續執行,而不是停止代碼執行。

5

你想要做什麼叫做混寫:你交換兩種方法讓你自己的方法現在被稱爲替代,您可以方法的名稱下訪問方法。似乎有點起初混淆,但在這裏是如何工作的:

#import <objc/runtime.h>  

@implementation UIImage(Debug) 

// Executed once on startup, before anything at UIImage is called. 
+ (void)load 
{ 
    Class c = (id)self; 

    // Use class_getInstanceMethod for "normal" methods 
    Method m1 = class_getClassMethod(c, @selector(imageNamed:)); 
    Method m2 = class_getClassMethod(c, @selector(swizzle_imageNamed:)); 

    // Swap the two methods. 
    method_exchangeImplementations(m1, m2); 
} 

- (id)swizzle_imageNamed:(NSString *)name 
{ 
    // Do your stuff here. By the time this is called, this method 
    // is actually called "imageNamed:" and the original method 
    // is now "swizzle_imageNamed:". 

    doStuff(); 
    // Call original method 
    id foo = [self swizzle_imageNamed:name]; 
    doMoreStuff(); 
    return something; 
} 

@end 
+0

您不應該在類別中覆蓋/重新實現'+ load'。覆蓋類別中的現有方法會導致未定義的行爲,如Apple的文檔中所述(因爲所使用的實現將取決於Runtime加載類及其類別的順序),因此這可能具有風險。 _(相反,您可以在虛擬外部類中提取您的「+ load」方法)_ – AliSoftware

+2

@AliSoftware:謝謝,您對'+ load'說得對。我發現了一個答案,表明另一個好的解決方案:[__attribute__((constructor))](http://stackoverflow.com/questions/4668887/objective-c-is-it-safe-to-overwrite-nsobject-initialize/4671741 #4671741)。 – DarkDust

+0

好的,我確實知道'__attribute __((構造函數))'的存在,但從來沒有想過要使用它!不錯的一個;)無論如何,當我在我的代碼中調用方法時,我更喜歡使用'+ load',並且通常我避免直接​​使用'__attribute__'(或者我使用它通過宏,如NS_REQUIRES_NIL_TERMINATION或類似),主要是爲了可讀性和編譯器兼容性有點差別,因爲LLVM可能支持某些屬性,但不支持GCC,或者在另一個假想的未來編譯器中不支持)。 _但我不得不承認這是一個原始的解決方案;)_ – AliSoftware