Effective Objective-C 2.0: Method Swizzling to Debug Opaque Methods
来源:互联网 发布:男生油性皮肤知乎 编辑:程序博客网 时间:2024/05/30 22:42
运行时替换方法实现(通常是新增一个方法(category),然后swap); 这里推荐了一个,debug时候有点用
Item 13: Consider Method Swizzling to Debug Opaque Methods
The method to call when a message is sent to an object in Objective-C is resolved at runtime, as explained in Item 11. It might then occur to you that perhaps the method invoked for a given selector name could be changed at runtime. You’d be correct. This feature can be used to great advantage, as it can be used to change functionality in classes for which you don’t have the source code, without having to subclass and override methods. Thus, the new functionality can be used by all instances of the class rather than only instances of the subclass with overridden methods. Such an approach is often referred to as method swizzling.
A class’s method list contains a list of selector names to implementation mappings, telling the dynamic messaging system where to find the implementation of a given method. The implementations are stored as function pointers called IMP
s and have the following prototype:
id (*IMP)(id, SEL, ...)
The NSString
class responds to selectors called lowercaseString
,uppercaseString,
and capitalizedString,
among others. Each selector points to a different IMP
, making up a table like the one shown in Figure 2.3.
This table can be manipulated by using a few different functions exposed by the Objective-C runtime. You can add selectors to the list, change the implementation pointed to for a given selector, or swap the implementation pointed to by two selectors. After performing a few of these operations, the class’s method table might look something like Figure 2.4.
A new selector called newSelector
has been added, the implementation ofcapitalizedString
has been changed, and the implementations of lowercaseString
and uppercaseString
have been swapped. All this can be done without writing a single subclass, and the new method table layout will be used for each instance ofNSString
in the application. A very powerful feature, I’m sure you’ll agree!
The topic of this item refers to the process of exchanging implementations. In doing so, additional functionality can be added to a method. However, before explaining how it can be used to add functionality, I will explain how to simply swap two existing method implementations. To exchange implementations, you use the following function:
void method_exchangeImplementations(Method m1, Method m2)
This function takes as its arguments the two implementations to exchange, which can be obtained by using the following function:
Method class_getInstanceMethod(Class aClass, SEL aSelector)
This function retrieves a method from a class for the given selector. To swap the implementations of lowercaseString
and uppercaseString
as in the preceding example, you would perform the following:
Click here to view code image
Method originalMethod =
class_getInstanceMethod([NSString class],
@selector(lowercaseString));
Method swappedMethod =
class_getInstanceMethod([NSString class],
@selector(uppercaseString));
method_exchangeImplementations(originalMethod, swappedMethod);
From then on, whenever an NSString
instance has lowercaseString
called on it, the original implementation of uppercaseString
will be invoked and vice versa:
Click here to view code image
NSString *string = @"ThIs iS tHe StRiNg";
NSString *lowercaseString = [string lowercaseString];
NSLog(@"lowercaseString = %@", lowercaseString);
// Output: lowercaseString = THIS IS THE STRING
NSString *uppercaseString = [string uppercaseString];
NSLog(@"uppercaseString = %@", uppercaseString);
// Output: uppercaseString = this is the string
That explains how to exchange method implementations, but in reality, simply swapping two implementations like that is not very useful. After all, there’s a good reason why the implementation for uppercaseString
and lowercaseString
do what they do! There’s no reason why you’d want to swap them. But the same approach can be used to add functionality to an existing method implementation. What if you wanted to log something every time lowercaseString
was called? The same approach can be used to achieve just that. It involves adding another method that implements the additional functionality and then calls through to the original implementation.
The additional method can be added using a category, like so:
Click here to view code image
@interface NSString (EOCMyAdditions)
- (NSString*)eoc_myLowercaseString;
@end
This method is going to be swapped with the original lowercaseString
method, so the method table will end up looking like the one in Figure 2.5.
The implementation of this new method would look like this:
Click here to view code image
@implementation NSString (EOCMyAdditions)
- (NSString*)eoc_myLowercaseString {
NSString *lowercase = [self eoc_myLowercaseString];
NSLog(@"%@ => %@", self, lowercase);
return lowercase;
}
@end
This might look like a recursive call, but remember that the implementations are going to be swapped. So at runtime, when the eoc_myLowercaseString
selector is looked up, it’s the implementation of lowercaseString
that gets called. Finally, to swap the method implementations, the following is used:
Click here to view code image
Method originalMethod =
class_getInstanceMethod([NSString class],
@selector(lowercaseString));
Method swappedMethod =
class_getInstanceMethod([NSString class],
@selector(eoc_myLowercaseString));
method_exchangeImplementations(originalMethod, swappedMethod);
From then on, whenever any NSString
has lowercaseString
called on it, the log line will be printed out:
Click here to view code image
NSString *string = @"ThIs iS tHe StRiNg";
NSString *lowercaseString = [string lowercaseString];
// Output: ThIs iS tHe StRiNg => this is the string
Being able to add in logging like this to methods that are completely opaque to you can be a very useful debugging feature. However, this is usually useful only for debugging. Rarely will you find a need other than debugging to perform method swizzling like this to alter functionality of a class globally. Don’t feel that you should use such a feature just because you can. Overuse can easily lead to code that is difficult to read and unmaintainable.
Things to Remember
Method implementations for a given selector of a class can be added and replaced at runtime.
Swizzling is the process of swapping one method implementation for another, usually to add functionality to the original implementation.
Meddling with methods through the runtime is usually good only for debugging and should not be used just because it can.
- Effective Objective-C 2.0: Method Swizzling to Debug Opaque Methods
- Objective-C Method Swizzling
- Objective-C Method Swizzling
- Objective-C Method Swizzling
- Objective-C Method Swizzling
- Objective-C Method Swizzling
- Objective-C中的Method Swizzling
- [iOS]Objective-C Method Swizzling
- Objective-C的hook方案: Method Swizzling
- Objective-C的hook方案: Method Swizzling
- Objective-C Method Swizzling 的最佳实践
- Objective-C Method Swizzling 的最佳实践
- Objective-c Method Swizzling的详细使用方法
- Objective-C Method Swizzling 的最佳实践
- Objective-C Runtime 运行时:Method Swizzling
- iOS安全–Objective-C Method Swizzling
- Objective-C Runtime :Method Swizzling - method_exchangeImplementations
- iOS安全–Objective-C Method Swizzling
- CvArr、Mat、CvMat、IplImage、BYTE转换(总结而来)
- NYOJ 205 求余数
- MongoDB学习笔记(六) MongoDB索引用法和效率分析
- 工厂模式--java
- 【程序语言】C/C++中如何使用Lua脚本
- Effective Objective-C 2.0: Method Swizzling to Debug Opaque Methods
- 一些有用的链接
- android 处理图片工具
- 【Unity Shaders】Diffuse Shading——向Surface Shader添加properties
- Unity__之NGUI动态加载贴图
- 如何查看服务器RAID卡信息的SHELL脚本和命令介绍
- 数据结构与算法-“数组、链表、树、哈希表”选哪个
- C++赋值兼容规则和类类型转换注意事项之一
- poj 2135最小费用最大流