Effective Objective-C 2.0: Method Swizzling to Debug Opaque Methods

原创 2013年12月05日 23:53:24

运行时替换方法实现(通常是新增一个方法(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 IMPs and have the following prototype:

id (*IMP)(idSEL, ...)

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.

Image

Figure 2.3 NSString’s selector table

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.

Image

Figure 2.4 NSString’s selector table after performing a few operations on it

A new selector called newSelector has been added, the implementation ofcapitalizedString has been changed, and the implementations of lowercaseStringand 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:

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:

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:

@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.

Image

Figure 2.5 Swapping the implementations of lowercaseString andeoc_myLowercaseString

The implementation of this new method would look like this:

@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:

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:

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

Image Method implementations for a given selector of a class can be added and replaced at runtime.

Image Swizzling is the process of swapping one method implementation for another, usually to add functionality to the original implementation.

Image Meddling with methods through the runtime is usually good only for debugging and should not be used just because it can.

编写高质量的iOS代码--Effective Objective-C 2.0 读书笔记

编写高质量的iOS代码--Effective Objective-C 2.0 读书笔记 这本书年初刷完,感觉不错,介绍了很多小点,都是平日不怎么关注的. 第1章 熟悉Objective-C...
  • uxyheaven
  • uxyheaven
  • 2014年12月26日 23:56
  • 5031

阅读《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》总结

第1条:了解Objective-C语言的起源 Objective-C为C语言添加了面向对象特性,是其超集。Objective-C使用动态绑定的消息结构,也就是说,在运行时才会检查对象类型。接收一...
  • caojengineer
  • caojengineer
  • 2015年06月07日 22:52
  • 1159

Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法(一)

1. 使用消息结构的语言,其运行时所应执行的代码由运行环境来决定;而使用函数调用的语言,则由编译器来决定。 如果范例代码调用的函数是多态的,则运行时根据虚函数表来查出应该执行哪个函数实现。 而采用...
  • mad2man
  • mad2man
  • 2014年03月25日 16:23
  • 3039

《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》--读书笔记

个人觉得这本书很不错!里面有很多很实用但以前没有太注意的地方,所以想纪录下来,当作自己的读书笔记吧。 熟悉Objective-C 1)Objective-C语言使用“消息结构”而不是“函数调用”。O...
  • weimeng809
  • weimeng809
  • 2016年01月11日 00:18
  • 571

神经病院Objective-C Runtime出院第三天——如何正确使用Runtime

前言 到了今天终于要"出院"了,要总结一下住院几天的收获,谈谈Runtime到底能为我们开发带来些什么好处。当然它也是把双刃剑,使用不当的话,也会成为开发路上的一个大坑。 目录 1.Runt...
  • qq_19697705
  • qq_19697705
  • 2016年10月21日 18:00
  • 289

iOS之方法变换(Method Swizzling)

C语言是静态语言,它的工作方式是通过函数调用,这样在编译时我们就已经确定程序如何运行的。而Objective-C是动态语言,它并非通过调用类的方法来执行功能,而是给对象发送消息,对象在接收到消息之后会...
  • LVXIANGAN
  • LVXIANGAN
  • 2017年07月26日 17:21
  • 295

Objective-C 2.0 基础要点归纳

本文的阅读基本条件: 具备C/C++基础知识,了解面向对象特征 阅读过《Objective-C 2.0 程序设计(第二版)》、《Objective-C 程序设计 第6版》或相关基础OC书籍 知识要点...
  • gujinjin2008
  • gujinjin2008
  • 2015年07月18日 20:11
  • 1590

methods和computed和watch的联系和区别

一.methods和computed computed是计算属性,methods是方法。 html: p>Reversed message: "{{ reversedMessage() }}...
  • xiaoninvhuang
  • xiaoninvhuang
  • 2017年04月23日 19:07
  • 3128

【Effective Objective-C 2.0读书笔记】第一章:熟悉Objective-C

Objective-C通过一套全新语法,在C语言基础上添加了面向对象特性。Objective-C的语法中频繁使用方括号,且不吝于写出极长的方法名。第1条:了解Objective-C语言的起源所有Obj...
  • freeWayWalker
  • freeWayWalker
  • 2015年07月21日 17:42
  • 1421

关于不同类的Method Swizzling

iOS的Method Swizzling是一个非常有意思的run time应用案例.用它可以实现AOP,也可以用来hook很多API,进行很多hack的操作相关资料很多,总体来讲就是调换两个Metho...
  • heiby
  • heiby
  • 2016年10月19日 15:55
  • 617
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Effective Objective-C 2.0: Method Swizzling to Debug Opaque Methods
举报原因:
原因补充:

(最多只允许输入30个字)