美文网首页iOS 学习iOS 知识点
IOS SEL(@selector)原理以及应用

IOS SEL(@selector)原理以及应用

作者: 后浪普拉斯 | 来源:发表于2017-11-20 15:32 被阅读201次

    IOS SEL(@selector)原理
    其中@selector()是取类方法的编号,取出的结果是SEL类型。
    SEL:类成员方法的指针,与C的函数指针不一样,函数指针直接保存了方法的地址,而SEL只是方法的编号。

    说一下C的函数指针:

    int addOne(int val)
    {
        return val + 1;
    }
    int main(int argc, char *argv[]) {
        int (*p)(int val);//定义一个函数指针
        p = addOne;//p 指向addOne的地址
        cout<<"result :"<<(*p)(5)<<endl;//直接通过函数指针调用函数
    }
    

    objective-c 中时:

    SEL method = @selector(func);//定义一个类方法的指针,selector查找是当前类(包含子类)的方法
    

    objective demo:

    父类 SelectorDemo.h

    #import <Foundation/Foundation.h>
    
    @interface SelectorDemo : NSObject
    
    @property (nonatomic, assign) SEL methodTest;
    
    -(void)TestParentMethod;
    
    -(void)TestChildMethod;
    
    @end
    

    selectorDemo.m

    #import "SelectorDemo.h"
    
    @implementation SelectorDemo
    
    -(void)parentMethod{
        NSLog(@"parent method call success");
    }
    
    -(void)TestParentMethod{
        if (_methodTest) {
            [self performSelector:_methodTest withObject:nil];
        }
    }
    
    -(void)TestChildMethod{
        if (_methodTest) {
            [self performSelector:_methodTest withObject:nil];
        }
    }
    
    @end
    

    子类SelectorSub.h

    #import "SelectorDemo.h"
    
    @interface SelectorSub : SelectorDemo
    
    @end
    

    子类SelectorSub.m

    #import "SelectorSub.h"
    
    @implementation SelectorSub
    
    -(void)SubMethod{
        NSLog(@"sub class method call success");
    }
    
    @end
    

    调用:

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        SelectorSub *SClass = [[SelectorSub alloc] init];
        SClass.methodTest = @selector(parentMethod);
        [SClass TestChildMethod];
        SClass.methodTest = @selector(SubMethod);
        [SClass TestParentMethod];
    }
    

    运行的结果如下:

    
    2017-11-16 10:52:16.089 SEL方法[50141:9692483] parent method call success
    2017-11-16 10:52:16.091 SEL方法[50141:9692483] sub class method call success
    

    我们看到SClass.methodTest = @selector(parentMethod);时候子类的对象去通过@selector()方法去寻找方法的ID,这时寻找的范围包括本身和父类,我们找到方法之后将ID赋值给自身的成员变量_methodTest,之后的调用就是直接调用父类的TestChildMethod方法。
    SClass.methodTest = @selector(SubMethod);通过@selector()去父类寻找方法,没有找到继续找子类的方法,找到之后继续调用,没有找到之后会报地址寻找出错。

    注意:

    我们在调用方法的时候,调用不属于自己的方法和调用没有定义的方法是一样的,所以需要我们验证方法是否返回消息(即是否实现这个方法),所以在不确定的时候需要respondsToSelector: 来确定方法是否定义。

    SEL消息机制的工作原理:

    在作为所有类的根类的NSObject 中.isa的成员变量,所以所有的对象都有一个isa的变量,而isa变量指向该对象的类。
    查看NSObject的类的时候可以发现

    struct objc_object {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    };
    

    在runtime.h的查看类objc_class的时候,我们可以看到一个包含isa指针的结构体。所以类也是一个对象,同时它也必须是另一个类的实例,这份类就是元类。

    struct objc_class {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    
    #if !__OBJC2__
        Class _Nullable super_class                              OBJC2_UNAVAILABLE;
        const char * _Nonnull name                               OBJC2_UNAVAILABLE;
        long version                                             OBJC2_UNAVAILABLE;
        long info                                                OBJC2_UNAVAILABLE;
        long instance_size                                       OBJC2_UNAVAILABLE;
        struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
        struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
        struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
        struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
    #endif
    
    } OBJC2_UNAVAILABLE;
    

    元类同时也是一个对象,它指向的是根元类,根元类本身的isa 指针指向自己,就形成了闭环。


    20150901201727650.jpeg

    接着上面继续,所有的对象都有一个isa的变量,而isa变量指向该对象的类。类其实也是实体的存在, 程序运行时每个类都有自己的存储空间,而isa 便指向这样一个类的空间,便建立了类和对象的对应关系,类空间包含了该类的成员变量以及方法实现,还包含指向父类空间的指针。


    image.png

    方法以selector作为索引,selector的数据类型是SEL,对应每个方法的位置的ID,当我们寻找方法的时候寻找的是方法的ID,存在一个方法和ID对应的methodList表来存储这种对应关系。

    selector-funName关系图:
    image.png

    编译时,编译器会通过selector来查找

    [myobject funMethod1:para];
    

    编译之后的方法应是:

    objc_msgSend(myObject, 8, para);
    

    这里的objc_msgSend()函数会使用myObject的isa指针来找到myObject放入类空间结构并在类空间结构中查找selector 8所对应的方法,如果没有找到,那么将使用指向父类的指针找到父类空间结构进行 selector 8方法的查找,如果还没有找到,就继续沿着父类网上找,直到找到,若果一直到NSObject还没有找到,就会抛异常。

    相关文章

      网友评论

        本文标题:IOS SEL(@selector)原理以及应用

        本文链接:https://www.haomeiwen.com/subject/cllhvxtx.html