IOS学习 - 方法选择器Selector的相关使用

问题的由来

Objective-C是一门动态的语言,只要有可能,Objective-C总会使用动态的方式来解决问题,比如它尽可能的将编译和连接要做的事推迟到运行时,所以它需要一个强大的运行时系统(runtime)来执行编译好的代码。runtime的角色类似于Objective-C语言的操作系统,是Objective-C的灵魂。Objective-C的很多特性都是基于这个强大的运行时的,是十分值得去学习理解的。

动态语言Objective-C很常见的一个消息发送语句(比较准确的说是消息发送,在不混淆的情况下我还是喜欢说方法调用):

1 [receiver message];

在编译时会转化成:

1 objc_msgSend(receiver, selector);

然后再进行编译的后续工作。

事实上还有几个类似的方法:

1 objc_msgSend_stret(返回值是结构体)
2 objc_msgSend_fpret(返回值是浮点型)
3 objc_msgSendSuper(调用父类方法)
4 objc_msgSendSuper_stret(调用父类方法,返回值是结构体)

编译时只是确定了要向确定的实例发特定的消息,并没有去验证消息的实现(IMP),IDE Xcode会做一些基本的验证,但是涉及到@selector()得来的方法,仅仅只是警告,这里也经常发生崩溃。

编译时不验证消息实现,消息也只有在运行时才会具体发送。

注:个人理解是,消息的实现叫方法,方法的传递过程叫消息。

Selector/SEL是什么

Selector/SEL又叫方法选择器,SEL在objc.h中是这样定义的,而“@selector()”是取得一个SEL指针。说白了,方法选择器仅仅是一个char *指针,表示它所代表的是方法的名字:

1 typedef struct objc_selector *SEL;
2 SEL selector = @selector(message); //@selector不是函数调用,只是给编译器的一个提示
3 NSLog (@"%s", (char *)selector); //print message

Objective-C在编译的时候,会根据方法的名字,生成一个唯一的用来区分这个方法的ID,这个ID就是SEL类型的。需要注意的是,只要方法的名字相同,那么它们的ID都是相同的,所以Objective-C,没有重载这个概念,所以你会在Objective-C中看到一个很奇怪的方法命名现象:

1 //-(void)setWidth:(int)width;            这个函数被改写为下面的函数
2 -(void)setWidthIntValue:(int)width;
3
4 //-(void)setWidth:(double)width;      这个函数被改写为下面的函数
5 -(void)setWidthDoubleValue:(double)width            

这里先提一下,获取了SEL后,若想向某一对象发送消息,可以使用performSelecor:

1 [receiver performSelecor:@selector(message)];

但一般建议先用respondsToSelector检查对象是否可以响应这个消息。

写到这里,你可能感觉这个SEL和C/C++中的函数指针很像,那么你可能要失望了,因为IMP和函数指针更像。

IMP是”implementation”的缩写,它是objetive-C 方法(method)实现代码块的地址,可像C函数一样直接调用。通常情况下我们是通过[object method:parameter]或objc_msgSend()的方式向对象发送消息,然后Objective-C运行时(Objective-C runtime)寻找匹配此消息的IMP,然后调用它。

IMP也是一个指针,它指向的是方法的地址,而SEL只是一个ID,用于runtime从方法表中找到对应的IMP。SEL是方法的ID,IMP是方法的实现。IMP和C/C++中函数指针的不同是,函数指针的值,在编译时是确定的,而IMP也是在运行时才确定。关于IMP的部分后几天总结一下再发上来,有兴趣的也可以自己先搜一下。

selector主要用于两个对象之间进行松耦合的通讯。这种方法很多开发环境用到,比如GTK,Delphi。基本上整个Cocoa库之间对象,控制之间通讯都是在这个基础构建的。Selector是Objective-C动态性实现的十分重要的一部分。

Selector/SEL的存储、查找

刚说到,编译器会在编译时根据方法名为方法生成唯一的ID(SEL),而这些SEL构成了若一个集合,由runtime(运行时)维护。runtime为所有正在使用的类和实例维护着这些集合,并且为了减少动态消息带来的时间成本,runtime还为他们维护着一个SEL的cache。方法集合和cache是通过优化过的Hash实现的,通过散列可以可以加快查询速度。

