使用 NSProxy 實現消息轉發

来源:https://www.cnblogs.com/r360/archive/2022/07/04/16441909.html
-Advertisement-
Play Games

一、簡介 ​ 在 iOS 應用開發中,自定義一個類一般需要繼承自 NSObject 類或者 NSObject 子類,但是,NSProxy 類不是繼承自 NSObject 類或者 NSObject 子類,而是一個實現了 NSObject 協議的抽象基類。 /* NSProxy.h Copyright ...


一、簡介

​ 在 iOS 應用開發中,自定義一個類一般需要繼承自 NSObject 類或者 NSObject 子類,但是,NSProxy 類不是繼承自 NSObject 類或者 NSObject 子類,而是一個實現了 NSObject 協議的抽象基類。

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

#import <Foundation/NSObject.h>

@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

NSProxy 的作用就是作為一個委托代理對象,將消息轉發給一個真實的對象或者自己載入的對象。

為了進一步瞭解 NSProxy 類的作用,我們來實現一個同事調用 NSMutableString 和 NSMutableArray 兩個類中的方法的委托類,模擬多繼承。

首先創建 TargetProxy 類,讓他繼承 NSProxy。並實現初始化方法。

@interface TargetProxy : NSProxy


/// 初始化方法,保存兩個真實對象
/// @param object1 第一個真實對象
/// @param object2 第二個真實對象
- (instancetype)initWithObject1:(id)object1 object2:(id)object2;

@end
@implementation TargetProxy {

    // 保存需要將消息轉發到的第一個真實對象
    // 第一個真實對象的方法調用優先順序會比第二個真實對象的方法調用優先順序高
    id _realObject1;
    // 保存需要將消息轉發到的第二個真實對象
    id _realObject2;
}

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

然後在 TargetProxy.m 文件中,重寫 - methodSignatureForSelector: 獲取真實對象方法簽名,並重寫 - forwardInvocation: 方法,調用真實的對象方法。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    // 獲取 _realObject1 中 sel 的方法簽名
    NSMethodSignature *signature = [_realObject1 methodSignatureForSelector:sel];
    // 如果 _realObject1 中有該方法,那麼返回該方法的簽名
    // 如果沒有,返回 _realObject1 方法簽名
    if (signature) {
        return signature;
    }
    // 獲取 _realObject1 中的 sel 的方法簽名
    signature = [_realObject2 methodSignatureForSelector:sel];
    return signature;
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    // 獲取擁有該方法的真實對象
    id target = [_realObject1 methodSignatureForSelector:[invocation selector]] ? _realObject1 : _realObject2;

    // 執行方法
    [invocation invokeWithTarget:target];
}

最後,進行 Demo 測試

- (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);
    }
}

運行上面的代碼,輸入日誌如下:

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.

​ 以上說明,我們使用 TargetProxy 類成功的實現了消息轉發。

​ 當然,在大部分情況下,使用 NSObject 類也可以實現消息轉發,實現方式和 NSProxy 類似,但是大部分情況下使用 NSProxy 更加合適。因為:

  • NSProxy 類實現了包括 NSObject 協議在內基類所需的基礎方法
  • 通過 NSObject 類實現的代理類不會自動的轉發 NSObject 協議中的方法
  • 通過 NSObject 類實現的代理類不會自動的轉發 NSObject 類別中的方法

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ###@Spark分區器(Partitioner) ####HashPartitioner(預設的分區器) HashPartitioner分區原理是對於給定的key,計算其hashCode,並除以分區的個數取餘,如果餘數小於0,則餘數+分區的個數,最後返回的值就是這個key所屬的分區ID,當key為 ...
  • 一鍵直達直播間 一、直播介紹 上兩期渡劫同學為大家分享了ChunJun數據還原的DDL模塊,想必大家對這一模塊有了比較深入的瞭解,本期無倦同學將會為大家分享ChunJun同步Hive事務表的相關內容,直播將從Hive事務表的結構及原理、ChunJun讀寫Hive事務表實戰、源碼解析及ChunJun文 ...
  • 一. 下載mysql 8.0.29軟體包 下載點我 二. 解壓,初始化安裝 1,打開下載後文件所在目錄,使用解壓軟體解壓,打開文件夾!(如圖,文件路徑不要出現中文!) 2,創建my.ini文件,創建前先開啟文件尾碼名顯示防止文件格式錯誤! 3,右鍵空白處,新建>文本文檔>選中文件>重命名>全選>文件 ...
  • 前言 上文介紹了ES的各種查詢; 本文介紹如何在ES進行MySQL中的分組和聚合查詢 實現用戶輸入拼音自動補全功能 實現MySQL和ES之間的數據自動同步; 一、分組聚合 在ES中對於聚合查詢,主要分為2大類:指標(Metric)聚合 與 桶(Bucket)聚合。 指標聚合:max、min、sum等 ...
  • PostgreSQL upgrade 以升級 PostgreSQL 9.1 至 PostgreSQL 11 (跨越 9.2、9.3、9.4、9.5、9.6、10 六個大版本) 為例,本文將分享一下過去一年升級數十套 PostgreSQL 生產集群的實際經驗。 此步驟同樣適用於 PostgreSQL ...
  • SIGMOD 數據管理國際會議是資料庫領域具有最高學術地位的國際性會議,位列資料庫方向頂級會議之首。近日,騰訊雲資料庫團隊的最新研究成果入選 SIGMOD 2022 Research Full Paper(研究類長文),入選論文題目為“HUNTER: An Online Cloud Database ...
  • 前言 前面我已經搭建好了ElasticSearch服務,並完成了MySQL到ElasticSearch的數據遷移; 使用ES專門做搜索功能,打破MySQL搜索瓶頸; ElasticSearch的應用場景 資料庫欄位太多,查詢太慢,索引沒有辦法再做優化; 資料庫1個count查詢就拖死全表; MySQ ...
  • Docker容器搭建android編譯環境 1 部署容器 1.1 手動部署 1.1.1 配置docker # 安裝之前必須修改docker路徑,防止docker空間增長暴滿。 sudo ln -s /opt/docker /var/lib/docker # 安裝docker.io sudo apt- ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...