***iOS开发中@selector的理解与应用

简介: @selector 是什么?   1一种类型 SEL2代表你要发送的消息(方法), 跟字符串有点像, 也可以互转.: NSSelectorFromString() / NSSelectorFromString() 3可以理解为类似函数指针的东西--是能让Objective-C动态调用方法的玩意.

@selector 是什么?

 

1一种类型 SEL
2代表你要发送的消息(方法), 跟字符串有点像, 也可以互转.: NSSelectorFromString() / NSSelectorFromString()

3可以理解为类似函数指针的东西--是能让Objective-C动态调用方法的玩意.--是 object-c 的动态后绑定技术 可以通过字符串 访问的函数指针
4其实就是消息响应函数---选一个消息响应的函数地址给你的action
5@selector(function_name) 即取得一个function的id

objc_msgxxx 系列函数是全局的
performSelector 是NSObject成员方法,ms效果差不多
 
 
Objective-C的动态后绑定技术:可以通过字符串访问的函数指针
 
typedef obj_handler * SEL;
 

 

SEL 类成员方法的指针

可以理解 @selector()就是取类方法的编号,他的行为基本可以等同C语言的中函数指针,只不过C语言中,可以把函数名直接赋给一个函数指针,而Object-C的类不能直接应用函数指针,这样只能做一个@selector语法来取.

它的结果是一个SEL类型。这个类型本质是类方法的编号(函数地址)

C/C++函数指针

 

int test(int val)

{
return val+1;

}

int (* c_func)(int val); //定义一个函数指针变量c_func = add ; //把函数addr地址直接赋给c_func

 

object-c的选择器,

@interface foo
-(int)add:int val;

@end

SEL class_func ; //定义一个类方法指针class_func = @selector(add:int);

注意1、@selector是查找当前类(含子类)的方法。

举例:

父类.h文件

 

[plain]  view plain copy
 
  1. #import <Foundation/Foundation.h>  
  2.   
  3. @interface SelectorDemo : NSObject  
  4. {  
  5.     SEL _methodTest;  
  6. }  
  7.   
  8. @property (nonatomic,assign) SEL methodTest;//这里声明为属性方便在于外部传入。  
  9.   
  10. -(void)TestParentMethod;  
  11.   
  12. -(void)TestSubMethod;  
  13.   
  14.   
  15. @end  
.m文件

 

 

[plain]  view plain copy
 
  1. #import "SelectorDemo.h"  
  2.   
  3. @implementation SelectorDemo  
  4.   
  5. @synthesize methodTest = _methodTest;  
  6.   
  7. -(void)parentMethod  
  8. {  
  9.     NSLog(@"parent method Call Success!");  
  10. }  
  11.   
  12. -(void)TestParentMethod  
  13. {  
  14.     if (_methodTest)  
  15.     {  
  16.         [self performSelector:_methodTest withObject:nil];  
  17.     }  
  18. }  
  19.   
  20. -(void)TestSubMethod  
  21. {  
  22.     if (_methodTest)  
  23.     {  
  24.         [self performSelector:_methodTest withObject:nil];  
  25.     }  
  26. }  
  27.   
  28. @end  

子类:

 

.h文件

 

[plain]  view plain copy
 
  1. #import <Foundation/Foundation.h>  
  2. #import "SelectorDemo.h"  
  3.   
  4.   
  5. @interface SelectorSub : SelectorDemo  
  6.   
  7.   
  8. @end  
.m文件

 

 

[plain]  view plain copy
 
  1. #import "SelectorSub.h"  
  2.   
  3.   
  4. @implementation SelectorSub  
  5.   
  6. -(void)SubMethod  
  7. {  
  8.     NSLog(@"Sub method Call Success!");  
  9. }  
  10.   
  11.   
  12. @end  

进行测试调用。
[plain]  view plain copy
 
  1. SelectorSub *ss = [[SelectorSub alloc]init];  
  2.     ss.methodTest = @selector(parentMethod);  
  3.     [ss TestParentMethod];  
  4.     ss.methodTest = @selector(SubMethod);  
  5.     [ss TestParentMethod];  
  6.     [ss release];  

 

ss.methodTest = @selector(parentMethod); 这句在运行期时,会寻找到父类中的方法进行调用。

ss.methodTest = @selector(SubMethod);//这句就在运行期时,会先寻找父类,如果父类没有,则寻找子类。

 

