Selectors and SEL types

The method name (selector) in the program is replaced after compilation by an internal identifier that corresponds to the SEL type.

Objective-c defines the @selector() instruction in order to manipulate compiled selectors within the program. The compiled selector is referenced directly by the @selector() instruction. Variables of type SEL can also be declared.

Example @selector() instruction:

    @selector(creationDate)
    @selector(showPhotos)
    @selector(createPath)
Copy the code

Different selectors generate different SEL type values after compilation and conversion. The SEL type values corresponding to the same selector must be the same. We don’t need to know what the SEL type value of the selector is, it’s processor-specific. However, if an SEL variable is invalid, you can set it to NULL, or you can use (SEL)0 as usual.

A variable of type SEL can be used to send a message, and the following methods are declared in nsobject.h.

// Send the message represented by aSelector to the receiver of the message and return the execution result of the message
- (id)performSelector:(SEL)aSelector;
// Send the message represented by aSelector to the receiver of the message. The message parameter is object, and return the execution result of the message
- (id)performSelector:(SEL)aSelector withObject:(id)object;
// Send the message represented by aSelector to the receiver of the message. The parameters of the message are object1, object2, and return the execution result of the message
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
Copy the code

The following two types of message sending are the same.

  [self showPhotos];
  [self performSelector:@selector(showPhotos)];
Copy the code

Using performSelector in ARC projects: The function appears “performSelector may cause a leak because its selector is unknown” warning. This is because in ARC mode, the runtime needs to know what to do with the return value of the method you are calling. The return value can be any value. Normal static selectors do not display this warning because the information for static selectors is determined at compile time. However, when using performSelector:, ARC does not know the exact return value of the method and is not sure what to do with it. Retained ns_returns_or ns_returns_autoreleased. You can use macro definitions to resolve this warning.

#define SuppressPerformSelectorLeakWarning(code) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \ code; \ _Pragma("clang diagnostic pop") \
Copy the code

The following is an example of how to dynamically select which method to execute

ProductModel *product1=[[ProductModel alloc]init];
product1.url=@"https://www.baidu.com/";
ProductModel *product2=[[ProductModel alloc]init];
product2.url=@"https://www.hao123.com/";
_fileType=1;
_isFile=YES;
SEL method=(_fileType==0)?@selector(showVideo:) : @selector(showPhoto:);
id object1 =(_isFile==NO)? product1 : product2; SuppressPerformSelectorLeakWarning([self performSelector:method withObject:object1])

- (void)showVideo:(ProductModel *)obj{
    NSLog(@"showVideo %@",obj.url);
}

- (void)showPhoto:(ProductModel *)obj{
    NSLog(@"showPhoto %@",obj.url);
}
Copy the code

SEL calls methods with performSelector in a manner similar to how function Pointers are used in C. The same effect can be achieved using function Pointers.

A function pointer is the memory address of a function. The function corresponding to the pointer is determined at compile time and cannot execute functions other than those specified. SEL types are equivalent to method names that dynamically execute different methods, depending on the recipient of the message.

The SEL type specifies the method to execute, which is how Objective-C messages are sent. And in this way, you’re talking about the dynamics of Objective-C.

Message search

Which method an object executes when it receives a message is dynamically determined. All instance variables have an ISA variable of type Class, the Class object. When a message is received, the runtime system checks to see if there is a method in the class that matches the message selector, executes the corresponding method if there is one, and if not, looks for the corresponding method in the parent class through the pointer to the parent class in the class object. If you keep looking for the root class without finding the corresponding method, an execution-time error is prompted.

If you need to look up a method every time you receive a message, the process of sending a message can be expensive. In this case, a hash table is cached internally by the runtime system, which records information about what methods a class has that correspond to what selectors, where the methods are defined, and so on. In this way, the next time you receive the same message, you can directly use the information cached last time, and there is no need to search again.

Message search and class object legend

Nsobject. h defines methods for dynamically querying whether an object corresponds to a selector.

// Query whether the receiver of the message has methods that can respond to aSelector, including methods inherited from the parent class. Return YES - (BOOL)respondsToSelector:(SEL)aSelector; // Query whether the recipient of the message belongs to a class that can respond to instance methods of aSelector, including methods inherited from the parent class. If exists, return YES + (BOOL) instancesRespondToSelector (SEL) aSelector;Copy the code

Call a method as a function

Methods defined in a class are usually implemented in the form of functions, but they are usually not called directly during programming. If you want the program to run faster, or need to pass the function pointer in the form of C language, you can call the function directly to save the cost of sending messages. Or load the definition of a method dynamically at execution time, or call a method as a function. However, if you call a method as a function, you can’t take advantage of features like object-oriented dynamic binding. Sending messages, while slower than calling functions directly, can be object-oriented compared to functions.

To get a function pointer to a method held by an object, do the following.

// Search for the method corresponding to the specified selector and return a pointer to the function implementation of that method. - (IMP)methodForSelector:(SEL)aSelector; / / search and specified selector corresponding instance methods, and returns the pointer to the instance method of function + (IMP) instanceMethodForSelector (SEL) aSelector;Copy the code

IMP, short for implementation, is a function pointer that points to the entry of the method implementation code.

IMP definition

typedef id (*IMP)(id,SEL,...) ;Copy the code

This function pointer points to a function that contains the id (self pointer), SEL of the call, and other arguments.

case

  // Returns the function pointer implemented by the showPhoto method
  IMP imp =(IMP)[self methodForSelector:@selector(showPhoto:)];
// Define a function pointer that contains parameters of type id, SEL, and ProductModel, and assign imp function pointer to function pointer func
    void(*func)(id,SEL,ProductModel*)=(void*)imp;
// Call the function pointer func
    (*func)(self.@selector(showPhoto:),product1);
Copy the code

As you can see from the example, when calling a function pointer, you pass the message receiver object and the message selector as parameters, in addition to the parameters required for the method declaration. These two parameters are accessible inside the method and are called implicit parameters. The receiver of the first argument message is self, and the second argument selector is accessible through the _cmd variable.

I’m going to assign self

As mentioned above, self is an implicit argument to the method, representing the object that received the message. Then self can be sent to itself again. Self can also be used as an argument to the message or as the return value of the method.

Example: When initializing a method definition, assign self to the return value of the parent initializer method

- (instancetype)init{
    if (self= [super init]) {
        self.features=@"hello";
    }
    return self;
}
Copy the code

If you assign self to an object, the object will become the receiver of the message and continue running. In ARC mode, an error will be reported unless self is assigned before the method is initialized. Therefore, assignment to self is limited to initialization methods.