iOS中的obj都继承于NSObject,在NSObjcet中存在一个Class的isa指针。

1 @interface NSObject  {
2     Class isa  OBJC_ISA_AVAILABILITY;
3 }

再来看Class:

 1 typedef struct objc_class *Class;
 2 struct objc_class {
 3     Class isa; //指向元类metaclass
 4
 5     Class super_class ; //指向其父类superclass
 6     const char *name ; //类名
 7     long version ; //类的版本信息,初始化默认为0,可以通过runtime函数class_setVersion和class_getVersion进行修改、读取
 8     long info; //一些标识信息,如CLS_CLASS(0x1L)表示该类为普通class,其中包含对象方法和成员变量;CLS_META (0x2L) 表示该类为元类metaclass,其中包含类方法;
 9     long instance_size ; //该类的实例变量大小(包括从父类继承下来的实例变量);
10     struct objc_ivar_list *ivars; //用于存储每个成员变量的地址
11     struct objc_method_list **methodLists ; //与 info 的一些标志位有关,如CLS_CLASS(0x1L),则存储对象方法,如CLS_META(0x2L),则存储类方法;
12     struct objc_cache *cache; //指向最近使用的方法的指针,用于提升效率;
13     struct objc_protocol_list *protocols; //存储该类遵守的协议
14 }

可以看到,任意的类的isa成员(从NSObject继承下来的)中存储了该类的很多信息。

Class isa:指向metaclass,也就是静态的Class。

Class super_class:指向父类,如果这个类是根类,则为NULL。

下面是isa和superclass的指向关系:

isa指针:

一个Obj实例中的isa会指向普通的Class,这个Class中存储普通成员变量和对 象方法(“-”开头的方法)。

普通Class中的isa指针指向静态Class,静态Class中存储static类型成员变量和类方法(“+”开头的方 法)。

所有元类metaclass中isa指针均指向根元类root metaclass。根元类root metaclass的isa指针也指向自身。

super_class:

普通的Class的super_class指向其父类普通的Class。

元类metaclass的super_class指向其父类元类metaclass。

根元类root metaclass的super_class指向根类普通的Class,根类普通Class的super_class为nil。(顺便提一下,Objective-C中,nil、Nil、NULL的值相同)

下面我们就来看看具体消息发送之后是怎么来动态查找对应的方法的。

首先,编译器将代码[receiver message];转化为objc_msgSend(receiver , @selector (message));,在objc_msgSend函数中。首先通过receiver的isa指针找到receiver对应的class。在Class中先去cache中通过SEL查找对应函数method,若 cache中未找到。再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并通过查找到的IMP跳转到对应的函数中去执行。

Selector相关用法

-(BOOL) isKindOfClass: classObj //判断是否是某个类或其子类的实例

-(BOOL) isMemberOfClass: classObj //判断是否是某个类的实例

-(BOOL) respondsToSelector: selector //判断是否有以某个名字命名的方法(被封装在一个selector的对象里传递)

+(BOOL) instancesRespondToSelector: selector //判断实例是否有以某个名字命名的方法. 和上面一个不同之处在于, 前面这个方法可以用在实例和类上,而此方法只能用在类上.

-(id) performSelector: selector //发送消息(调用方法)

………………今天白天补吧,这都一点半了………………

…………………………………T,T……………………………………

时间: 2024-10-12 13:57:44

IOS学习 - 方法选择器Selector的相关使用的相关文章

ios学习(界面传值的方法)

ios(学习)界面传值的方法 block: 实现界面传值的方法1.block: 实现界面传值,都是从第二个界面向第一个界面传值:第一种block 首先).在第二个界面secondViewController声明set方法 声明block @property (nonatomic,copy) void(^change)(UIColor *color); 其次).在.m文件实现 实现block的方法 _callback([UIColor redColor]);//注意这里之所以是_callback的

[ jquery 过滤器 nextUntil([exp|ele][,fil]) ] 此方法用于在选择器的基础之上搜索被选元素的后面的所有同级元素,方法返回 selector 与 stop 之间的每个元素之后的所有同级元素,并且提供第二个参数来用于实现过滤效果,多个参数使用逗号相隔

