我最近在我的应用中发现了一个相当大的性能问题,原因是在[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:name]当然会导致再次调用扩展方法......
有什么建议吗?
感谢 托马斯
答案 0 :(得分:6)
您应该从不使用类别来覆盖现有方法。这将导致意外的结果(使用的实现将取决于运行时加载二进制映像/模块的顺序)。
相反,您可以使用objc运行时的可能性来交换一个选择器与另一个选择器的实现(有时称为方法调整)。但如果你真的不知道其含义,我会劝阻你这样做。 (如果要调用原始方法以避免调用循环,请调用swapped方法,在父类中实现方法时管理用例,而不是子级,以及更多细微之处)
但如果您只想调试并在未找到UIImage时收到提醒使用符号断点! (断点不限于放在给定的代码行!)
断点比你想象的更强大(我鼓励你观看关于“在Xcode中掌握调试”的 WWDC'11 视频会话),特别是你可以放置一个断点不在代码中的特定行,但在特定方法调用上(在您的情况下为方法-imageNamed:
),在断点处添加条件所以它只会在某种情况下被击中(返回的图像为零?)。您甚至可以要求断点记录一些信息(或播放声音或其他)和/或继续执行而不是停止执行代码。
答案 1 :(得分:5)
您想要做的是称为 swizzling :您交换了两种方法,因此现在调用您自己的方法,您可以访问原始方法,名称为<你的方法。起初看起来有点令人困惑,但这是它的工作原理:
#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