@dynamic详细介绍

转自:http://blog.youkuaiyun.com/bsplover/article/details/7918268

Objective-C 2.0 中增加了@dynamic 指令,表示变量对应的属性访问器方法,是动态实现的,你需要在NSObject 中继承而来的+(BOOL) resolveInstanceMethod:(SEL) sel 方法中指定动态实现的方法或者函数。

Person.h:

[cpp]  view plain copy
  1. @interface Person : NSObject{  
  2. NSString *name;  
  3. float weight;  
  4. }  
  5. -(Person*) initWithWeight: (int) weight;  
  6. @property (retain,readwrite) NSString* name;  
  7. @property (readonly)float weight;  
  8. @property float height;  
  9. -(void) print: (NSString*) str;  
  10. @end  

Person.m:

[cpp]  view plain copy
  1. void dynamicMethod(id self,SEL _cmd,float w){  
  2. printf("dynamicMethod-%s\n",[NSStringFromSelector(_cmd)  
  3. cStringUsingEncoding:NSUTF8StringEncoding]);  
  4. printf("%f\n",w);  
  5. }  
  6. @implementation Person  
  7. @synthesize name;  
  8. @synthesize weight;  
  9. @dynamic height;  
  10. -(Person*) initWithWeight: (int) w{  
  11. self=[super init];  
  12. if (self) {  
  13. weight=w;  
  14. }  
  15. return self;  
  16. }  
  17. -(void) print: (NSString*) str{  
  18. NSLog(@"%@%@",str,name);  
  19. }  
  20. +(BOOL) resolveInstanceMethod: (SEL) sel{  
  21. NSString *methodName=NSStringFromSelector(sel);  
  22. BOOL result=NO;  
  23. //看看是不是我们要动态实现的方法名称  
  24. if ([methodName isEqualToString:@"setHeight:"]) {  
  25. class_addMethod([self class], sel, (IMP) dynamicMethod,  
  26. "v@:f");  
  27. result=YES;  
  28. }  
  29. return result;  
  30. }  
  31. -(void) dealloc{  
  32. [self setName:nil];  
  33. [super dealloc];  
  34. }  
  35. @end  

这里我们对于接口中的height在实现类中使用了@dynamic指令,紧接着,你需要指定一个函数或者其他类的方法作为height的setter、getter方法的运行时实现。为了简单,我们指定了Person.m中定义的函数(注意这是C语言的函数,不是Objective-C的方法)dynamicMethod
作为height的setter方法的运行时实现。被指定为动态实现的方法的dynamicMethod的参数有如下的要求:
A.第一个、第二个参数必须是id、SEL;
B.第三个参数开始,你可以按照原方法(例如:setHeight:(float))的参数定义。
再接下来,你需要覆盖NSObject 的类方法resolveInstanceMethod,这个方法会把需要动态实现的方法(setHeight:)的选择器传递进来,我们判断一下是否是需要动态实现的选择器,如果是就把处理权转交给dynamicMethod。如何转交呢?这里我们就要用到运行时函数class_addMethod(Class,SEL,IMP,char[])。
运行时函数位于objc/runtime.h,正如名字一样,这里面都是C 语言的函数。按照这些函数的功能的不同,主要分为如下几类:操作类型、操作对象、操作协议等。大多数的函数都可以通过名字看出是什么意思,例如:class_addProtocol 动态的为一个类型在运行时增加协议、objc_getProtocol 把一个字符串转换为协议等。具体这些运行时函数都是做什么用的,你可以参看Apple 官方页面:
http://developer.apple.com/library/ios/documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html#//apple_ref/doc/uid/TP40001418
言归正传,我们来解释一下这里需要用到的class_addmethod 方法,这个方法有四个参数,Class 表示你要为哪个类型增加方法,SEL 参数表示你要增加的方法的选择器,IMP 表示你要添加的方法的运行时的具体实现的函数指针。其实在这里你能够看出SEL 并不能在运行时找到真正要调用的方法,IMP 才可以真正的找到实现方法的。
在讲解第四个参数char[]之前,我们先看一下第一篇文档中提到的@encode 指令,在把任意非Objective-C 对象类型封装为NSValue 类型的时候使用到了@encode 指令,但当时我们没有详细说明这个指令的含义。实际上@encode()可以接受任何类型,Objective-C 中用这个指令做类型编码,它可以把任何一个类型转换为字符串,譬如:void 类型被编码之后为v,对象类型为@,SEL 类型为:等,具体的你可以参看Apple 官方页面关于Type Encoding 的描述:
http://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW
现在我们来正式的看以下第四个参数v@:f 的含义,它描述了IMP 指向的函数的描述信息,按照@encode 指令编译之后的字符说明,第一个字符v 表示返回值为void,剩余的字符为dynamicMethod 函数的参数描述,@表示第一个参数id,:自然就是第二个参数SEL,f 就是第三个参数float。由于前面说过动态方法的实现的前两个参数必须是id、SEL,所以第四个参数中的字符串的第二、三个字符一定是@:。我们看到resolveInstanceMethod 方法的返回值为BOOL,也就是这个方法返回YES 表示找到了动态方法的具体实现,否则就表示没有在运行时找到真实的实现,程序就汇报错。
经过了上面的处理,Objective-C 的运行时只要发现你调用了@dynamic 标注的属性的setter、getter 方法,就会自动到resolveInstanceMethod 里去寻找真实的实现。这也就是说你在main.m 中调用peson.height 的时候,实际上dynamicMethod 函数被调用了。实际上除了@dynamic 标注的属性之外,如果你调用了类型中不存在的方法,也会被
resolveInstanceMethod 或者resolveClassMethod 截获,但由于你没有处理,所以会报告不能识别的消息的错误。你可能在感叹一个@dynamic 指令用起来真是麻烦,我也是研究了半天Apple 官方的晦涩的鸟语才搞明白的。不过好在一般Objective-C 的运行时编程用到的并不多,除非你想设计一个动态化的功能,譬如:从网络下载一个升级包,不需要退出原有的程序,就可以动态的替换掉旧的功能等类似的需求。