如果这里将ss.methodTest = @selector(test); 其中test即不是ss父类,也不是ss本身,也非SS子类,哪么这个时候在使用

[self performSelector:_methodTest withObject:nil];就会出现地址寻找出错 。

 

 

[cpp]  view plain copy
 
  1. 下面的其实是很好的解释为什么必须是自身类或者子类。  
[cpp]  view plain copy
 
  1. [friend performSelector:@selector(gossipAbout:) withObject:aNeighbor];  
等价于:
[cpp]  view plain copy
 
  1. [friend gossipAbout:aNeighbor];  
[cpp]  view plain copy
 
  1.   
[cpp]  view plain copy
 
  1. 通过这个原理,当把属性设置为SEL类型时,如果回调机制使用的不是SEL声明的类或子类。想实现其它类的回调,必须传入其它类的上下文句柄。  
[cpp]  view plain copy
 
  1. 举例:  
[cpp]  view plain copy
 
  1. 上面的SelectorDemo 类修改为:  

 

 

[plain]  view plain copy
 
  1. #import <Foundation/Foundation.h>  
  2.   
  3. @interface SelectorDemo : NSObject  
  4. {  
  5.     SEL _methodTest;  
  6.     id _handle;  
  7. }  
  8.   
  9. @property (nonatomic,assign) SEL methodTest;  
  10. @property (nonatomic,retain) id handle;        //添加其它类的实例句柄属性。  
  11. -(void)TestParentMethod;  
  12. -(void)TestSubMethod;  
  13.   
  14. @end  
[plain]  view plain copy
 
  1. #import "SelectorDemo.h"  
  2.   
  3. @implementation SelectorDemo  
  4.   
  5. @synthesize methodTest = _methodTest;  
  6. @synthesize handle = _handle;  
  7.   
  8. -(void)parentMethod  
  9. {  
  10.     NSLog(@"parent method Call Success!");  
  11. }  
  12.   
  13. -(void)TestParentMethod  
  14. {  
  15.     if (_methodTest)  
  16.     {  
  17.         [_handle performSelector:_methodTest withObject:nil];//这里面原来self属为相应的实例句柄  
  18.     }  
  19. }  
  20.   
  21. -(void)TestSubMethod  
  22. {  
  23.     if (_methodTest)  
  24.     {  
  25.         [_handle performSelector:_methodTest withObject:nil];  
  26.     }  
  27. }  
  28.   
  29. @end  

到这里我想熟悉IOS,target-action模式的,都清晰了。

 

Target-Action设计模式

 

在处理用户-接口控件方面,AppKit充分发挥了在运行时改变接收者和消息的能力。

NSControl对象是一个图形设备,可以用来向应用程序发送指令,。大多实现了现实世界中的控制装置,例如button、switch、knob、text field、dial、menu item等。在软件中,这些设备处于用户和和应用程序之间。它们解释来自硬件设备,如键盘和鼠标的事件,并将它们翻译成应用程序特定的指令。例如,名为“Find”的按钮将会把鼠标点击事件翻译成开始搜索的应用程序指令。

AppKit为创建控件设备定义了模板,并定义了一些自己的现成设备。例如,NSButtonCell类定义了一个对象,可以指派给一个NSMatrix实例,并初始化它的大小、名称、图片、字体和键盘快捷键。当用户点击按钮(或使用键盘快捷键)时,NSButtonCell对象发送消息,指示应用程序工作。为此,NSButtonCell对象不仅要初始化图像、大小和名称,还要确定消息要发往何方和发给谁。相应地,NSButtonCell实例可以为一个action消息(它将在自己发送的消息中使用的对象选择器)和一个target(接收该消息的对象)进行初始化。

 

[cpp]  view plain copy
 
  1. [myButtonCell setAction:@selector(reapTheWind:)];  
  2. [myButtonCell setTarget:anObject];  
当用户点击了相应的按钮,该按钮单元将使用NSObject协议方法performSelector:withObject:发送消息。所有action消息带有单独一个参数,既发送该消息的控件设备的id。

 

如果Objective-C不允许改变消息,所有的NSButtonCell对象将不得不发送相同的消息,方法的名字将在NSButtonCell源代码中写死。与简单的实现将用户action转换为action消息的机制不同,按钮单元和其他控件不得不限制消息的内容。受限的消息会使很多对象难以响应多于一个的按钮单元。要么每个按钮有一个target,要么target对象能发现消息来自于那个按钮,并做相应处理。每次在重新布局用户接口时,你也必须实现响应action消息的方法。动态消息的缺乏将会带来不必要的麻烦,但Objective-C很好地避免了这一点。


