In normal development, we mostly use NSObject and its subclasses, but under Apple’s Foundation, there’s another class that’s on par with NSObject, and that’s NSProxy. This class is rarely used, so we’ll discuss it in detail here.

NSProxy is defined in the Official Apple documentation:

An abstract superclass defining An API for objects that act as standins for other objects or for objects that don’t exist yet.

The result: an abstract superclass that defines an API for objects that act as stand-in for one or more objects that do not yet exist.

A few things to note here:

  • Abstract parent class
  • API
  • double

Abstract parent class

Being an abstract parent means that we cannot use this class directly, but must create a new class to inherit from it before we can use the corresponding API in this class. In this regard, NSProxy is consistent with common NSOperation.

Take a look at the overview of NSProxy in the official documentation.

NSProxy implements the basic methods required of a root class, including those defined in the NSObject protocol.

NSProxy implements the basic methods required by the root class, including those defined in the NSObject protocol.

However, as an abstract class it doesn’t provide an initialization method, and it raises an exception upon receiving any message it doesn’t respond to.

However, as an abstract class, it does not provide an initialization method and will raise an exception when receiving any message that it does not respond to.

A concrete subclass must therefore provide an initialization or creation method and override the forwardInvocation: And methodSignatureForSelector: the methods to handle messages that it doesn ‘t implement itself.

Therefore, a concrete subclass must provide an initialization method, or create method, and rewrite forwardInvocation: and methodSignatureForSelector: method to deal with it no news of their implementation.

A subclass’s implementation of forwardInvocation: should do whatever is needed to process the invocation, such as forwarding the invocation over the network or loading the real object and passing it the invocation.

The implementation of forwardInvocation: in the subclass should do whatever it needs to handle the invocation, such as forwarding the actual object over the network and passing it to the invocation.

methodSignatureForSelector: is required to provide argument type information for a given message; a subclass’s implementation should be able to determine the argument types for the messages it needs to forward and should construct an NSMethodSignature object accordingly.

MethodSignatureForSelector: be required to provide a given message type information; A subclass implementation should be able to determine the parameter types of messages that need to be forwarded, and should build an NSMethodSignature object based on this.

See the NSDistantObject, NSInvocation, and NSMethodSignature class specifications for more information.

See the NSDim Object, NSInvocation, and NSMethodSignature classes for more information.

API

From the OVERVIEW of NSProxy, we have basically seen what we need to do to create a new subclass of NSProxy:

  1. Provide an initialization method
  2. rewriteforwardInvocation:methodSignatureForSelector:methods

Need to be ahead of schedule, forwardInvocation: and methodSignatureForSelector: in the process of the two methods is forward to participate in forwarding method, thus rewrite these two methods are actually to be double object for message forwarding.

The specific code is as follows:

#import <Foundation/Foundation.h> @interface OCWeakProxy : NSProxy @property (nonatomic, weak, readonly) id target; - (instancetype)initWithTarget:(id)target; + (instancetype)proxyWithTarget:(id)target; @ end = = = = = = = = = = = = = = = = separation line = = = = = = = = = = = = = = = = = = = # import "OCWeakProxy. H" @ interface OCWeakProxy () @ property (nonatomic, weak, readwrite) id target; @end @implementation OCWeakProxy - (instancetype)initWithTarget:(id)target { self.target = target; return self; } + (instancetype)proxyWithTarget:(id)target { return [[OCWeakProxy alloc] initWithTarget:target]; } - (void)forwardInvocation:(NSInvocation *)invocation { SEL sel = [invocation selector]; if ([self.target respondsToSelector:sel]) { [invocation invokeWithTarget:self.target]; } } - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { return [self.target methodSignatureForSelector:sel]; } @endCopy the code

It should be noted that since NSProxy is an abstract class, the initialization method of its inherited subclass is still difficult to write, especially for Swift version. OC version is temporarily written here.

double

In the above code, the target passed in with -initWithTarget: and +proxyWithTarget: is the object being substituted.

2. Use NSProxy to implement the Timer cyclic reference problem.

Only the code for the child page is shown here:

#import "NextViewController.h" #import "OCWeakProxy.h" @interface NextViewController () @property (nonatomic, strong) NSTimer *timer; @property (nonatomic, assign) NSInteger count; @end @implementation NextViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.title = @"Next"; self.view.backgroundColor = [UIColor lightGrayColor]; self.count = 0; UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 100, 50)]; button.backgroundColor = [UIColor redColor]; [button addTarget:self action:@selector(clickOnButton:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button]; } - (void)clickOnButton:(UIButton *)sender {// click the button to trigger the timer, And set the target to the Proxy self. The timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: [OCWeakProxy proxyWithTarget: self] selector:@selector(timerInvoked) userInfo:nil repeats:YES]; } - (void)timerInvoked { self.count++; NSLog(@"current count: %ld", self.count); } - (void)dealloc { [self.timer invalidate]; NSLog(@"NextViewController is dealloced."); } @endCopy the code

The running results are as follows:

The analysis is as follows:

Throughout the scene, there are references like this:

Where the dotted line that Proxy points to Controller indicates that this is a weak reference, which is consistent with the previous modification of weak in OCWeakProxy.

When the timer runs, the timerinvocation message is first sent to the proxy and the message is forwarded because the proxy does not implement the corresponding method internally. Finally, the forwardInvocation is rewritten internally: And methodSignatureForSelector: two methods will message is forwarded to the controller, and complete the call.

Since the Proxy reference to the Controller is weak, when the Controller exits, it is released, the reference to the timer is destroyed, and the timer’s -invalidate method is called in the -dealloc method to completely release the timer and remove it from the RunLoop.

At this point, all referenced objects are destroyed.

conclusion

Reference described in the example above and official document, we can find that using NSProxy its greatest value lies in its double function and statement message forwarding method, if the rewrite its subclasses, through these two methods can achieve the original object forward and isolation between method calls, to avoid similar circular reference, high coupling, and so on and so forth. Therefore, in these cases, we can consider using NSProxy to deal with them.