Replacing Objective-C Methods at Runtime

本文演示了如何使用Objective-C动态替换类的方法,并通过创建控制器类实例化对象并调用不同方法,展示了其实现过程及运行效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Replacing Objective-C Methods at Runtime


One of the more interesting features of Objective-C is the ability to replace entire methods at runtime, and it turns out that it's actually pretty easy. To do this, we need to dig into libobjc, which has headers in /usr/include/objc.


In this case, we specifically want to look at objc-class.h, which is where the objc_method struct is defined:

typedef struct objc_method *Method;

struct objc_method {
  SEL method_name;
  char *method_types;
  IMP method_imp;
};


This is the template for all Objective-C methods. This may look pretty alien, but there's not much to it. The first field is the selector, which is basically a method name. The second field is a C string which stores type encodings (don't worry about it for now).

The last field is the IMP, or implementation. An IMP is a pointer to the actual function that is called when this Objective-C method is activated. This field is what we'll use to replace the method. First, here's our extremely simple class, with one method:

@interface MiniObject : NSObject { }
- (void)generateFrogs;
@end

@implementation MiniObject
- (void)generateFrogs
{
  NSLog(@"Generating Frogs...");
}
@end


So if this class has its way, we'll be generating frogs all day and night. As omnipotent programmers with a dynamic runtime, though, we have other plans. Here's our Controller class. Remember to import objc-class.h!

#import "Controller.h"
#import <objc/objc-class.h>

@implementation Controller

- (IBAction)click:(id)sender
{
  MiniObject * mini = [[MiniObject alloc] init];
  [mini generateFrogs];
  
  SEL frogSel = @selector(generateFrogs);
  Method frogMethod = class_getInstanceMethod([MiniObject class], frogSel);
  
  SEL lizSel = @selector(generateLizards);
  Method lizardMethod = class_getInstanceMethod([self class], lizSel);
  
  frogMethod->method_imp = lizardMethod->method_imp;
  [mini generateFrogs];
  
  [mini release];
}

- (void) generateLizards
{
  NSLog(@"Generating Lizards...");
}

@end


So the class has two methods, a "click" action method and generateLizards. First, we create a MiniObject and call generateFrogs (the original method):

MiniObject * mini = [[MiniObject alloc] init];
[mini generateFrogs];


Next, we get the Method structs for both generateFrogs and generateLizards (Method is a pointer to an objc_method struct):

SEL frogSel = @selector(generateFrogs);
Method frogMethod = class_getInstanceMethod([MiniObject class], frogSel);

SEL lizSel = @selector(generateLizards);
Method lizardMethod = class_getInstanceMethod([self class], lizSel);


Notice how we use [self class] in the last line because we want to replace the original method in MiniObject with the one from Controller.

Finally, we use standard C syntax to replace the value of the method_imp field in frogMethod with the method_imp from lizardMethod. Then, we send the generateFrogs message again:

frogMethod->method_imp = lizardMethod->method_imp;
[mini generateFrogs];


The run log looks like this:

Generating Frogs...
Generating Lizards...


The code shows that we never actually called generateLizards directly. Instead, we replaced the implementation of generateFrogs with that of generateLizards. Pretty fancy.


From: http://theocacao.com/document.page/266

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值