- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章iOS Runntime 动态添加类方法并调用-class_addMethod由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
上手开发 iOS 一段时间后,我发现并不能只着眼于完成需求,利用闲暇之余多研究其他的开发技巧,才能在有限时间内提升自己水平。当然,“其他开发技巧”这个命题对于任何一个开发领域都感觉不找边际,而对于我来说,尝试接触 objc/runtime 不失为是开始深入探索 iOS 开发的第一步.
刚了解 runtime 当然要从比较简单的 api 开始,今天就罗列整理一下 class_addMethod 的相关点:
首先从文档开始.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/**
* Adds a new method to a class with a given name and implementation.
*
* @param cls The class to which to add a method.
* @param name A selector that specifies the name of the method being added.
* @param imp A function which is the implementation of the new method. The function must take at least two arguments—self and _cmd.
* @param types An array of characters that describe the types of the arguments to the method.
*
* @return YES if the method was added successfully, otherwise NO
* (for example, the class already contains a method implementation with that name).
*
* @note class_addMethod will add an override of a superclass's implementation,
* but will not replace an existing implementation in this class.
* To change an existing implementation, use method_setImplementation.
*/
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp,
const
char
*types)
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
|
大意翻译一下,这个方法的作用是,给类添加一个新的方法和该方法的具体实现。分析一下这个方法需要的参数:
1
|
Class cls
|
cls 参数表示需要添加新方法的类.
1
|
SEL name
|
name 参数表示 selector 的方法名称,可以根据喜好自己进行命名.
1
|
IMP imp
|
imp 即 implementation ,表示由编译器生成的、指向实现方法的指针。也就是说,这个指针指向的方法就是我们要添加的方法.
1
|
const
char
*types
|
最后一个参数 *types 表示我们要添加的方法的返回值和参数.
简要介绍了 class_addMethod 中所需要的参数以及作用之后,我们就可以开始利用这个方法进行添加我们所需要的方法啦!在使用之前,我们首先要明确 Objective-C 作为一种动态语言,它会将部分代码放置在运行时的过程中执行,而不是编译时,所以在执行代码时,不仅仅需要的是编译器,也同时需要一个运行时环境(Runtime),为了满足一些需求,苹果开源了 Runtime Source 并提供了开放的 api 供开发者使用.
其次,我们需要知道在什么情况下需要调用 class_addMethod 这个方法。当项目中,需要继承某一个类(subclass),但是父类中并没有提供我需要的调用方法,而我又不清楚父类中某些方法的具体实现;或者,我需要为这个类写一个分类(category),在这个分类中,我可能需要替换/新增某个方法(注意:不推荐在分类中重写方法,而且也无法通过 super 来获取所谓父类的方法)。大致在这两种情况下,我们可以通过 class_addMethod 来实现我们想要的效果.
好了,说了这么多那么到底应该如何调用呢?如果不清楚使用方法,那么看说明书就是最好的方法。在 Apple 提供的文档中就有详细的使用方法(Objective-C Runtime Programming Guide - Dynamic Method Resolution),以下内容就以 myCar 这个类来详细说明一下具体的使用规则:
首先,既然要给某个类添加我们的方法,就应该继承或者给这个类写一个分类,这里我新建一个名为「myCar」的类,作为「Car」类的分类.
1
2
3
|
#
import
"Car+myCar.h"
@implementation
Car (myCar)
@end
|
我们知道,在 Objective-C 中,正常的调用方法是通过消息机制(message)来实现的,那么如果类中没有找到发送的消息方法,系统就会进入找不到该方法的处理流程中,如果在这个流程中,我们加入我们所需要的新方法,就能实现运行过程中的动态添加了。这个流程或者说机制,就是 Objective-C 的 Message Forwarding 。
这个机制中所涉及的方法主要有两个:
1
2
|
+ (BOOL)resolveInstanceMethod:(SEL)sel
+ (BOOL)resolveClassMethod:(SEL)sel
|
两个方法的唯一区别在于需要添加的是静态方法还是实例方法。我们就拿前者来说,既然要添加方法,我们就在「myCar」类中实现它,代码如下:
1
2
3
4
5
6
|
#
import
"Car+myCar.h"
void
startEngine(id self, SEL _cmd) {
NSLog(@
"my car starts the engine"
);
}
@implementation
Car (myCar)
@end
|
至此,我们实现了我们要添加的 startEngine 这个方法。这是一个 C 语言的函数,它至少包含了 self 和 _cmd 两个参数(self 代表着函数本身,而 _cmd 则是一个 SEL 数据体,包含了具体的方法地址)。如果要在这个方法中新增参数呢?见如下代码:
1
2
3
4
5
6
|
#
import
"Car+myCar.h"
void
startEngine(id self, SEL _cmd, NSString *brand) {
NSLog(@
"my %@ car starts the engine"
, brand);
}
@implementation
Car (myCar)
@end
|
只要在那两个必须的参数之后添加所需要的参数和类型就可以了,返回值同样道理,只要把方法名之前的 void 修改成我们想要的返回类型就可以,这里我们不需要返回值.
接着,我们重载 resolveInstanceMethod: 这个函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#
import
"Car+myCar.h"
#
import
<objc/runtime.h>
void
startEngine(id self, SEL _cmd, NSString *brand) {
NSLog(@
"my %@ car starts the engine"
, brand);
}
@implementation
Car (myCar)
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if
(sel ==
@selector
(drive)) {
class_addMethod([self
class
], sel, (IMP)startEngine,
"v@:@"
);
return
YES;
}
return
[
super
resolveInstanceMethod:sel];
}
@end
|
解释一下,这个函数在 runtime 环境下,如果没有找到该方法的实现的话就会执行。第一行判断的是传入的 SEL 名称是否匹配,接着调用 class_addMethod 方法,传入相应的参数。其中第三个参数传入的是我们添加的 C 语言函数的实现,也就是说,第三个参数的名称要和添加的具体函数名称一致。第四个参数指的是函数的返回值以及参数内容.
至于该类方法的返回值,在我测试的时候,无论这个 BOOL 值是多少,并不会影响我们的执行目标,一般返回 YES 即可.
如果觉得用 C 语言风格写新函数比较不适应,那么可以改写成以下的代码:
1
2
3
4
5
6
7
8
9
10
11
12
|
@implementation
Car (myCar)
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if
(sel ==
@selector
(drive)) {
class_addMethod([self
class
], sel, class_getMethodImplementation(self,
@selector
(startEngine:)),
"s@:@"
);
return
YES;
}
return
[
super
resolveInstanceMethod:sel];
}
- (
void
)startEngine:(NSString *)brand {
NSLog(@
"my %@ car starts the engine"
, brand);
}
@end
|
其中 class_getMethodImplementation 意思就是获取 SEL 的具体实现的指针.
然后创建一个新的类「DynamicSelector」,在这个新类中我们实现对「Car」的动态添加方法.
1
2
3
4
5
6
7
8
|
#
import
"DynamicSelector.h"
#
import
"Car+myCar.h"
@implementation
DynamicSelector
- (
void
)dynamicAddMethod {
Car *c = [[Car alloc] init];
[c performSelector:
@selector
(drive) withObject:@
"bmw"
];
}
@end
|
注意,在这里就不能使用 [self method:] 进行调用了,因为我们添加的方法是在运行时才执行,而编译器只负责编译时的方法检索,一旦对一个对象没有检索到它的 drive 方法,就会报错,所以这里我们使用 performSelector:withObject: 来进行调用,保存,运行.
1
2
|
2016
-
08
-
26
10
:
50
:
17.207
objc-runtime[
76618
:
3031897
] my bmw car starts the engine
Program ended with exit code:
0
|
打印结果符合我们期望实现的目标。如果需要返回值,方法类似.
以上所述是小编给大家介绍的iOS Runntime 动态添加类方法并调用-class_addMethod,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我网站的支持! 。
原文链接:http://www.cnblogs.com/chipmuck/archive/2016/08/26/5807190.html 。
最后此篇关于iOS Runntime 动态添加类方法并调用-class_addMethod的文章就讲到这里了,如果你想了解更多关于iOS Runntime 动态添加类方法并调用-class_addMethod的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
在 JavaScript 中,我们可以动态创建 元素并附加到 部分,以便为大量元素应用 CSS 规则。 这种方法的优点或缺点是什么? 如果它确实提供了与元素上的 javascript 迭代相比的性
我有这个代码 import "./HTTPMethod.dart"; import '../../DataModel/DataModel.dart'; mixin RouterMixin { HT
哪些 OLAP 工具支持动态、动态地创建维度或层次结构? 例如,层次结构将成员定义为:“前 5 名”、“前 6-10 名”、“其他”... 计算成员是通常的答案,我正在寻找不同的东西。计算器的问题。成
我正在 CakePHP 中创建一个“表单编辑器”。 该界面允许用户选择要应用于字段的验证,例如数字、电子邮件等 因此,我需要根据用户输入为模型动态创建验证。为此,我可以使用验证对象:https://b
这是一个场景: 我有一个Web服务,我们将其称为部署在tomcat(轴)上的StockQuoteService。通过此 Web 服务公开了 getStockQuote() 方法。 现在,我想构建一个
我正在尝试从服务器获取 JSON 响应并将其输出到控制台。 Future login() async { var response = await http.get( Uri.
我从另一个问题中得到了这段代码(感谢 chunhunghan)。我需要创建一个登录屏幕,并尝试根据服务器发回给我的响应来验证用户凭据,但是每次我尝试运行代码时,它都会给我“未处理的异常:Interna
当我在“Dart”主程序中运行它时,一切正常,并且我得到了一个与会者列表。但是,当我在我的 Flutter 应用程序中调用它时,出现错误: flutter:“List”类型不是“List>”类型的子类
本文实例为大家分享了js实现验证码动态干扰的具体代码,供大家参考,具体内容如下 效果一 效果二 代码一 ?
目前我正在为我的网站使用 No-Ip,我想使用 cloudflare 来抵御 ddos 和机器人程序。我注意到您需要一个用于 cloudflare 的域。我还搜索了网络,发现了一个叫做 cloud
有没有办法在 Excel VBA 中构建动态 if 语句?基本上我正在尝试创建一个参数化计算,用户将能够输入不同的变量,即 变量 1 “变量 2” “变量 3” 在这种情况下 变量 1 是单元格引用
大家好, 请查看上面的图片,我有两张 table 。在下面代码的第一个表中,我得到了这种格式。 但我想像 Table2 那样格式化,每个合并单元格中的行数是动态的,而且不一样。 有没有办法像table
如何根据我添加的 View 修改标题部分的高度?heightForHeaderInSection在 viewForHeaderInSection 之前被调用我不知道 View 大小,直到我创建它。 最
是否存在在运行时生成 AST/解析树的解析器?有点像一个库,它会接受一串 EBNF 语法或类似的东西并吐出数据结构? 我知道 antlr、jlex 和他们的同类。他们生成可以做到这一点的源代码。 (喜
我在持有汽车制造商的表格上有一个 MultipleChoiceField。我想将我的汽车数据库过滤到已检查的品牌,但这会导致问题。如何动态获取所有 Q(make=...) 语句? 我如何开始:['va
$end = preg_replace($pattern, $replacement, $str); 如何使替换字符串 $replacement 随 $str 中的每次匹配而变化?例如,我想用关联的图
我正在编写一个 VBA 程序,用于过滤表中的值。我试图使其成为一个适用于您提供的所有表格的通用程序。在我的程序中,我必须设置它正在过滤的表的范围:Set rng = dataSheet.Range("
我正在循环一个元素数组,并且我想使用给定的模板递归地显示该元素 然后在该模板内使用带有切换功能的按钮来显示/隐藏给定元素的Child的更深级别模板(Child也是一个元素) 这是我的模板
从客户端(html)发送表单,服务器端通过选择选项之一决定运行哪个函数。 const decideWho = (form) => { const choice = form.choice; c
我有一个具有以下属性的按钮: circle_normal.xml(在 res/drawable 中) circle.xml(在 res/drawable 中)
我是一名优秀的程序员,十分优秀!