UIImage imageName extension

I recently discovered a pretty big performance issue in my application that was caused by the image not being found in [UIImage imagenamed:].

I was wondering if there is a “drop-in” solution so that some kind of “errors” are logged in some way? I started writing an extension for the UIImage class, something like this:

@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 

But this causes an infinite loop, because [UIImage imageNamed: name], of course, calls the extension method again ...

Any suggestions?

Thank you Thomas

+1
ios uiimage
source share
2 answers

You should never use categories to override an existing method . This will lead to unexpected results (the implementation used will depend on how the runtime loads binary images / modules).

Instead, you can use the capabilities of the objc runtime to exchange implementations of one selector with another (sometimes called the swizzling method ). But I would discourage you from doing this if you really don't know the meaning . (call the swap method if you want to call the original to avoid calling cycles, control use-case, when the method is implemented in the parent class, but not in the child device, and much more subtleties)


But , if you only want to debug and receive warnings when UIImage is not found, use character breakpoints ! (Breakpoints are not limited to placing on a given line of code !)

Breakpoints are more powerful than you can imagine (I recommend you see the WWDC'11 session on “mastering debugging in Xcode”), especially you can place a breakpoint not on a specific line of your code, but on calling a specific method (in your In the case of the -imageNamed: method, and add the condition to the breakpoint , so it will only get in a certain state (the image is nil?) You can even request a breakpoint to register any information (or play a sound or something else) and / or continue execution instead of stopping code execution.

+6
source share

What you want to do is called swizzling: you change the two methods, so instead, your own method is now called, and you can access the original method under the name of your method. This is a bit confusing at first, but here's how it works:

 #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 
+6
source share

All Articles