Nsproxy implements AOP to implement exception handling policies for iOS apps

Source: Internet
Author: User
Tags finally block

I have been concerned about the AOP implemented by objc some time ago.

I found two libraries in GitHub: AOP-in-objective-C and AOP-for-objective-C.

The first is implemented based on nsproxy; the second is implemented based on GCD and block;

Both use the cocoa runtime programming technology to inject the interceptor into the proxy object so that it can interfere with the execution sequence of the real object so as to add a "cut-plane" to the code, the mode here is the normal proxy mode.

Because of the time relationship, I only read the code of the first database for the moment. I will briefly analyze it below.

Nsproxy: as its name, it is used to "proxy" a real object. This is the built-in implementation of the foundation library. Most people know that nsobject is usually the root class in cocoa. That's right, but there are actually more than one root class. nsproxy is also the root class with nsobject, it is only an abstract class and is not used for programming purposes in the general sense, so it is not so widely known (in fact, I only know it today ). And nsobject sees it and you think it is a class. But when I read the nsproxy definition today, I found that its header file is defined as follows:

@interface NSProxy <NSObject>

At first, I was puzzled. If it was inherited from nsobject, it should be a colon. This is obviously the way to implement the Protocol. So I checked the information and there was an nsobject protocol, and the nsobject class also implemented the nsobject protocol. For more information, see this article.

Both nsproxy and nsobject implement the nsobject protocol. This allows nsproxy implementation classes to "proxy" nsobject sub-classes, abstract the nsobject protocol, and allow them to share certain behaviors.

Let's see how it works (for the test code, see the aoplibtest. M file ):

Where you need to use AOP, you first need to instantiate an object. For example, if you need to instantiate an nsmutablearray, you need to use aopproxy to instantiate it:

NSMutableArray* testArray = (NSMutableArray*)[[AOPProxy alloc] initWithNewInstanceOfClass:[NSMutableArray class]];

Here, it is actually indirect instantiation. It provides an interface for you to pass your class name to it and instantiate it for you. In fact, this is an injection method. After reading the definition of this method, you will see that it actually returns to you is not an instance of nsmutablearray (in fact, it is aopproxy, the reason why they are forced to convert each other is that they all implement the nsobject Protocol ):

- (id) initWithNewInstanceOfClass:(Class) class {    // create a new instance of the specified class    id newInstance = [[class alloc] init];    // invoke my designated initializer    [self initWithInstance:newInstance];    // release the new instance    [newInstance release];    // finally return the configured self    return self;}

The above self refers to aopproxy, where the initwithinstance method:

- (id) initWithInstance:(id)anObject {        parentObject = [anObject retain];    methodStartInterceptors = [[NSMutableArray alloc] init];    methodEndInterceptors = [[NSMutableArray alloc] init];    return self;}

It can be seen that it holds the real object inside and instantiates two arrays to store the interceptor set before and after method execution.

Next, we can add an interceptor for nsmutablearray:

[(AOPProxy*)testArray interceptMethodStartForSelector:@selector(addObject:)                                    withInterceptorTarget:self                                      interceptorSelector:@selector( addInterceptor: )];        [(AOPProxy*)testArray interceptMethodEndForSelector:@selector(removeObjectAtIndex:)                                  withInterceptorTarget:self                                    interceptorSelector:@selector( removeInterceptor: )];

Because these two methods are the instance methods of aopproxy, you still need to forcibly convert them back when writing them (in fact, when you track them in xcode, here, testarray has always been an object of the apoproxy type, because it was first generated by aopporxy allo ). The implementation of these two methods is very simple, but the interceptor is taken out of the corresponding array and will be executed later.

[testArray addObject:[NSNumber numberWithInt:1]];        [testArray removeObjectAtIndex:0];

Well, it seems that the behavior of an object is called. Why does it look like it? Isn't it. Of course not. I have already said it above. Here we just name it testarray. In fact, it is not an nsmutablearray instance, but an aopproxy instance. But why can it still call the addobject method? Because it is forcibly converted to the nsmutablearray type, the editor can accept this type conversion, that is, this is legal. So the editor thinks it is an nsmutablearray object, so it can be called like this, but you will see it later. At runtime, the compiler actually knows that it is not a real nsmutablearray type (that is, it cannot respond to addobject and removeobjectatindex ), therefore, it is handed over to another dedicated method to process these unrecoverable messages:

-(Void) forwardinvocation :( nsinvocation *) aninvocation;

This method is actually inherited from nsporxy. nsproxy actually throws an exception and the subclass needs to implement it again to pass its message to the real object. For more information, see the official documentation!

Let's take a look at its implementation:

- (void)forwardInvocation:(NSInvocation *)anInvocation;{    SEL aSelector = [anInvocation selector];    // check if the parent object responds to the selector ...    if ( [parentObject respondsToSelector:aSelector] ) {        [anInvocation setTarget:parentObject];        //        // Intercept the start of the method.        //                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];        for ( int i = 0; i < [methodStartInterceptors count]; i++ ) {            // first search for this selector ...            AOPInterceptorInfo *oneInfo = [methodStartInterceptors objectAtIndex:i];            if ( [oneInfo interceptedSelector] == aSelector ) {                // extract the interceptor info                id target = [oneInfo interceptorTarget];                SEL selector = [oneInfo interceptorSelector];                // finally invoke the interceptor                [(NSObject *) target performSelector:selector withObject:anInvocation];            }        }        [pool release];        //        // Invoke the original method ...        //                [self invokeOriginalMethod:anInvocation];                //        // Intercept the ending of the method.        //                NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];                for ( int i = 0; i < [methodEndInterceptors count]; i++ ) {                        // first search for this selector ...            AOPInterceptorInfo *oneInfo = [methodEndInterceptors objectAtIndex:i];                        if ( [oneInfo interceptedSelector] == aSelector ) {                                // extract the interceptor info                id target = [oneInfo interceptorTarget];                SEL selector = [oneInfo interceptorSelector];                                // finally invoke the interceptor                [(NSObject *) target performSelector:selector withObject:anInvocation];            }        }                [pool2 release];            } //    else {//        [super forwardInvocation:invocation];//    }}

You can cut it down so that the real object calls the method and interferes with the behavior of the object, and adds the interceptor before and after it to execute the operation. Thus, AOP is implemented elegantly.

The Library also provides two aspect:

Aopmethodloger-logs used for simple method recording;

Aopthreadinvoker-used to execute methods on a separate thread;

Previously, AOP instances were widely used in Java and. net. common applications include log, exception capture, and so on. Recently, IOS apps also involve exception capture, especially database operations and business logic exceptions. They always write code to capture blocks and take up a lot of trouble. Therefore, I added an aspect: aopexcettioncatcher. The exception capture function is implemented here.

The invokeoriginalmethod method is re-implemented:

- (void)invokeOriginalMethod:(NSInvocation *)anInvocation{    NSLog(@"%@",@"entry into try block");    @try {        [super invokeOriginalMethod:anInvocation];    }    @catch (NSException *exception) {        NSLog(@"%@",@"entry into catch block");        NSLog(@"%@",[exception reason]);    }    @finally {        NSLog(@"%@",@"entry into finally block");    }}

Of course, this is only one application, and you can use it to do more things.


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.