@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效果差不多

关于objc_msgSend & performSelector系列函数的问题。

1。objc_msgSend:
书上说这个函数是OC编译器在编译的时候,遇到类似[object foo]的写法时,就会把相应OC的语法转成C的objc_msgSend函数了。还有相应的objc_msgSendSuper

2。关于performSelector系列:
performSelector, performSelector:withDelay:, performSelector:withObj:withDelay, XXX...
除了第一个,后面几个都可以指定一个延迟时间,然后就把selector放到当前线程的runloop中等待调用。从方法名上看,感觉performSelector == performSelector:withDelay系列中,delay为0的情况。

我的问题是:
1。objc_msgSend这个函数是直接由[object foo]转过来的吗? 也就是说,中间不会转成performSelector吧?objc_msgSend是同步的吗?
2。performSelector:withDelay:这个是异步的了,放进runloop中,那如果performSelector和performSelector:withDelay(delay为0时)一样的话, 那performSelector也是异步的吗?也要进runloop吗?

刚开始看cocoa,有很多不懂的。

tianya 2011-05-11 08:13
答:
1、是的,不要performSelector了,直接objc_msgSend。事实上,这个是OC编译时就转好的,也就是OC编译的时候就把对象调用转成函数的call了
2、是的,delay为0也进runloop,这样做的好处是可以不阻住当前调用performSelector:withDelay:的方法的执行

怎样证实上边的答案没有问题?
很简单,写几个测试代码,然后打断点一跑,在运行时看调用栈。
观察程序运行最好的方法总是调试器~

respondsToSelector

3、delegate属性使用assign的原因。

循环引用

所有的引用计数系统,都存在循环应用的问题。例如下面的引用关系:

对象a创建并引用到了对象b.

对象b创建并引用到了对象c.

对象c创建并引用到了对象b.

这时候b和c的引用计数分别是2和1。当a不再使用b,调用release释放对b的所有权,因为c还引用了b,所以b的引用计数为1,b不会被释放。b不释放,c的引用计数就是1,c也不会被释放。从此,b和c永远留在内存中。

这种情况,必须打断循环引用,通过其他规则来维护引用关系。比如,我们常见的delegate往往是assign方式的属性而不是retain方式的属性,赋值不会增加引用计数,就是为了防止delegation两端产生不必要的循环引用。如果一个UITableViewController 对象a通过retain获取了UITableView对象b的所有权,这个UITableView对象b的delegate又是a,如果这个delegate是retain方式的,那基本上就没有机会释放这两个对象了。自己在设计使用delegate模式时,也要注意这点。

因为循环引用而产生的内存泄露也是Instrument无法发现的,所以要特别小心。

4、delegate属性使用assign的原因。

还有一些用法会让系统拥有对象的所有权。比如NSObject 的performSelector:withObject:afterDelay 。如果有必要,需要显示的调用cancelPreviousPerformRequestsWithTarget:selector:object: ,否则有可能产生内存泄露。

iPhone开发中,动态调用类和方法:

NSClassFromString

NSSelectorFromString

正常来说,

id myObj = [[NSClassFromString(@"MySpecialClass") alloc] init];

id myObj = [[MySpecialClass alloc] init];

是一样的。但是,如果你的程序中并不存在MySpecialClass这个类,下面的写法会出错,而上面的写法只是返回一个空对象而已。

因此,在某些情况下,可以使用NSClassFromString来进行你不确定的类的初始化。

比如在iPhone中,NSTask可能就会出现这种情况,所以在你需要使用NSTask时,最好使用:

[[NSClassFromString(@"NSTask") .....]]

而不要直接使用[NSTask ...]这种写法。

NSClassFromString的好处是:

1 弱化连接,因此并不会把没有的Framework也link到程序中。

2 不需要使用import,因为类是动态加载的,只要存在就可以加载。

