NSProxy的理解和使用

lmg4819發表於2018-05-16

1.NSProxy的定義

NSProxy is an abstract superclass defining an API for objects that act as stand-ins for other objects or for objects that don’t exist yet. Typically, a message to a proxy is forwarded to the real object or causes the proxy to load (or transform itself into) the real object. Subclasses of NSProxy can be used to implement transparent distributed messaging (for example, NSDistantObject) or for lazy instantiation of objects that are expensive to create.
複製程式碼

翻譯:NSProxy是一個抽象的超類,它定義了一個物件的API,用來充當其他物件或者一些不存在的物件的替身。通常,傳送給Proxy的訊息會被轉發給實際物件,或使Proxy載入(轉化為)實際物件。 NSProxy的子類可以用於實現透明的分散式訊息傳遞(例如,NSDistantObject),或者用於建立開銷較大的物件的惰性例項化。

 眾所周知,NSObject類是Objective-C中大部分類的基類。但不是很多人知道除了NSObject之外的另一個基類——NSProxy,NSProxy實現被根類要求的基礎方法,包括定義NSObject協議。然而,作為抽象類,它不實現初始化方法,並且會在收到任何它不響應的訊息時引發異常。因此,具體子類必須實現一個初始化或者建立方法,並且重寫- (void)forwardInvocation:(NSInvocation *)invocation;和- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel方法,來轉發它沒實現的方法。這也是NSProxy的主要功能,負責把訊息轉發給真正的target的代理類,NSProxy正是代理的意思。

2.NSProxy的使用

  1. 模擬多繼承

    多繼承可以看作是單繼承的擴充套件。所謂多繼承是指派生類具有多個基類,派生類與每個基類之間的關係仍可看作是一個單繼承。大家知道,Objective-C不支援多繼承,但是NSProcy可以在一定程度上解決這個問題,但需要注意的是,這只是一個模擬的多繼承,並不是完全的多繼承。接下來,我們看一個官方的例子:

    @interface TargetProxy : NSProxy
     {
         id realObject1;
         id realObject2;
     }
     -(id)initWithTarget1:(id)t1 target:(id)t2;
     
     @end
     
     @implementation TargetProxy
     
     -(id)initWithTarget1:(id)t1 target:(id)t2
     {
           realObject1 = t1;
           realObject2 = t2;
           return self;
     }
     -(void)forwardInvocation:(NSInvocation *)invocation
     {
            id target = [realObject1 methodSignatureForSelector:invocation.selector]?realObject1:realObject2;
            [invocation invokeWithTarget:target];
     }
     -(NSMethodSignature *)methodSignatureForSelector:(SEL)sel
     {
          NSMethodSignature *signature;
          signature = [realObject1 methodSignatureForSelector:sel];
          if (signature) {
               return signature;
          }
          signature = [realObject2 methodSignatureForSelector:sel];
          return signature;
     }
     -(BOOL)respondsToSelector:(SEL)aSelector
     {
          if ([realObject1 respondsToSelector:aSelector]) {
             return YES;
          }
          if ([realObject2 respondsToSelector:aSelector]) {
             return YES;
           }
         return NO;
     }
     @end
     
     使用案例:
     NSMutableArray *array = [NSMutableArray array];
     NSMutableString *string = [NSMutableString string];
     
     id proxy = [[TargetProxy alloc]initWithTarget1:array target:string];
     [proxy appendString:@"This "];
     
     [proxy appendString:@"is "];
     [proxy addObject:string];
     [proxy appendString:@"a "];
     [proxy appendString:@"test!"];
      NSLog(@"count should be 1,it is:%ld",[proxy count]);
     if ([[proxy objectAtIndex:0] isEqualToString:@"This is a test!"]) {
         NSLog(@"Appending successful: %@",proxy);
     }else
     {
         NSLog(@"Appending failed, got: %@", proxy);
     }
         NSLog(@"Example finished without errors.");
     //TargetProxy擁有了NSSting與NSArray倆個類的方法屬性
    
    複製程式碼
  2. 解決NSTimer無法釋放的問題

    解決NSTimer的記憶體洩漏問題

  3. 實現兩個甚至多個不同物件的訊息分發

#import <Foundation/Foundation.h>
 
 @protocol TeacherProtocol <NSObject>
 - (void)beginTeachering;
 @end
 
 @interface Teacher : NSObject
 
 @end

 #import "Teacher.h"
 
 @implementation Teacher
 
 -(void)beginTeachering
 {
     NSLog(@"%s",__func__);
 }
 @end

 #import <Foundation/Foundation.h>
 
 @protocol StudentProtocol <NSObject>
  - (void)beginLearning;
 @end
 
 
 @interface Student : NSObject
 @end
 
 #import "Student.h"
 
 @implementation Student
 
 -(void)beginLearning
 {
     NSLog(@"%s",__func__);
 }
 @end

 #import <Foundation/Foundation.h>
 #import "Teacher.h"
 #import "Student.h"
 
 @interface JSDistProxy : NSProxy<TeacherProtocol,StudentProtocol>
 +(instancetype)sharedInstance;
 -(void)registerMethodWithTarget:(id)target;
 @end

 #import "JSDistProxy.h"
 #import <objc/runtime.h>
 
 @interface JSDistProxy ()
 @property(nonatomic,strong) NSMutableDictionary *selectorMapDic;
 @end
 
 
 @implementation JSDistProxy
 
 +(instancetype)sharedInstance
 {
    static JSDistProxy *proxy;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
       proxy = [JSDistProxy alloc];
       proxy.selectorMapDic = [NSMutableDictionary dictionary];
    });
     return proxy;
 }
 -(void)registerMethodWithTarget:(id)target
 {
     unsigned int count = 0;
     Method *methodList = class_copyMethodList([target class], &count);
     for (int i=0; i<count; i++) {
          Method method = methodList[i];
          SEL selector = method_getName(method);
          const char *method_name = sel_getName(selector);
         [self.selectorMapDic setValue:target forKey:[NSString stringWithUTF8String:method_name]];
     }
     free(methodList);
 }
 
 -(NSMethodSignature *)methodSignatureForSelector:(SEL)sel
 {
       NSString *methodStr = NSStringFromSelector(sel);
       if ([self.selectorMapDic.allKeys containsObject:methodStr]) {
       id target = self.selectorMapDic[methodStr];
           return [target methodSignatureForSelector:sel];
        }
       return [super methodSignatureForSelector:sel];
 }
 -(void)forwardInvocation:(NSInvocation *)invocation
 {
        NSString *methodName = NSStringFromSelector(invocation.selector);
        if ([self.selectorMapDic.allKeys containsObject:methodName]) {
            id target = self.selectorMapDic[methodName];
            [invocation invokeWithTarget:target];
        }else
        {
            [super forwardInvocation:invocation];
         }
 }

 @end
 使用案例:
 JSDistProxy *proxy = [JSDistProxy sharedInstance];
 Teacher *teacher = [Teacher new];
 Student *student = [Student new];
 [proxy registerMethodWithTarget:teacher];
 [proxy registerMethodWithTarget:student];
 
 [proxy beginLearning];
 [proxy beginTeachering];
 //實現方法的實現與宣告分離,提升專案程式碼的可維護性,更加模組化。
複製程式碼

參考資料:

關於NSProxy的理解

NSProxy——少見卻神奇的類


相關文章