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

@selector 是什么?

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

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

objc_msgxxx 系列函数是全局的
performSelector 是NSObject成员方法,ms效果差不多

Objective-C的动态后绑定技术:可以通过字符串访问的函数指针

typedef obj_handler * SEL;


IOS SEL (@selector) 原理及使用总结(一)

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 plaincopy

  1. #import <Foundation/Foundation.h>
  2. @interface SelectorDemo : NSObject
  3. {
  4. SEL _methodTest;
  5. }
  6. @property (nonatomic,assign) SEL methodTest;//这里声明为属性方便在于外部传入。
  7. -(void)TestParentMethod;
  8. -(void)TestSubMethod;
  9. @end

.m文件

[plain] view plaincopy

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

子类:

.h文件

[plain] view plaincopy

  1. #import <Foundation/Foundation.h>
  2. #import "SelectorDemo.h"
  3. @interface SelectorSub : SelectorDemo
  4. @end

.m文件

[plain] view plaincopy

  1. #import "SelectorSub.h"
  2. @implementation SelectorSub
  3. -(void)SubMethod
  4. {
  5. NSLog(@"Sub method Call Success!");
  6. }
  7. @end

进行测试调用。

[plain] view plaincopy

  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 plaincopy

  1. 下面的其实是很好的解释为什么必须是自身类或者子类。

[cpp] view plaincopy

  1. [friend performSelector:@selector(gossipAbout:) withObject:aNeighbor];

等价于:

[cpp] view plaincopy

  1. [friend gossipAbout:aNeighbor];

[cpp] view plaincopy

[cpp] view plaincopy

  1. 通过这个原理,当把属性设置为SEL类型时,如果回调机制使用的不是SEL声明的类或子类。想实现其它类的回调,必须传入其它类的上下文句柄。

[cpp] view plaincopy

  1. 举例:

[cpp] view plaincopy

  1. 上面的SelectorDemo 类修改为:

[plain] view plaincopy

  1. #import <Foundation/Foundation.h>
  2. @interface SelectorDemo : NSObject
  3. {
  4. SEL _methodTest;
  5. id _handle;
  6. }
  7. @property (nonatomic,assign) SEL methodTest;
  8. @property (nonatomic,retain) id handle;        //添加其它类的实例句柄属性。
  9. -(void)TestParentMethod;
  10. -(void)TestSubMethod;
  11. @end

[plain] view plaincopy

  1. #import "SelectorDemo.h"
  2. @implementation SelectorDemo
  3. @synthesize methodTest = _methodTest;
  4. @synthesize handle = _handle;
  5. -(void)parentMethod
  6. {
  7. NSLog(@"parent method Call Success!");
  8. }
  9. -(void)TestParentMethod
  10. {
  11. if (_methodTest)
  12. {
  13. [_handle performSelector:_methodTest withObject:nil];//这里面原来self属为相应的实例句柄
  14. }
  15. }
  16. -(void)TestSubMethod
  17. {
  18. if (_methodTest)
  19. {
  20. [_handle performSelector:_methodTest withObject:nil];
  21. }
  22. }
  23. @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 plaincopy

  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 plaincopy

  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 plaincopy

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

***iOS开发中@selector的理解与应用的相关文章

iOS开发中Block的理解与使用

简介 我们可以把Block当做Objective-C的匿名函数.Block允许开发者在两个对象之间将任意的语句当做数据进行传递,往往这要比引用定义在别处的函数直观.另外,block的实现具有封闭性(closure),而又能够很容易获取上下文的相关状态信息. block是代码块,其本质和变量类似.不同的是代码块存储的数据是一个函数体.使用Block,就可以像其他标准函数一样,传入参数,并得到返回值. block的格式: a:Block的返回值类型,可以为空(void); b:Block对象名称,可

iOS开发中GCD在多线程方面的理解

GCD为Grand Central Dispatch的缩写. Grand Central Dispatch (GCD)是Apple开发的一个多核编程的较新的解决方法.在Mac OS X 10.6雪豹中首次推出,并在最近引入到了iOS4.0. GCD是一个替代诸如NSThread等技术的很高效和强大的技术.GCD完全可以处理诸如数据锁定和资源泄漏等复杂的异步编程问题. GCD可以完成很多事情,但是这里仅关注在iOS应用中实现多线程所需的一些基础知识. 在开始之前,需要理解是要提供给GCD队列的是代

