Dynamic tips and tricks for objective-c

Source: Internet
Author: User
Tags new set

A large number of objective-c developers have emerged in the past few years. Some are transferred from a dynamic language, such as Ruby or Python, some of which are transferred from a strongly typed language, such as Java or C #, and, of course, with Objective-c as the introductory language. This means that a large part of the developers are not using objective-c for too long. When you touch a new language, you pay more attention to the basics, such as grammar and features. But there are usually more advanced, less-known and powerful features waiting for you to open up.


The Runtime

Objective-c is a simple language, 95% is C. Just add some keywords and grammar to the language level. What really makes Objective-c so powerful is its runtime. It's small but very powerful. The core of it is message distribution.

Messages

If you are moving from a dynamic language such as Ruby or Python, you may know what a message is, and you can skip directly to the next section. Those who have turned from other languages continue to see.

Executing a method, some languages, the compiler performs some additional optimizations and error checking because the invocation relationship is straightforward and obvious. But for message distribution, it is less obvious. You do not have to know whether an object can handle a message before sending it. You send the message to it, it may be processed, or it may be transferred to another object for processing. A message does not have to correspond to a method, and an object may implement a method to handle multiple messages.

In Objective-c, the message is implemented by Objc_msgsend (), a runtime method, and a similar approach. This method requires a target,selector, and there are some parameters. In theory, the compiler simply executes the message distribution into objc_msgsend. For example, the following two lines of code are equivalent.

[Array Insertobject:foo atindex:5];objc_msgsend (array, @selector (insertobject:atindex:), Foo, 5);


Most object-oriented languages have classes and objects concepts. Objects is generated by classes. But in objective-c, classes itself is also objects (translator Note: This is similar to Python), can also handle messages, which is why there are class methods and instance methods. Specifically, the object in Objective-c is a struct (struct), and the first member is ISA, which points to its own class. This is defined in the objc/objc.h.

typedef struct OBJC_OBJECT {    Class Isa;} *id;

The class of object holds a list of methods, as well as pointers to the parent class. But classes is also objects, there will be an Isa variable, then where does it point? This leads to a third type: metaclasses. A metaclass is pointed to Class,class and is pointed to object. It holds a list of all the implemented methods, as well as the metaclass of the parent class. Read This article if you want to get a clearer idea of how objects,classes and metaclasses together workplace.

Methods, selectors and IMPs

We know that the runtime sends a message to the object. We also know that the class of an object holds a list of methods. How are these messages mapped to methods, and how are these methods executed?

The answer to the first question is simple. The class method list is actually a dictionary, and key is selectors,imps as value. An IMP is an implementation that points to the method in memory. It is important that the relationship between selector and Imp is determined at run time, not at compile time. So we can play some tricks.

IMP is usually a pointer to a method, the first argument is self, the type is ID, the second argument is _cmd, the type is sel, and the remainder is the parameter of the method. This is also where self and _cmd are defined. The following shows the method and Imp

-(ID) dosomethingwithint: (int) aint{} ID dosomethingwithint (id self, SEL _cmd, int aInt) {}


Other run-time methods

Now that we know objects,classes,selectors,imps and message distribution, what can the runtime do? There are two main functions:

1. Create, modify, Introspect classes and objects

2. Message distribution

Message distribution has been mentioned before, but this is only a small subset of the functionality. All run-time methods have a specific prefix. Here are some interesting ways to do this:

Class

Class begins with a method to modify and Introspect classes. Methods such as Class_addivar, Class_addmethod, Class_addproperty, and Class_addprotocol allow reconstruction of classes.

Class_copyivarlist, Class_copymethodlist, Class_copyprotocollist, and class_copypropertylist can get all the content of a class. And Class_getclassmethod, class_getclassvariable, Class_getinstancemethod, class_getinstancevariable, Class_ Getmethodimplementation and Class_getproperty return individual content.

There are also some common methods of introspection, such as Class_conformstoprotocol, Class_respondstoselector, Class_getsuperclass. Finally, you can use Class_createinstance to create an object.

Ivar

These methods allow you to get names, memory addresses, and objective-c type encoding.

Method

These methods are mainly used for introspection, such as Method_getname, Method_getimplementation, Method_getreturntype and so on. There are also some methods of modification, including method_setimplementation and Method_exchangeimplementations, which we will talk about later.

