zoukankan      html  css  js  c++  java
  • @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也就是一个整数来查找方法,由于整数的查找和匹配比字符串要快得多,所以这样可以在某种程度上提高执行的效率。
  • 相关阅读:
    HTML_常用标签
    介绍Html_前端
    Mysql(2)数据类型与约束
    Mysql命令
    python_高级进阶(5)协程_事件
    python_高级进阶(4)同步_异步_回调函数_阻塞
    数据类型的补充以及各类型之间的相互转换
    二次编码
    坑坑坑坑坑坑----列表
    字典
  • 原文地址:https://www.cnblogs.com/ganeveryday/p/4378482.html
Copyright © 2011-2022 走看看