使用 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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...