[转载]对iOS开发中内存管理的一点总结与理解

对iOS开发中内存管理的一点总结与理解 做iOS开发也已经有两年的时间,觉得有必要沉下心去整理一些东西了,特别是一些基础的东西,虽然现在有ARC这种东西,但是我一直也没有去用过,个人觉得对内存操作的理解是衡量一个程序员成熟与否的一个标准.好了,闲话不说,下面进入正题. 众所周知,ObjectiveC的内存管理引用的一种叫做“引用计数“ (Reference Count)的操作方式,简单的理解就是系统为每一个创建出来的对象,(这里要注意,只是对象,NSObject的子类,基本类型没有‘引用计数’)

全面理解iOS开发中的Scroll View

转自:http://mobile.51cto.com/hot-430409.htm 可能你很难相信,UIScrollView和一个标准的UIView差异并不大,scroll view确实会多一些方法,但这些方法只是UIView一些属性的表面而已.因此,要想弄懂UIScrollView是怎么工作之前,你需要了解 UIView,特别是视图渲染过程的两步. 光栅化和组合 渲染过程的第一部分是众所周知的光栅化,光栅化简单的说就是产生一组绘图指令并且生成一张图片.比如绘制一个圆角矩形.带图片.标题居中的U

深入理解 iOS 开发中的锁

来源:伯乐在线 - 夏天然后 链接:http://ios.jobbole.com/89474/ 点击 → 申请加入伯乐在线专栏作者 摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比.iOS 常见知识点(三):Lock.本文也不会详细介绍锁的具体实现原理,这会涉及到太多相关知识,笔者不敢误人子弟. 本文要做的就是简单的分析 iOS 开发中常见的几种锁如何实现,以及优缺点是什么,为什么会有性能上

ios 开发中 动态库 与静态库的区别

使用静态库的好处 1,模块化,分工合作 2,避免少量改动经常导致大量的重复编译连接 3,也可以重用,注意不是共享使用 动态库使用有如下好处: 1使用动态库,可以将最终可执行文件体积缩小 2使用动态库,多个应用程序共享内存中得同一份库文件,节省资源 3使用动态库,可以不重新编译连接可执行程序的前提下,更新动态库文件达到更新应用程序的目的. 从1可以得出,将整个应用程序分模块,团队合作,进行分工,影响比较小. 等其他好处, 从2可以看出,其实动态库应该叫共享库,那么从这个意义上来说,苹果禁止iOS开

文顶顶 iOS开发UI篇—iOS开发中三种简单的动画设置

iOS开发UI篇—iOS开发中三种简单的动画设置 [在ios开发中,动画是廉价的] 一.首尾式动画 代码示例: // beginAnimations表示此后的代码要“参与到”动画中 [UIView beginAnimations:nil context:nil]; //设置动画时长 [UIView setAnimationDuration:2.0]; self.headImageView.bounds = rect; // commitAnimations,将beginAnimation之后的所

iOS开发UI篇—iOS开发中三种简单的动画设置

iOS开发UI篇—iOS开发中三种简单的动画设置 [在ios开发中,动画是廉价的] 一.首尾式动画 代码示例: // beginAnimations表示此后的代码要“参与到”动画中 [UIView beginAnimations:nil context:nil]; //设置动画时长 [UIView setAnimationDuration:2.0]; self.headImageView.bounds = rect; // commitAnimations,将beginAnimation之后的所

iOS开发中三种简单的动画设置

iOS开发中三种简单的动画设置 [在ios开发中,动画是廉价的] 一.首尾式动画 代码示例: // beginAnimations表示此后的代码要“参与到”动画中 [UIView beginAnimations:nil context:nil]; //设置动画时长 [UIView setAnimationDuration:2.0]; self.headImageView.bounds = rect; // commitAnimations,将beginAnimation之后的所有动画提交并生成动