从前面的例子可以得知如果SEL不是自身的方法,在调用时就会出错,引起CRASH,哪么如何避免消息传递引起的错误。见下文章:

 

避免消息错误

 

如果一个对象接收了一条消息去执行不归它管的方法,就会产生错误结果。这和调用一个不存在的函数是同一类错误。但是,因为消息发生在运行时,错误只有在程序执行后才会出现。

当消息选择器是常数并且接收对象类已知时,处理这种错误相对容易。在写程序时,你可以确保接收者能够响应。如果接收者时静态类型,编译器将替你完成该测试。

但是,如果消息选择器或接收者是变化的,那么只能在运行时进行相关测试。NSObject类中定义的respondsToSelector:方法可以测试一个接收者是否能够响应某条消息。它将方法选择器作为参数并返回接收者是否已经访问了与选择器相匹配的一个方法:

 

[cpp]  view plain copy
 
  1. if ( [anObject respondsToSelector:@selector(setOrigin::)] )  
  2.     [anObject setOrigin:0.0 :0.0];  
  3. else  
  4.     fprintf(stderr, "%s can’t be placed\n",  
  5.         [NSStringFromClass([anObject class]) UTF8String]);  
当你向一个你在编译时无法控制的对象发送消息时,respondsToSelector:运行时测试非常重要。例如,如果你写了一段代码向一个对象发送消息,而这个对象是一个他人可以设定值的变量,那么你就要确保接收者实现了响应该消息的方法。

 

注意:一个对象在收到不是自己负责直接响应的消息时可以转发该消息给其他对象。这种情况下,从调用者的角度来看,对象直接处理了消息,尽管该对象是通过转发消息给其他对象来处理的。


 

注意2、查找类方法时,除了方法名,方法参数也查询条件之一.

这个主要是多个参数时需要注意,如:

[cpp]  view plain copy
 
  1. SEL setWidthHeight;  
  2. setWidthHeight = @selector(setWidth:height:);  

 

 

注意3、可以用字符串来找方法 SEL 变量名 = NSSelectorFromString(方法名字的字符串);

注意4、 可以运行中用SEL变量反向查出方法名字字符串,如:NSString *method = NSStringFromSelector(setWidthHeight);

注意5、SEL 查找的方法不支持类方法(即静态方法,在C++中带static关键字的,在OBJECT-C中即方法前带+号的,DELPHI中为class function)。

 

 


 

iOS开发 关于SEL的简单总结

SEL就是对方法的一种包装。包装的SEL类型数据它对应相应的方法地址,找到方法地址就可以调用方法。在内存中每个类的方法都存储在类对象中,每个方法都有一个与之对应的SEL类型的数据,根据一个SEL数据就可以找到对应的方法地址,进而调用方法

  1. @interface Person : NSObject 
  2.  
  3. + (void)test1; 
  4. - (void)test2; 
  5. @end 
  6.  
  7.     // 根据.h文件中定义的Person类和方法 执行完这行代码 在内存中如下 
  8.     Person *person = [[Person alloc] init]; 

SEL就是对方法的一种包装。包装的SEL类型数据它对应相应的方法地址,找到方法地址就可以调用方法

1.方法的存储位置

  • 在内存中每个类的方法都存储在类对象中
  • 每个方法都有一个与之对应的SEL类型的数据
  • 根据一个SEL数据就可以找到对应的方法地址,进而调用方法
  • SEL类型的定义:  typedef struct objc_selector *SEL

2.SEL对象的创建

  1. SEL s1 = @selector(test1); // 将test1方法包装成SEL对象  
  2. SEL s2 = NSSelectorFromString(@"test1"); // 将一个字符串方法转换成为SEL对象 

