SoFunction
Updated on 2025-04-14

ios uses NSProxy to implement message forwarding

Introduction

​ In iOS application development, customizing a class generally requires inheriting from the NSObject class or the NSObject subclass. However, the NSProxy class does not inherit from the NSObject class or the NSObject subclass, but an abstract base class that implements the NSObject protocol.

/*    
    Copyright (c) 1994-2019, Apple Inc. All rights reserved.
*/

#import <Foundation/>

@class NSMethodSignature, NSInvocation;

NS_ASSUME_NONNULL_BEGIN

NS_ROOT_CLASS
@interface NSProxy <NSObject> {
    __ptrauth_objc_isa_pointer Class    isa;
}

+ (id)alloc;
+ (id)allocWithZone:(nullable NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
+ (Class)class;

- (void)forwardInvocation:(NSInvocation *)invocation;
- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available");
- (void)dealloc;
- (void)finalize;
@property (readonly, copy) NSString *description;
@property (readonly, copy) NSString *debugDescription;
+ (BOOL)respondsToSelector:(SEL)aSelector;

- (BOOL)allowsWeakReference API_UNAVAILABLE(macos, ios, watchos, tvos);
- (BOOL)retainWeakReference API_UNAVAILABLE(macos, ios, watchos, tvos);

// - (id)forwardingTargetForSelector:(SEL)aSelector;

@end

NS_ASSUME_NONNULL_END

The function of NSProxy is to serve as a delegate proxy object, forward the message to a real object or an object loaded by itself.

In order to further understand the role of the NSProxy class, we will implement a delegate class that a colleague calls the methods in the two classes NSMutableString and NSMutableArray, and simulates multiple inheritance.

First create the TargetProxy class and let it inherit NSProxy. And implement the initialization method.

@interface TargetProxy : NSProxy


/// Initialization method, save two real objects/// @param object1 The first real object/// @param object2 The second real object- (instancetype)initWithObject1:(id)object1 object2:(id)object2;

@end
@implementation TargetProxy {

    // Save the first real object to which the message needs to be forwarded    // The method call priority of the first real object will be higher than the method call priority of the second real object    id _realObject1;
    // Save the second real object to which the message needs to be forwarded    id _realObject2;
}

- (instancetype)initWithObject1:(id)object1 object2:(id)object2 {
    _realObject1 = object1;
    _realObject2 = object2;
    
    return self;
}

Then in the file, override - methodSignatureForSelector: to get the real object method signature and override - forwardInvocation: method to call the real object method.

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    // Get the method signature of sel in _realObject1    NSMethodSignature *signature = [_realObject1 methodSignatureForSelector:sel];
    // If the method is in _realObject1, then the signature of the method is returned    // If not, return the _realObject1 method signature    if (signature) {
        return signature;
    }
    // Get the method signature of sel in _realObject1    signature = [_realObject2 methodSignatureForSelector:sel];
    return signature;
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    // Get the real object that owns the method    id target = [_realObject1 methodSignatureForSelector:[invocation selector]] ? _realObject1 : _realObject2;

    // Execution method    [invocation invokeWithTarget:target];
}

Finally, perform a demo test

- (void)testTargetProxy {
    NSMutableString *string = [NSMutableString string];
    NSMutableArray *array = [NSMutableArray array];
    
    id proxy = [[TargetProxy alloc] initWithObject1:string object2:array];
    [proxy appendString:@"This "];
    [proxy appendString:@"is "];
    [proxy addObject:string];
    [proxy appendString:@"a "];
    [proxy appendString:@"test!"];
    
    NSLog(@"The string is length is: %@", [proxy valueForKey:@"length"]);
    NSLog(@"count should be 1, it is %ld", [proxy count]);
    
    if ([[proxy objectAtIndex:0] isEqualToString:@"This is a test!"]) {
        NSLog(@"Appending successful.");
    } else {
        NSLog(@"Appending failed,, got: '%@'", proxy);
    }
}

Run the above code and enter the log as follows:

2022-04-02 11:30:35.957145+0800 Demo[19783:586710] SuccessFully create Delegere Proxy automatically.
2022-04-02 11:30:35.959722+0800 Demo[19783:586710] The string is length is: 15
2022-04-02 11:30:35.960175+0800 Demo[19783:586710] count should be 1, it is 1
2022-04-02 11:30:40.086227+0800 Demo[19783:586710] Appending successful.

​ As mentioned above, we successfully implemented message forwarding using the TargetProxy class.

Of course, in most cases, using the NSObject class can also implement message forwarding, and the implementation method is similar to NSProxy, but in most cases, it is more appropriate to use NSProxy. because:

  • The NSProxy class implements the basic methods required for the inline class including the NSObject protocol
  • Proxy classes implemented through the NSObject class will not automatically forward methods in the NSObject protocol.
  • Proxy classes implemented through the NSObject class will not automatically forward methods in the NSObject class.

This is the article about ios using NSProxy to implement message forwarding. For more related ios NSProxy message forwarding content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!