Document directory
Objective-C 2.0 with Cocoa Foundation --- 6, NSObject mysteries 6, NSObject mysteries
This series of lectures has a strong correlation. If you are reading this article for the first time and want to better understand the content of this chapter, I suggest you read it from Chapter 1 of this series of lectures, click here.
In the previous chapter, I introduced several very important concepts in Objective-C, namely SEL, Class, and IMP. We know that Objective-C is an extension of C language. With these three concepts, we also talked about inheritance and encapsulation. Objective-C has undergone earth-shaking changes, it is compatible with the efficient features of C language and implements object-oriented functions.
Objective-C is essentially a C language. So how does the internal implementation of SEL, Class and IMP, as well as encapsulation and inheritance? To answer this question, I decided to give a brief introduction to the main class of Objective-C, NSObject.
But to be honest, if you think that the content in this chapter is obscure, you will not have any negative impact on writing programs if you do not read the content in this chapter. However, if you have mastered the content in this chapter, to deepen our understanding of Objective-C, it will be a great boost for the content I will talk about in the future.
6.1 execution results of this Chapter
In this chapter, we will continue to use the classes Cattle and Bull we have constructed in the previous chapters. In the current Xcode version, some important things such as the prototype definition of Class are put into the LIB file, so the specific definition of these things, this is invisible to us.
We first open the code in chapter 1, and then open "Cattle. h file, move the mouse over NSObject, right-click, and select "Jump to Definition" in the pop-up menu ". Then a small menu is displayed, and we select "interface NSObject ". We can see the following code:
@ Interface NSObject <NSObject> {
Class isa;
We know that there is only one variable in the so-called NSObject, which is the isa of the Class type. Isa is a pointer. That is to say, there is only one instance variable isa in NSObject. Okay. We need to know what a Class is. Move the mouse over "Class", right-click the Class, and select "Jump to Definition" in the pop-up menu ", we see the following code:
Typedef struct objc_class * Class;
Typedef struct objc_object {
Class isa;
} * Id;
...
Here we know that the Class is actually a pointer type of the objc_class. We move the mouse over "objc_class", right-click the Class, and select "Jump to Definition" in the pop-up menu ", in this window, Xcode does not bring us to the definition of objc_class, so we have no idea what the objc_class is like.
By the way, you may have noticed the definition of id. id is actually a pointer to the objc_object structure. There is only one element in it, that is, Class. As we can see above, the so-called id is the pointer of the objc_class pointer. Let's recall the following code:
Id cattle = [Cattle new];
This is the cattle object in initialization and instance. In this process, we can actually understand that runtime initializes the Class pointer for us and returns this pointer to us. After the initialization object is complete, the object we get is actually a Class pointer to this object.
Let's look back at this mysterious Class. We cannot see in Xcode the definition of Class, that is, objc_class. Fortunately, this part is defined as GCC Code and is open-source. After downloading the open-source code, I made some minor adjustments to the open-source code, and then put the Class definition in our engineering files. After the type conversion, finally, we can see the true colors of Class, SEL, and isa.
In the previous chapters, we will introduce the Screen Copy of program execution results in each chapter. This chapter is the same, but the execution results in this chapter are very simple. This chapter focuses on understanding the NSObject mechanism.
Figure 6-1. program running result in this Chapter
When you see the Screen Copy of the running results of programs in this chapter, it may be boring, because from the result screen alone, we did not find anything interesting. On the contrary, these are some of the old faces that students are already familiar. However, what I want to talk about in this chapter may be something that students have never met in other languages. These things will be fresh and exciting.
6.2. Implementation steps
Step 1: Create a project named 06-NSObject according to the method described in Chapter 2nd. If this is the first time you read this article, please refer to Chapter 2 here.
Step 2, according to the method described in step 2, step 3, and step 4 in chapter 4th, refer to the "Cattle. h "," Cattle. m "," Bull. h "and" Bull. "m" is imported into the project in this chapter. If you do not have the code in Chapter 4th, please download it here. If you have not read chapter 1, see here.
Step 3: Move the mouse over "Source" in the project browser, select "Add" in the pop-up menu, and select "New File" in the sub-menu ", select "Other" at the bottom left of the new File dialog box, select "Empty File" in the right window, and select "Next ", in the "New File" dialog box, enter "MyNSObject" in the "File Name" column. h ". Then input the following code (or copy it, because it is C code. If you are familiar with the C language, copy it to save time:
# Include <stddef. h>
Typedef const struct objc_selector
{
Void * sel_id;
Const char * sel_types;
} * MySEL;
Typedef struct my_objc_object {
Struct my_objc_class * class_pointer;
} * MyId;
Typedef myId (* MyIMP) (myId, MySEL ,);
Typedef char * STR;/* String alias */
Typedef struct my_objc_class * MetaClass;
Typedef struct my_objc_class * MyClass;
Struct my_objc_class {
MetaClass class_pointer;
Struct my_objc_class * super_class;
Const char * name;
Long version;
Unsigned long info;
Long instance_size;
Struct objc_ivar_list * ivars;
Struct objc_method_list * methods;
Struct sarray * dtable;
Struct my_objc_class * subclass_list;
Struct my_objc_class * sibling_class;
Struct objc_protocol_list * protocols;
Void * gc_object_type;
};
Typedef struct objc_protocol {
Struct my_objc_class * class_pointer;
Char * protocol_name;
Struct objc_protocol_list * protocol_list;
Struct objc_method_description_list * instance_methods, * class_methods;
} Protocol;
Typedef void * retval_t;
Typedef void (* apply_t) (void );
Typedef union arglist {
Char * arg_ptr;
Char arg_regs [sizeof (char *)];
} * Arglist_t;
Typedef struct objc_ivar * Ivar_t;
Typedef struct objc_ivar_list {
Int ivar_count;
Struct objc_ivar {
Const char * ivar_name;
Const char * ivar_type;
Int ivar_offset;
} Ivar_list [1];
} IvarList, * IvarList_t;
Typedef struct objc_method {
MySEL method_name;
Const char * method_types;
MyIMP method_imp;
} Method, * Method_t;
Typedef struct objc_method_list {
Struct objc_method_list * method_next;
Int method_count;
Method method_list [1];
} MethodList, * MethodList_t;
Struct objc_protocol_list {
Struct objc_protocol_list * next;
Size_t count;
Protocol * list [1];
};
Step 4: Open the 06-NSObject.m file, enter the following code and save
# Import <Foundation/Foundation. h>
# Import "Cattle. h"
# Import "Bull. h"
# Import "MyNSObject. h"
Int main (int argc, const char * argv []) {
NSAID utoreleasepool * pool = [[NSAID utoreleasepool alloc] init];
Id cattle = [Cattle new];
Id redBull = [Bull new];
SEL setLegsCount_SEL = @ selector (setLegsCount :);
IMP cattle_setLegsCount_IMP = [cattle methodForSelector: setLegsCount_SEL];
IMP redBull_setLegsCount_IMP = [redBull methodForSelector: setLegsCount_SEL];
[Cattle setLegsCount: 4];
[RedBull setLegsCount: 4];
[RedBull setSkinColor: @ "red"];
Class cattle_class = cattle-> isa;
MyClass my_cattle_class = cattle-> isa;
SEL say = @ selector (saySomething );
IMP cattle_sayFunc = [cattle methodForSelector: say];
Cattle_sayFunc (cattle, say );
Class redBull_class = redBull-> isa;
MyClass my_redBull_class = redBull-> isa;
IMP redBull_sayFunc = [redBull methodForSelector: say];
RedBull_sayFunc (redBull, say );
[Pool drain];
Return 0;
}
Step 5: Click [pool drain]; on the left side of the Code in the window of the 06-NSObject.m file to check whether a blue rod is displayed, if yes, the breakpoint is selected. 6-2
Figure 6-2: Select execute breakpoint
Step 6: Select "Run" in the menu above Xcode, select "Debuger", and select "Build and Go" in the "Debuger" window ".
All right, everyone will stop here. Don't do anything else. We will interrupt the program and almost execute it to the final breakpoint, we will use Debuger to see exactly what magic happens inside Objective-C.
Note:
There are some warnings during the process from compilation to execution. The program instructions in this chapter are used to describe something inside NSObject, so ignore these warnings. Of course, when we write our own program, the warning generated by compilation is generally not negligible.
6.3. superclass method call
Now we open the "06-NSObject.m" file and find the following code:
SEL setLegsCount_SEL = @ selector (setLegsCount :);
IMP cattle_setLegsCount_IMP = [cattle methodForSelector: setLegsCount_SEL];
IMP redBull_setLegsCount_IMP = [redBull methodForSelector: setLegsCount_SEL];
This piece of code is nothing new for the students. We have already talked about it in chapter 5th. This is the concept of SEL and IMP. Here we get the setLegsCount: function pointer of the cattle object and redBull object.
If you are not in Debuger, select "Run" in the Xcode menu and select "Debuger ".
We noticed that in Debuger, the address of cattle_setLegsCount_IMP is exactly the same as that of redBull_setLegsCount_IMP, as shown in 6-3:
Figure 6-3 shows the addresses of cattle_setLegsCount_IMP and redBull_setLegsCount_IMP.
Note:
Because the environment and the memory used for execution are different, the value of the address displayed on the computer may be different from the value shown in Figure 6-3.
The addresses of cattle_setLegsCount_IMP and redBull_setLegsCount_IMP are identical, indicating that they use the same code segment. How is this result produced? Open "MyNSObject. h" and refer to the following code:
Struct my_objc_class {
MetaClass class_pointer;
Struct my_objc_class * super_class;
Const char * name;
Long version;
Unsigned long info;
Long instance_size;
Struct objc_ivar_list * ivars;
Struct objc_method_list * methods;
Struct sarray * dtable;
Struct my_objc_class * subclass_list;
Struct my_objc_class * sibling_class;
Struct objc_protocol_list * protocols;
Void * gc_object_type;
};
The author adds the "my _" prefix to the definition of the open source code, just to differentiate it. "MyNSObject. h "contains many code problems. I have never used this code in actual code. The main purpose of using this code is to explain the concept to everyone, please ignore all the problems in the code.
We noticed that the methods variable contains the class method name (SEL) definition and the method pointer address (IMP ). When we execute
IMP cattle_setLegsCount_IMP = [cattle methodForSelector: setLegsCount_SEL];
Runtime uses the dtable array to quickly find the required function pointer. The search function is defined as follows:
_ Inline _ IMP
Objc_msg_lookup (id receiver, SEL op)
{
If (else ER)
Return sarray_get (receiver-> class_pointer-> dtable, (sidx) op );
Else
Return nil_method;
Okay. Now there is no problem with our cattle_setLegsCount_IMP. What should we do with redBull_setLegsCount_IMP? In the Bull Class, we didn't define the instance method setLegsCount:. So in the Bull Class, can't the runtime find setLegsCount? The answer is, yes, runtime cannot be found directly, because setLegsCount is not defined in the Bull class at all :.
However, from the result, it is obvious that runtime cleverly found the setLegsCount: Address. How does runtime find it? The answer is:
Struct my_objc_class * super_class;
If it is not found in its own class, runtime will go to the super cattle class of the Bull class to find it. Fortunately, it successfully found setLegsCount In the runtime class of the cattle class: so we get redBull_setLegsCount_IMP. RedBull_setLegsCount_IMP and cattle_setLegsCount_IMP are both defined in the Cattle class, so their code addresses are the same.
Let's assume that if the runtime cannot find setLegsCount in the cattle? It doesn't matter. cattle also has super classes, that is, NSObject. Therefore, the runtime will be searched in NSObject. Of course, NSObject is not magical enough to predict that we need to define setLegsCount: So runtime cannot be found.
At this time, the runtime does not give up the final effort and does not find the corresponding method, runtime will send a forwardInvocation: Message to the object, in addition, the original message and message parameters are packaged into an NSInvocation object and used as the unique parameter of forwardInvocation. ForwardInvocation: It is defined in NSObject. If you need to reload this function, you can use forwardInvocation to send undefined messages to your class: and send messages to a safe place to avoid system errors.
I have not rewritten forwardInvocation: In the code in this chapter. However, when rewriting forwardInvocation:, be sure to avoid sending messages cyclically. For example, in the forwardInvocation of Class A objects, students send messages and message parameters that Class A cannot respond to Class B objects; at the same time, when class B cannot respond to A message in Class B forwardInvocation, it is easy to form an endless loop. Of course, this problem is not easy for a person to write code. When you write code in a group, if you rewrite forwardInvocation:, You need to reach a consensus with others in the group, this avoids loop calls.
6.4. Call of overloaded Methods
Let's continue to follow the "06-NSObject.m" file. Please refer to the following code of idea:
1 Class cattle_class = cattle-> isa;
2 MyClass my_cattle_class = cattle-> isa;
3 SEL say = @ selector (saySomething );
4 IMP cattle_sayFunc = [cattle methodForSelector: say];
5 cattle_sayFunc (cattle, say );
6
7 Class redBull_class = redBull-> isa;
8 MyClass my_redBull_class = redBull-> isa;
9
10 IMP redBull_sayFunc = [redBull methodForSelector: say];
11 redBull_sayFunc (redBull, say );
The content in this section is similar to that in section 6.3. I don't think it is necessary to explain the Code. If you are not familiar with it, refer to Chapter 5th.
Both our Cattle class and Bull class have the saySometing instance method. We know that as long as the method definition is the same, their SEL is the same. Based on a SEL say, we found their function pointers in the cattle and redBull objects. According to section 6.3, we know that when runtime receives the search method, it will first look for it in this class, and then the search process will end, at the same time, the IMP of this method is returned to us. Therefore, the cattle_sayFunc and redBull_sayFunc in the above Code should be different, as shown in 6-4:
Figure 6-4: Address of cattle_sayFunc and redBull_sayFunc
6.5. Class in the superclass and subclasses
During Class memory allocation, for a Class, runtime needs to find the superclass of the Class, and then assign the address of the pointer to the super_class in isa. Therefore, the Class in our cattle should be exactly the same as the super_class in the Class in redBull. See figure 6-5:
Figure 6-5: super_class in the Class in cattle and redBull
6.6. Memory Allocation location of instance variables
Let's first recall how an object was created. When creating an object, the class content needs to be transferred to the memory. We call it memory Allocation (Allocation). Then we need to initialize the object variable (Initialization ), after these steps are completed, our class is instantiated. We call the class that has been instantiated an Object ).
For the memory allocation process, the runtime needs to know the memory allocated and the location of each instance variable. Let's go back to "MyNSObject. h" and refer to the following code:
1 typedef struct objc_ivar * Ivar_t;
2 typedef struct objc_ivar_list {
3 int ivar_count;
4 struct objc_ivar {
5 const char * ivar_name;
6 const char * ivar_type;
7 int ivar_offset;
8} ivar_list [1];
9} IvarList, * IvarList_t;
Let's take a closer look at the ivar_name of row 5th. As the name suggests, this is the name of the instance variable. The ivar_type of row 6th is the type of the instance variable, and the ivar_offset of row 7th is the location definition. Runtime obtains this information from the isa class and then knows how to allocate memory. Let's take a look at Figure 6-6:
Figure 6-6 Location of instance variables in memory
In cattle, we can see that the first instance variable is isa, and the second is our defined legsCount. Isa is a super variable and legsCount is a variable of the Cattle class. We can see that super class variables are always put in front, and then sub-class variables.
So what does redBull look like? Let's take a look at Figure 6-7.
Figure 6-7: Location of instance variables in redBull
Figure 6-7 shows that the position offset of skinColor in the redBull Class is 8. Obviously, runtime reserves two positions for isa and legsCount.
6.7 summary of this Chapter
Thank you very much!
In this chapter, the author uses a small "trick" to unveil the secrets of NSObject. Although the content in this chapter is not necessary to understand Objective-C, it will be helpful to understand the content of subsequent chapters. I hope you will spend a little time to read it.
In addition, I need to emphasize that the content in this chapter is complete and accurate because I have not obtained official instructions. The content in this chapter is for your reference and entertainment only, I hope you will understand.
Posted @ Yaski read (7906) Comment (18) Edit