此方法用于在选择器的基础之上搜索被选元素的后面的所有同级元素,方法返回 selector 与 stop 之间的每个元素之后的所有同级元素,并且提供第二个参数来用于实现过滤效果,多个参数使用逗号相隔,参数解释如下: 概述: 查找当前元素之后所有的同辈元素,直到遇到匹配的那个元素为止. 如果提供的jQuery代表了一组DOM元素,.nextUntil()方法也能让我们找遍所有元素所在的DOM树,直到遇到了一个跟提供的参数匹配的元素的时候才会停下来.这个新jQuery对象里包含了下面所有找到的同辈元素

iOS学习笔记(5)形参个数可变的方法

如果在定义方法时,在最后一个形参明后增加逗号和三点(,...),则表明该形参可以接受多个参数值. 为了在程序中获取个数可变的形参,需要使用如下关键字 · va_list:这是一个类型,用于定义指向可变参数列表的指针变量 · va_start:这是一个函数,该函数指定开始处理可变形参的列表,并让指针变量指向可变形参列表的第一个参数 · va_end:结束处理可变形参,释放指针变量 · va_arg:该函数返回获取指针当前指向的参数的值,并将指针移动到指向下一个参数 例子 Varargs.h #im

苹果新革命引发iOS学习新方法

苹果系统凭借其流畅的系统和良好的用户体验赢得了一致好评,由于对ios系统有浓厚的兴趣,所以最近一直对苹果ios系统有关注度.今年以来一直在学习有关ios方面的知识,最近利用空余时间分享一些我在扣丁学堂在线学习的有关学习ios的方法,希望能够帮助大家研究iphone和ipad.做程序的都知道,iphone和ipad都是用Objective-C语言来开发的,所以一般情况在学习ios之前,最好有一定的Objective-C基础.学习之前了解和学习一些关于Objective-C的语法基础和规则对往后帮助

iOS学习笔记-精华整理

iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始等待用户的操作,自动释放池就会被释放掉(调用dealloc),池中的对象都会收到一个release,有可能会因此被销毁. 2-成员属性:     readonly:不指定readonly,默认合成getter和setter方法.外界毫不关心的成员,则不要设置任何属性,这样封装能增加代码的独立性和安全

iOS: 学习笔记, performSelectorOnMainThread

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait performSelectorOnMainThread:withObject:waitUntilDone: 基于默认模式调用主线程中接收器的方法 Invokes a method of the receiver on the main thread using the default mode. 参数 Par

IOS学习笔记 -- Modal和Quartz2D

一. Modal1.Modal的默认效果:新控制器从屏幕的最底部往上钻,直到盖住之前的控制器为止;Modal只是改变了View的现实,没有改变rootViewController 2.常用方法1>.以Modal的形式展示控制器- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion2>.关

2015最新iOS学习线路图

iOS是由苹果公司开发的移动操作系统,以xcode为主要开发工具,具有简单易用的界面.令人惊叹的功能,以及超强的稳定性,已经成为iPhone.iPad 和iPod touch 的强大基础:iOS 内置的众多技术和功能让 Apple设备始终保持着遥遥领先的地位. iOS学习路线:http://www.mobiletrain.org/page/ios.html 课程分  类 课程模块 模块介绍 课程内容 Part1C语言 C语言和Objective-C语言 C语言 Mac系统及常用工具.进制:C数据

ios学习记录 day41 UI17 多线程

CPU(工厂) 进程(车间) 线程(工人) 一个进程代表一个应用程序 CPU总是运行一个进程,其它进程处于非运行状态.一个进程可以包含多个线程.线程与线程之间可以共享进程的内存区域. 打开一个应用程序,系统会给我们创建一个线程,称为主线程 管理主界面的UI与内部循环机制(与界面相关的东西必须放在主线程中!!!) 压力比较大且会造成线程阻塞(界面卡),因此我们通过创建子线程来对主线程进行分压. 什么时候用多线程 1.网络请求(同步的) 2.文件读写(少) 3.大数据计算(冒泡) 4.数据库sele