3.SEL对象的其他用法

  1. // 将SEL对象转换为NSString对象 
  2. NSString *str = NSStringFromSelector(@selector(test)); 
  3.  
  4.  Person *p = [Person new]; 
  5.  
  6. // 调用对象p的test方法 
  7. [p performSelector:@selector(test)];
  1. /******************************* Person.h文件 **********************************/ 
  2.  
  3. #import <Foundation/Foundation.h> 
  4.  
  5. @interface Person : NSObject 
  6.  
  7. - (void)test1; 
  8.  
  9. - (void)test2:(NSString *)str; 
  10.  
  11. @end 
  12.  
  13. /******************************* Person.m文件 **********************************/ 
  14.  
  15. #import "Person.h" 
  16.  
  17. @implementation Person 
  18.  
  19. - (void)test1 
  20.     NSLog(@"无参数的对象方法"); 
  21.  
  22. - (void)test2:(NSString *)str 
  23.     NSLog(@"带有参数的方法%@",str); 
  24. @end 
  25.  
  26. /******************************* main.m文件 **********************************/ 
  27.  
  28. #import "Person.h" 
  29. #import <Foundation/Foundation.h> 
  30.  
  31. /* 
  32.  调用方法有两种方式: 
  33.  1.直接通过方法名来调用 
  34.  2.间接的通过SEL数据来调用 
  35.  */ 
  36.  
  37. int main(int argc, const char * argv[]) 
  38.     Person *person = [[Person alloc] init]; 
  39.      
  40.     // 1.执行这行代码的时候会把test2包装成SEL类型的数据 
  41.     // 2.然后根据SEL数据找到对应的方法地址(比较耗性能但系统会有缓存) 
  42.     // 3.在根据方法地址调用对应的方法 
  43.      
  44.     [person test1]; 
  45.      
  46.     // 将方法直接包装成SEL数据类型来调用 withObject:传入的参数 
  47.     [person performSelector:@selector(test1)]; 
  48.     [person performSelector:@selector(test2:) withObject:@"传入参数"]; 
  49.      
  50.     return 0; 

 

如何联系我:【万里虎】www.bravetiger.cn 【QQ】3396726884 (咨询问题100元起,帮助解决问题500元起) 【博客】http://www.cnblogs.com/kenshinobiy/
目录
相关文章
|
1月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
160 4
|
1月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
10天前
|
iOS开发 开发者 MacOS
深入探索iOS开发中的SwiftUI框架
【10月更文挑战第21天】 本文将带领读者深入了解Apple最新推出的SwiftUI框架,这一革命性的用户界面构建工具为iOS开发者提供了一种声明式、高效且直观的方式来创建复杂的用户界面。通过分析SwiftUI的核心概念、主要特性以及在实际项目中的应用示例,我们将展示如何利用SwiftUI简化UI代码,提高开发效率,并保持应用程序的高性能和响应性。无论你是iOS开发的新手还是有经验的开发者,本文都将为你提供宝贵的见解和实用的指导。
93 66
|
20天前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
|
24天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
26天前
|
存储 前端开发 Swift
探索iOS开发:从新手到专家的旅程
本文将带您领略iOS开发的奇妙之旅,从基础概念的理解到高级技巧的掌握,逐步深入iOS的世界。文章不仅分享技术知识,还鼓励读者在编程之路上保持好奇心和创新精神,实现个人成长与技术突破。
|
29天前
|
安全 IDE Swift
探索iOS开发之旅:从初学者到专家
在这篇文章中,我们将一起踏上iOS开发的旅程,从基础概念的理解到深入掌握核心技术。无论你是编程新手还是希望提升技能的开发者,这里都有你需要的指南和启示。我们将通过实际案例和代码示例,展示如何构建一个功能齐全的iOS应用。准备好了吗?让我们一起开始吧!
|
1月前
|
安全 Swift iOS开发
Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法
本文深入探讨了 Swift 与 UIKit 在 iOS 应用界面开发中的关键技术和实践方法。Swift 以其简洁、高效和类型安全的特点,结合 UIKit 丰富的组件和功能,为开发者提供了强大的工具。文章从 Swift 的语法优势、类型安全、编程模型以及与 UIKit 的集成,到 UIKit 的主要组件和功能,再到构建界面的实践技巧和实际案例分析,全面介绍了如何利用这些技术创建高质量的用户界面。
33 2
|
1月前
|
vr&ar Android开发 iOS开发
安卓与iOS开发中的用户界面设计原则
【10月更文挑战第41天】探索移动应用开发的精髓,本文将深入分析安卓和iOS平台上用户界面设计的核心原则。通过比较两大操作系统的设计哲学,我们将揭示如何打造直观、易用且美观的应用程序界面。无论你是初学者还是资深开发者,这篇文章都将为你提供宝贵的见解和实用的技巧,帮助你在竞争激烈的应用市场中脱颖而出。