ObjC

Once you've got the object, you can do some introspection and modification on it. You can get/set Ivar, use Object_copy and object_dispose to copy and free object memory. The most NB is not just to get a class, but to use Object_setclass to change the class of an object. You will be able to see the usage scenario later.

Property

Property holds a large part of the information. In addition to getting a name, you can also use Property_getattributes to discover more information about the property, such as the return value, whether it is atomic, getter/setter name, whether it is dynamic, the Ivar name used behind it, is a weak reference.

Protocol

Protocols is a bit like classes, but the Lite version, the runtime method is the same. You can get the method, property, protocol list, and check if other protocol are implemented.

Sel

Finally we have some ways to deal with selectors, such as getting a name, registering a selector, and so on.

Now we have a general understanding of Objective-c's running time to see what interesting things they can do.

Classes and selectors from Strings

A dynamic feature of the comparison base is the generation of classes and selectors by string. Cocoa provides the nsclassfromstring and Nsselectorfromstring methods, which are simple to use:

Class Stringclass = nsclassfromstring (@ "NSString");

So we got a string class. Next:

NSString *mystring = [Stringclass stringwithstring:@ "Hello World"];

Why did you do it? Is it more convenient to use class directly? This is usually the case, but there are scenarios where this method can be useful. First, it is possible to know if there is a class,nsclassfromstring that returns nil if the class does not exist at runtime. For example, you can check if nsclassfromstring (@ "nsregularexpression") is nil to determine if it is ios4.0+.

Another usage scenario is to return a different class or method based on different inputs. For example, if you are parsing some data, each data item has a string to parse and its own type (String,number,array). You can do this in one way, or you can use multiple methods. One of the methods is to get the type and then use if to invoke the matching method. The other is to generate a selector based on the type, and then call it. Here are two ways to achieve this:

-(void) Parseobject: (ID) object {for    (ID data in object) {        if ([[Data Type] isequaltostring:@ "String"]) {            [SE LF Parsestring:[data value]];         } else if ([[Data type] isequaltostring:@ "number"]) {            [self parsenumber:[data value]];        } else if ([[Data type] isequ altostring:@ "Array"]) {            [self parsearray:[data value]];}}    -(void) Parseobjectdynamic: (ID) object {for    (ID data in object) {        [self performselector:nsselectorfromstring ( [NSString stringwithformat:@ "parse%@:", [Data type]]) Withobject:[data value]];}    } -(void) parsestring: (NSString *) astring {}-(void) Parsenumber: (NSString *) anumber {}-(void) Parsearray: (NSString *) Aarray {}

As you can see, you could turn the code with the IF in line 7 into 1 lines. If there is a new type in the future, simply add the implementation method instead of adding the new else if.

Method swizzling

As we have said before, the method consists of two parts. Selector equivalent to a method of id;imp is the implementation of the method. One of the conveniences of such separation is that the correspondence between selector and IMP can be changed. For example, an IMP can have multiple selectors pointing to it.

The method swizzling can exchange two implementations. Maybe you'll ask, "What's the situation that will require this?" ”。 Let's look at the two ways of extending class in Objective-c. The first is subclassing. You can override a method to invoke the implementation of the parent class, which also means that you must use the subclass instance, but if you inherit a cocoa class, cocoa returns to the original class (such as Nsarray). In this case, you will want to add a method to Nsarray, that is, use category. 99% of cases This is OK, but if you rewrite a method, there is no chance to invoke the original implementation.

Method swizzling can handle this problem. You can override a method without inheriting it, and you can invoke the original implementation. A common practice is to add a method to the category (or a whole new class, of course). The implementation can be exchanged by method_exchangeimplementations this run-time method. To see a demo, this demo demonstrates how to rewrite the AddObject: method to record every newly added object.

#import  <objc/runtime.h> @interface Nsmutablearray (loggingaddobject)-(void) Logaddobject: (ID) aobject;@ End @implementation Nsmutablearray (loggingaddobject) + (void) load {    Method AddObject = Class_getinstancemethod ( Self, @selector (addobject:));    Method Logaddobject = Class_getinstancemethod (self, @selector (logaddobject:));    Method_exchangeimplementations (AddObject, logaddobject);} -(void) Logaddobject: (id) aobject {    [self logaddobject:aobject];    NSLog (@ "Added object%@ to Array%@", aobject, self);} @end


We put the method exchange into load, and this method is only called once and is loaded at runtime. If you point to a temporary use, you can put it somewhere else. Notice a very obvious recursive call to Logaddobject:. This is where method swizzling easy to confuse us, because we have exchanged the implementation of the methods, so actually the call is AddObject:

Dynamic inheritance, Exchange

We can create a new class at run time, which isn't used much, but it's still very powerful. You can create new subclasses through it and add new methods.

But what is the use of such a subclass? Don't forget a key point of objective-c: There is a variable inside object called Isa that points to its class. This variable can be changed without having to recreate it. Then you can add new Ivar and methods. You can modify the class of an object with the following command

Object_setclass (MyObject, [MySubClass class]);

This can be used in key Value observing. When you start observing an object, cocoa creates the subclass of the object's class, and then points the object's Isa to the newly created subclass. Click here to see a more detailed explanation.

Dynamic method Processing

So far, we have discussed method exchange and the processing of existing methods. So what happens when you send a message that an object cannot handle? It is obvious that "it breaks". This is true in most cases, but cocoa and runtime also provide some coping options.

The first is the dynamic method processing. Typically, a method is processed, and the runtime looks for a matching selector and executes it. Sometimes you just want to create a method at run time, for example, some information is only available at run time. To achieve this effect, you need to rewrite +resolveinstancemethod: and/or +resolveclassmethod:. If you do add a method, remember to return yes.

+ (BOOL) Resolveinstancemethod: (SEL) Aselector {    if (Aselector = = @selector (Mydynamicmethod)) {        Class_ Addmethod (Self, Aselector, (IMP) mydynamicimp, "[email protected]:");        return YES;    }    return [Super Resolveinstancemethod:aselector];}

What scenario will the cocoa use these methods? Core data is used a lot. Nsmanagedobjects has many properties that are added at run time to handle Get/set properties and relationships. What if the model is changed at run time?

Message forwarding

If Resolve method returns no, the runtime goes to the next step: message forwarding. There are two common use cases. 1) forward the message to another object that can process the message. 2) forward multiple messages to the same method.