[cpp] view plaincopy

    1. for (int c=0; c<[classNames count]; c++) {
    2. NSString *className=[classNames objectAtIndex:c];
    3. id class=[[NSClassFromString(className) alloc] init];
    4. for (int i=0; i<[params count]; i++) {
    5. [class performSelector:NSSelectorFromString([NSString stringWithFormat:@"setA%i",i])];
    6. }
    7. }
    8. object c中的selector

      其作用相当于函数指针,现在我看到的大多说用法都是在调用某些函数需要传递一个 函数指针 参数时,使用@selector。它会在当前类里面查找selector后面所跟的函数,返回一个SEL类型的值。
      SEL变量的执行.用performSelecor方法来执行. 
      [对象 performSelector:SEL变量 withObject:参数1 withObject:参数2]; 
      在调用respondsToSelector:@selector(method)时,这个method只有在该方法存在参数时需要 ":",如果该方法不需要参数就不需要加这个冒号。否则,编译不会报错,只是执行返回的值不对。当然如果方法有多个参数,需要多个冒号,参数有名称的需要带上参数名称。 
      如:有如下方法:
      -(NSString*)toXmlString;
      此时调用类似于:
      [self respondsToSelector:@selector(toXmlString)]
      如果toXmlString方法的定义为:
      -(NSString*)toXmlString:(NSString*)prefix;
      那么调用就必须加上冒号,如:[self respondsToSelector:@selector(toXmlString:)]
      ·-(BOOL) isKindOfClass: classObj 用来判断是否是某个类或其子类的实例 
      ·-(BOOL) isMemberOfClass: classObj 用来判断是否是某个类的实例 
      ·-(BOOL) respondsToSelector: selector 用来判断是否有以某个名字命名的方法(被封装在一个selector的对象里传递) 
      ·+(BOOL) instancesRespondToSelector: selector 用来判断实例是否有以某个名字命名的方法. 和上面一个不同之处在于, 前面这个方法可以用在实例和类上,而此方法只能用在类上. 
      ·-(id) performSelector: selector 执行某个方法

      [Objective-C]SEL类型、@selector选择器 

      1     id cattle[3]; 
      2     SEL say; 
      3     SEL skin;

        其中id cattle[3]定义了一个数组用于存储Cattle或者Bull对象。这一行代码估计大家都很熟悉,笔者就不赘述了。像这样的传统的数组并不能完全满足我们的需求,当我们需要做诸如追加,删除等操作的时候,会很不方便。在随后的章节里面笔者将要向大家介绍传统数组的替代解决方案NSArray。

        上一段代码的第二行和第三行是本节所关注的,就是SEL类型。Objective-C在编译的时候,会根据方法的名字(包括参数序列),生成一个用 来区分这个方法的唯一的一个ID,这个ID就是SEL类型的。我们需要注意的是,只要方法的名字(包括参数序列)相同,那么它们的ID都是相同的。就是 说,不管是超类还是子类,不管是有没有超类和子类的关系,只要名字相同那么ID就是一样的。除了函数名字和ID,编译器当然还要把方法编译成为机器可以执 行的代码,这样,在一个编译好的类里面,就产生了如下图所示方法的表格示意图(本构造属于笔者推测,没有得到官方证实,所以图5-2为示意图仅供参考,我们可以暂时认为是这样的)。

        
      图5-2,方法的表格示意图

        请注意setSkinColor后面有一个冒号,因为它是带参数的。由于存在这样的一个表格,所以在程序执行的时候,我们可以方便的通过方法的名字,获取到方法的ID也就是我们所说的SEL,反之亦然。具体的使用方法如下:

      1     SEL 变量名 = @selector(方法名字); 
      2     SEL 变量名 = NSSelectorFromString(方法名字的字符串); 
      3     NSString *变量名 = NSStringFromSelector(SEL参数);

      其中第1行是直接在程序里面写上方法的名字,第2行是写上方法名字的字符串,第3行是通过SEL变量获得方法的名字。我们得到了SEL变量之后,可以通过下面的调用来给一个对象发送消息:

        [对象 performSelector:SEL变量 withObject:参数1 withObject:参数2];

        这样的机制大大的增加了我们的程序的灵活性,我们可以通过给一个方法传递SEL参数,让这个方法动态的执行某一个方法;我们也可以通过配置文件指定需要执行的方法,程序读取配置文件之后把方法的字符串翻译成为SEL变量然后给相应的对象发送这个消息。

        从效率的角度上来说,执行的时候不是通过方法名字而是方法ID也就是一个整数来查找方法,由于整数的查找和匹配比字符串要快得多,所以这样可以在某种程度上提高执行的效率。

