Objective-C, usually written in ObjC and rarely used in Objective C or Obj-C, is an object-oriented programming language that expands C. Therefore, a certain degree of basic understanding of the C/C ++ language and understanding of Objective-C will be faster. This time, we will learn Objective-C language, master its syntax and understand its thoughts.
Syntax
Let's take a look at the class declaration in C ++ and Objective-C:
C ++
# Include "BaseClass. h"
Class MyClass: public BaseClass
{
Public:
MyClass ();
Virtual ~ MyClass ();
Virtual int GetValue () const;
Virtual void SetValue (int inValue );
Bool IsValid () const;
Static MyClass * GetInstance ();
Private:
Int mValue;
Static MyClass * sInstance;
};
Objective-C
# Import "BaseClass. h"
@ Interface MyClass: BaseClass
{
Int mValue;
}
-(Int) getValue;
-(Void) setValue: (int) inValue;
-(BOOL) isValid;
+ (MyClass *) getInstance;
@ End
By comparing the above two pieces of code, we can see that the Objective-C language has the following features:
Replace # include with # import
# Import is equivalent to # include + # pragma once in C/C ++. When the header file is nested, its function is realized.
When a file has been read and # include again, # pragma once will skip this read.
For example, we often define this in the header file of C/C ++ to implement # pragma once: 1
2
3 # ifndef INCLUDED_BASECLASS_H
# Include "BaseClass. h"
# Endif
There is no qualifier during inheritance
All inheritance is public. No build or fictitious Functions
Member variables/functions have no delimiters
The member variables are private by default, while the functions are public. No const keyword
No virtual keywords
In Objective-C, the default function is virtual. Next, let's take a look at the specific implementation:
C ++
# Include "MyClass. h"
# Include <assert. h>
MyClass * MyClass: sInstance = 0;
MyClass: MyClass (): mValue (0)
{
}
MyClass ::~ MyClass ()
{
MValue =-1;
}
Int MyClass: GetValue () const
{
Return (mValue );
}
Void MyClass: SetValue (int inValue)
{
Assert (IsValid ());
MValue = inValue;
}
Bool MyClass: IsValid () const
{
Return (0 <= inValue & inValue <= 1000 );
}
MyClass * MyClass: GetInstance ()
{
If (sInstance = 0 ){
SInstance = new MyClass ();
}
Return (sInstance );
}
Objective-C
# Import "MyClass. h"
Static MyClass * sInstance = 0;
@ Implementation MyClass
-(Int) getValue
{
Return (mValue );
}
-(Void) setValue: (int) inValue
{
NSParameterAssert ([self isValid]);
MValue = inValue;
}
-(BOOL) isValid
{
Return (0 <= inValue & inValue <= 1000 );
}
+ (MyClass *) getInstance
{
If (sInstance = 0 ){
SInstance = [MyClass alloc];
}
Return (sInstance );
}
@ End
Instance method
The "-" before the method is the class method of the instance method (similar to the class member function in C ++ ).
The prefix "+" is a class variable (similar to a static member function in C ++ or a global function ).
Like static variables in C/C ++, class variables in Objective-C are declared as static variables. (Valid only in the current definition file)
If the subclass also wants to refer to the class variables in the parent class, you must define the Property Reference Method (class method ). (This is contrary to the encapsulation concept in the object-oriented model, which reduces the cohesion.) A single inheritance
Objective-C, like Java, is a single inheritance.
If you want to implement multiple inheritance, you can only use methods similar to implements in Java. (Protocol in Objective-C) sends a message.
In Objective-C, function calls similar to C/C ++ are called "send messages ". A function is called to send a message. The format is shown in:
Message sent by Objective-C
Method, SEL, method implementation
Shows the relationship between methods, SEL-type, and implementations in Objective-C:
Objective-C method
Concept
Definition of SEL and IMP
Next, let's take a look at the definition of the header file objc. h in Objective-C:
// Objc. h
Typedef struct objc_class * Class;
Typedef struct objc_object {
Class isa;
} * Id;
Typedef struct objc_selector * SEL;
Typedef id (* IMP) (id, SEL ,...);
Typedef signed char BOOL;
# Define YES (BOOL) 1
# Define NO (BOOL) 0
# Ifndef Nil
# Define Nil 0/* id of Nil class */
# Endif
# Ifndef nil
# Define nil 0/* id of Nil instance */
# Endif
Id
Id and void * are not exactly the same. In the above Code, id is a pointer to struct objc_object. This basically means that id is an Object that points to any one that inherits the Object (or NSObject) class. Note that id is a pointer, so you do not need to add a star number when using id. For example, id foo = nil defines an nil pointer, which points to any subclass of NSObject. Id * foo = nil defines a pointer, which points to another pointer and points to a subclass of NSObject.
Objective-C Object
Nil
Nil is the same as NULL in C, which is defined in objc/objc. h. Nil indicates an Objctive-C object. The pointer of this object points to null (nothing is null ).
Nil
Nil and nil in the upper-case letters are a little different. Nil defines a Class (Class rather than an object) pointing to null ).
SEL
SEL is a type of "selector", indicating the name of a method. For example:
-[Foo count] and-[Bar count] use the same selector. Their selector is called count.
In the header file above, we can see that SEL is a pointer to struct objc_selector, but what is objc_selector? In fact, when you use the GNU Objective-C Runtime Library and the NeXT Objective-C Runtime Library (Mac OS X uses the NeXT Runtime Library, their definitions are different. Mac OSX only maps SEL to a C string. For example, if we define a Foo class with a-(int) blah method, the following code:
1 NSLog (@ "SEL = % s", @ selector (blah ));
The output is SEL = blah. To put it bluntly, SEL is the name of the returned method.
This mechanism greatly increases the flexibility of our program. We can pass the SEL parameter to a method so that this method can dynamically execute a method; you can also specify the method to be executed through the configuration file. After the program reads the configuration file, it translates the method string into a SEL variable and sends the message to the corresponding object.
In the Objective-C Runtime Library, selector is managed as an array. This is from the perspective of efficiency: When a function is called, it is not to compare the method name, but to compare the pointer value. Because the integer search and matching are much faster than the string, therefore, the execution efficiency can be improved to some extent.
In this way, the selector in all classes must point to the same object (array ). Once a new class is defined, the selector also needs to be mapped to this array.
In practice, there are two types of selector arrays in total: pre-defined built-in selector arrays and selector Arrays for dynamic append.
Built-in selector
In short, the built-in selector is a large string array. Definition in a objc-sel-table.h file: 1
# Define NUM_BUILTIN_SELS 16371
/* Base-2 log of greatest power of 2 <NUM_BUILTIN_SELS */
# Define LG_NUM_BUILTIN_SELS 13
Static const char * const _ objc_builtin_selectors [NUM_BUILTIN_SELS] = {
". Cxx_construct ",
". Cxx_destruct ",
"CGColorSpace ",
"CGCompositeOperationInContext :",
"CIContext ",
"CI_affineTransform ",
"CI_arrayWithAffineTransform :",
"CI_copyWithZone: map :",
"CI_initWithAffineTransform :",
"CI_initWithRect :",
"CI_rect ",
"CTM ",
"DOMDocument ",
"DTD ",
...
};
As you can see, the NUM_BUILTIN_SELS size of the array is defined as 16371. Strings are sorted alphabetically, simply for the speed of running-time retrieval (Binary Search ).
From the defined selector name, we can see some new method names, such as CIConetext. The method starting with CI is the library imported from Tiger.
This array also needs to be updated each time the system updates. Dynamic append selector
Another selector for dynamic append, which is defined in the objc-sel.m and objc-sel-set.m files
The new selector is appended to the _ buckets member, where the Hash algorithm is used for append and search. 1
Static struct _ objc_sel_set * _ objc_selectors = NULL;
Struct _ objc_sel_set {
Uint32_t _ count;
Uint32_t _ capacity;
Uint32_t _ bucketsNum;
SEL * _ buckets;
};
IMP
From the header file above, we can see that IMP is defined
1 id (* IMP) (id, SEL ,...).
In this case, IMP is a pointer to a function. The directed function includes the id ("self" pointer), The called SEL (method name), and some other parameters. To put it bluntly, IMP is the implementation method.
After getting the function pointer, it means that we have obtained the code entry for this method during execution, so that we can use this function pointer like a common C function call. Of course, we can pass the function pointer as a parameter to other methods or instance variables to achieve great dynamics. We get dynamic, but the price is that the compiler doesn't know which method we want to execute, so it won't find errors for us during compilation. We only know when we execute them, whether the function pointer we wrote is correct. Therefore, when using function pointers, you must accurately grasp all the possibilities that can arise and prevent them. This is especially important when you are writing an API for others' calls.
Method Definition
In the header file objc-class.h, there is a method definition:
Typedef struct objc_method * Method;
Struct objc_method {
SEL method_name;
Char * method_types;
IMP method_imp;
};
This definition seems to include other types we mentioned above. That is to say, Method (the Method we often call) represents a type, which is related to selector and implementation (implementation.
The original SEL is the method name method_name. The char type method_types indicates the parameters of the method. The final IMP is the actual function pointer pointing to the implementation of the function.
Class Definition
Class (Class) is defined as a pointer to struct objc_class, which is defined in the objc/objc-class.h as follows:
Struct objc_class {
Struct objc_class * isa;/* metaclass */
Struct objc_class * super_class;/* parent class */
Const char * name;/* class name */
Long version;/* version */
Long info;/* class information */
Long instance_size;/* instance size */
Struct objc_ivar_list * ivars;/* instance parameter linked list */
Struct objc_method_list ** methodLists;/* method linked list */
Struct objc_cache * cache;/* method cache */
Struct objc_protocol_list * protocols;/* protocol linked list */
};
From the above structure information, we can use members like structure operations in C language. For example, obtain the class name below:
Class cls;
Cls = [NSString class];
Printf ("class name % s \ n", (struct objc_class *) cls)-> name );
Different messages are sent from function calls.
Shows message transmission of Objective-C:
Message Transfer of Objective-C
The process of sending a message can be summarized as follows:
First, specify the method to call.
Obtain selector for method call
After the source code is compiled, the method is interpreted as selector. The selector here is just a string. Message sent to object B
Message transmission uses the objc_msgSend Runtime API. This API only passes selector to the target object B. Obtain the actual implementation method from selector
First, obtain the class information from object B and check whether the implementation of the method is cached (struct objc_cache * cache in the class definition above ;). If not
(Struct objc_method_list ** methodLists; in the previous class definition ;). Run
Use function pointers to call the implementation of methods. In this case, the first parameter is the object instance, and the second parameter is the selector. Transfer Return Value
Use the objc_msgSend API to send back the returned values of the method.
Simply from the process of sending the message above, we can see that the function is finally called using the function pointer. Why is it so hard to go around the big circle? 1
Over the years, with the expansion of library size, the use of dynamic link libraries has become very common. That is to say, the application itself does not include the library code, but dynamically loads the library at startup or during running. In this way, the program size can be reduced, and code reuse can be improved (without re-engineering ). However, the backward compatibility problem arises.
If the library is upgraded repeatedly, when a new method is added, the developer and user must maintain the same version; otherwise, a running error will occur. Generally, this problem is solved by checking whether a new method exists in the current system when calling the newly defined method. If not, skip it or simply generate a warning. RespondsToSelector in Objective-C: The method can be used to implement such an action.
However, this is not a comprehensive solution. If the application is compiled with the new dynamic library (containing the newly defined API), the newly defined API symbol is also included. When such an application is run in an old system (the old dynamic library), the program cannot start because the link symbol cannot be found. This is a common "DLL Region" in win32 systems 」.
To solve this problem, in the binary file compiled by Objective-C, the function is saved as a selector. That is to say, no matter what function is called, the binary file does not contain symbol information. To verify whether the binary file compiled by Objective-C contains symbolic information, run the nm command to check the information.
The source code is as follows:
Int main (int argc, const char * argv [])
{
NSString * string;
Int length;
String = [[NSString alloc] initWithString: @ "Objective-C"];
Length = [string length];
Return 0;
}
The alloc, initWithString:, length, and other methods are called here.
% Nm Test
U. objc_class_name_NSString
00003000 D _ NXArgc
00003004 D _ NXArgv
U ___ CFConstantStringClassReference
20172b98 T ___ darwin_gcc3_preregister_frame_info
U ___ keymgr_dwarf2_register_sections
U ___ keymgr_global
2017300c D ___ progname
201725ec t _ call_mod_init_funcs
201726ec t _ call_objcInit
U _ cthread_init_routine
00002900 t _ dyld_func_lookup
Listen 28a8 t _ dyld_init_check
U _ dyld_register_func_for_add_image
U _ dyld_register_func_for_remove_image
...
We can see that there are no symbols for the alloc, initWithString:, and lengthmethods. Therefore, even if a new method is added, it can be run in any new or old system. Before calling a function, use respondsToSelector: to determine whether a method exists. This feature enables the program to dynamically query the method to be executed during running, improving the flexibility of the Objective-C language.
Target-Action Paradigm
In Objective-C, the communication between GUI control objects uses Target-Action Paradigm. Unlike other event-driven GUI systems, You need to register message processing functions (Win32/MFC, Java AWT, X Window) in the form of callback functions ). Target-Action Paradigm is an object-oriented event transfer mechanism.
For example, if a user clicks a menu event, the Target-Action Paradigm is used to call the Action of the set Target in the menu. The method corresponding to this Action does not need to be implemented. The target and Action are not related to the implementation of the method. It is not detected during source code compilation, but confirmed at runtime (refer to the previous message transfer mechanism ).
During running, the respondsToSelector method is used to check the implementation. If there is an implementation, use javasmselector: withObject: to call a specific Action, as shown in the following code:
// Target object
Id target;
// Selector of the specific Action
SEL action;
...
// Check whether the target achieves the Action
If ([target respondsToSelector: actioin]) {
// Call a specific Action
[Target employee mselector: action withObject: self];
}
Using this architecture, you can use setTarget: To update other targets, or setAction: to change different actions. Implement dynamic method calls.
--------------------------------------------------------------------------------
1. in C/C ++, the callback function (callback) is used to realize the dynamic semantics of a program. Generally, this callback function is a global or static function, using the Thunk method, you can use the class member functions as Callback functions-use platform-related technologies to pass the object instance (this pointer) to the caller.
Author: Yi Feiyang