Message forwarding is a two-step phase. First, the runtime calls-forwardingtargetforselector: If you just want to send the message to another object, use this method because it is more efficient. If you want to modify the message, then use-forwardinvocation: The runtime packages the message into nsinvocation and returns it to you for processing. After processing, call Invokewithtarget:.

Cocoa has several places where message forwarding is used, the main two places are proxies (Proxies) and response chains (Responder Chain). Nsproxy is a lightweight class that works by forwarding messages to another object. It is useful if you want to lazily load a property of object. Nsundomanager also works, but intercepts messages, then executes them, rather than forwarding them to other places.

The response chain is about how cocoa handles and sends events and behaviors to the corresponding object. For example, using cmd+c executes the copy command, which sends-copy: to the response chain. First Responder, usually the current UI. If the message is not processed, it is forwarded to the next-nextresponder. Keep going until you find an object that can handle the message, or you can't find it, error.

Use block as Method IMP

IOS 4.3 brings a lot of new runtime methods. In addition to the enhancement of properties and protocols, a new set of methods to start with IMP is also brought. Typically an IMP is a pointer to a method implementation, with the first two arguments being object (self) and selector (_cmd). IOS 4.0 and Mac OS X 10.6 brought Block,imp_implementationwithblock () to allow us to use block as IMP, and the snippet below shows how to add a new method using block.

IMP myimp = Imp_implementationwithblock (^ (id _self, nsstring *string) {    NSLog (@ "Hello%@", string);}); Class_addmethod ([MYclass class], @selector (SayHello:), Myimp, "[Email protected]:@");


If you want to know how this is implemented, you can check out this article

As you can see, the objective-c surface looks quite simple, but still very flexible, can bring many possibilities. The advantage of a dynamic language is that it does a lot of clever things without expanding the language itself. Key Value observing, for example, provides an elegant API that seamlessly integrates with existing code without the need for new language-level features. We know that Objective-c development iOS app, have to question is app security, about iOS app security How to protect, can click here to see

Dynamic tips and tricks for objective-c

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.