时间: 2024-09-19 21:52:30

@selector详解(转)(正在理解)的相关文章

Java NIO 的前生今世 之四 NIO Selector 详解

Selector Selector 允许一个单一的线程来操作多个 Channel. 如果我们的应用程序中使用了多个 Channel, 那么使用 Selector 很方便的实现这样的目的, 但是因为在一个线程中使用了多个 Channel, 因此也会造成了每个 Channel 传输效率的降低.使用 Selector 的图解如下: 为了使用 Selector, 我们首先需要将 Channel 注册到 Selector 中, 随后调用 Selector 的 select()方法, 这个方法会阻塞, 直到

GDT,LDT,GDTR,LDTR 详解,包你理解透彻(转)

引自:http://www.techbulo.com/708.html 一.引入 保护模式下的段寄存器 由 16位的选择器 与 64位的段描述符寄存器 构成 段描述符寄存器: 存储段描述符 选择器:存储段描述符的索引 段寄存器 PS:原先实模式下的各个段寄存器作为保护模式下的段选择器,80486中有6个(即CS,SS,DS,ES,FS,GS)80位的段寄存器.由选择器CS对应表示的段仍为代码段,选择器SS对应表示的段仍为堆栈段. 二.详解 先说明一下概念 (1)全局描述符表GDT(Global

JMS学习(四) Selector详解

一.前言 在掌握了消息的结构之后,我们接下来看一下JMS的一个重要功能:选择器.有些时候,作为消费者只希望处理自己感兴趣的消息.如果某个消息只有一个消费者,我们可以在让该客户端根据规则来处理自己感兴趣的消息,那些不满足某些规则的就直接替丢弃掉. 但如果消息是广播的机制,那么让每个客户端都去做这样的处理,就加大了客户端的工作量,一种更好的方式,就是由消息提供者来完成消息的过滤和路由工作,这样就能大减轻客户端的工作量,消费者在真正处理的时候,完全不用关注对消息的过滤,可以只负责对消息的处理.也就是说

[数据库事务与锁]详解八:底理解数据库事务乐观锁的一种实现方式——CAS

注明: 本文转载自http://www.hollischuang.com/archives/1537 在深入理解乐观锁与悲观锁一文中我们介绍过锁.本文在这篇文章的基础上,深入分析一下乐观锁的实现机制,介绍什么是CAS.CAS的应用以及CAS存在的问题等. 线程安全 众所周知,Java是多线程的.但是,Java对多线程的支持其实是一把双刃剑.一旦涉及到多个线程操作共享资源的情况时,处理不好就可能产生线程安全问题.线程安全性可能是非常复杂的,在没有充足的同步的情况下,多个线程中的操作执行顺序是不可预

[数据库事务与锁]详解七: 深入理解乐观锁与悲观锁

注明: 本文转载自http://www.hollischuang.com/archives/934 在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性. 乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段. 无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想.其实不仅仅是关系型数据库系统中有乐观锁和悲观锁的概念,像memcache.hibernate.

android selector详解

--> 改变字体的颜色<selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 当前窗口失去焦点时 --> <item android:color="@android:color/black" android:state_window_focused="false" /> <!-- 不可用时 --> &

Android之Selector详解(背景选择器)

定义: 在XML中定义的一个可绘制(drawable)的对象,它可以根据状态的不同为同一个图形更换不同的图片.比如说,对于一个按钮控件(Button)可以有几种不同的状态(pressed,focused或者其它),你可以使用state list为其中的每一个状态来设置按钮的背景图片. XML的位置: android的selector是在res/drawable/中配置的 标签与属性: android:constantSize 这个是布尔型,false表示各个状态的大小(size)各自不同,tru

Callable、FutureTask和Future详解带你理解java并发编程

一. Callable接口与Runnable接口区别 创建java线程,我们经常使用两种方式: 一是直接继承Thread 另一种是实现Runnable接口 但这两种方式有一个缺陷:在执行完任务之后无法直接获取执行结果. 1. 接口定义 1.1 Callable接口 public interface Callable<V> { V call() throws Exception; } 1.2 Runnable接口 public interface Runnable { public abstra

Spring事务管理(详解+实例)

写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都