<think>嗯,用户询问dynamicdebug的详细介绍和使用方法。从引用内容来看,这是一个Linux内核的调试工具,允许在运行时动态控制调试信息输出。首先需要理清dynamicdebug的核心机制。它通过内核编译时添加的调试点实现,这些调试点在代码中以特殊的宏标记。用户可以通过操作sysfs接口动态启用或禁用这些标记的打印输出。用户可能是个开发人员,需要调试内核模块或驱动程序。ta明确提到需要详细信息和示例,应该是有实际使用场景,不是单纯的理论了解。因此要侧重实用性和可操作性。关于基本用法,引用[3][4]提到control文件的位置在/sys/kernel/debug/dynamic_debug/control,这是关键接口。用户可以通过echo命令向这个文件写入控制指令。格式如"filexxx.c+p"表示启用某个文件的调试输出。动态启用的特性很重要。引用[5]展示了启用具体函数日志的例子,这样可以避免重启系统或重新加载模块。对用户来说可能最关心这个,因为传统调试需要重新编译内核非常麻烦。选择器语法需要详细说明。引用[4]给出了关键参数:+p启用,-p禁用,还可以组合f/l/m等修饰符。用户可能想知道如何精确控制特定位置的输出,这部分应该用示例展示。引用[2]提到启动过程的调试是个特殊场景。需要说明early_boot阶段可能无法使用dynamicdebug,用户如果遇到这个问题需要提前知道限制。实际应用案例要强调效率提升,如引用[1]所说"提高调试效率"。可以补充说明:在排查复杂问题时,能动态聚焦到特定模块的输出,避免被无关日志干扰。最后要注意安全提示:操作control文件需要root权限,错误指令可能导致输出泛滥。应该像引用[5]那样给出具体的关闭日志示例,避免用户把自己淹没在日志里。</think> 以下是关于 Linux 内核 `dynamic_debug` 机制的详细介绍、使用方法和示例,结合站内引用信息整理: --- ### **1. dynamic_debug 介绍** `dynamic_debug` 是 Linux 内核提供的一种**动态调试**机制,允许在系统运行时通过文件系统接口灵活启停内核代码中的调试信息输出[^1]。优势包括: - **无需重启**:动态开关调试信息; - **精细控制**:支持按文件/函数/行号等粒度控制输出; - **零运行时开销**:未启用的调试语句在编译时会被优化移除。 --- ### **2. 核心机制** - **依赖编译选项**:内核需启用 `CONFIG_DYNAMIC_DEBUG` 和 `CONFIG_DEBUG_FS`; - **调试点标记**:内核代码中使用 `pr_debug()` 或 `dev_dbg()` 等宏标记调试语句; - **控制接口**:通过 `/sys/kernel/debug/dynamic_debug/control` 文件控制输出[^3]。 --- ### **3. 使用流程** #### **步骤 1:查看可用的调试点** ```bash cat /sys/kernel/debug/dynamic_debug/control ``` 输出示例: ``` kernel/sched/core.c:1493 [sched]sched_cpu_active =_ "Dynamic debug: sched_core active\012" fs/nfsd/nfsproc.c:1053 [nfsd]svc_process +p ... ``` 每行包含:**文件名、行号、模块名、函数名**和当前状态(`= ` 表示禁用,`=p` 表示启用)[^3]。 #### **步骤 2:启用调试输出** 通过 `echo` 向 `control` 文件写入指令: ```bash # 启用指定文件的调试信息 echo 'file xxx.c +p' > /sys/kernel/debug/dynamic_debug/control # 启用指定函数的调试信息 echo 'func svc_process +p' > /sys/kernel/debug/dynamic_debug/control ``` **关键参数**: - `+p`:启用调试输出; - `-p`:关闭调试输出[^4]; - 修饰符(可组合):`f`(函数名)、`l`(行号)、`m`(模块名)、`t`(线程ID)[^4]。 #### **步骤 3:关闭调试输出** ```bash # 关闭某文件所有调试 echo 'file xxx.c -p' > /sys/kernel/debug/dynamic_debug/control # 关闭所有动态调试信息 echo "module * -p" > /sys/kernel/debug/dynamic_debug/control ``` --- ### **4. 实用示例** #### **场景:调试 NFSD 的请求处理** ```bash # 查看 nfsd 相关调试点 grep "nfsd" /sys/kernel/debug/dynamic_debug/control # 启用 nfsproc.c 中 svc_process() 的调试输出 echo 'func svc_process +p' > /sys/kernel/debug/dynamic_debug/control # 输出结果将包含函数名、行号、模块名 echo 'format +pmtf' > /sys/kernel/debug/dynamic_debug/control # 完成后关闭 echo 'func svc_process -p' > /sys/kernel/debug/dynamic_debug/control ``` **输出日志示例**: ``` [ nfsd] svc_process:1053 [nfsd] thread=12: Received NFS request ``` #### **启动阶段调试(需谨慎)** 若需在**系统启动时**启用动态调试,可将命令加入 `initramfs` 或内核参数 `dyndbg=`[^2]: ```bash # 内核启动参数 dyndbg="file drivers/usb/core/hub.c +p" ``` --- ### **5. 注意事项** 1. **权限要求**:需 `root` 权限操作 `/sys/kernel/debug/`; 2. **作用域规则**:指令按 **“最后匹配生效”** 原则执行; 3. **生产环境慎用**:过度启用日志可能导致性能下降。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值