@selector详解(转)(正在理解)

@selector 是什么?

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

3可以理解为类似函数指针的东西--是能让Objective-C动态调用方法的玩意.--是 object-c 的动态后绑定技术 可以通过字符串 访问的函数指针
4其实就是消息响应函数---选一个消息响应的函数地址给你的action
5@selector(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.          
    9.                                   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为示意图仅供参考,我们可以暂时认为是这样的)。 

      Objective-C学习---------SEL类型  
      图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也就是一个整数来查找方法,由于整数的查找和匹配比字符串要快得多,所以这样可以在某种程度上提高执行的效率。
原文地址:https://www.cnblogs.com/ganeveryday/p/4378482.html