使用 NSProxy 实现消息转发
一、简介
在 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 类别中的方法
使用 NSProxy 实现消息转发的更多相关文章
- iOS 消息转发以及 NSProxy 实战
最后更新: 2018-01-17 一.消息派发机制-NSObject 在 iOS 开发中, 调用对象的方法就是给对象发送一个消息.了解消息的派发机制对于iOS开发来说是一个很实用且强大的工具, 下面我 ...
- iOS开发-消息转发
消息转发是OC运行时比较重要的特性,Objective-C运行时的主要的任务是负责消息分发,我们在开发中"unrecognized selector sent to instance xx& ...
- runtime消息转发机制
Objective-C 扩展了 C 语言,并加入了面向对象特性和 Smalltalk 式的消息传递机制.而这个扩展的核心是一个用 C 和 编译语言 写的 Runtime 库.它是 Objective- ...
- iOS - 消息转发处理
详细运行时基础 NSInvocation介绍 NSHipster-Swizzling Objective-C Method相关方法分析 Type Encodings Objc是OOP,所以有多态. 当 ...
- iOS 消息转发机制
这篇博客的前置知识点是 OC 的消息传递机制,如果你对此还不了解,请先学习之,再来看这篇.这篇博客我尝试用口语的方式像讲述 PPT 一样给大家讲述这个知识点. 我们来思考一个问题,如果对象在收到无法解 ...
- ios 消息转发初探
有时候服务器的接口文档上一个数据写的是string类型,这时候你就会直接把它赋值给一个label. 问题来了,有时候这个string的确是string,没有问题,有时候又是NSNumber,当然不管三 ...
- Effective Objective-C 2.0 — 第12条:理解消息转发机制
11 条讲解了对象的消息传递机制 12条讲解对象在收到无法解读的消息之后会发生什么,就会启动“消息转发”(message forwarding)机制, 若对象无法响应某个选择子,则进入消息转发流程. ...
- runtime之消息转发
前言 在上一篇文章中我们初尝了runtime的黑魔法,可以在程序编译阶段就获取到成员变量的名字,特性以及动态的给对象增加属性等等,在接下来中我们进一步了解OC的消息发送机制.如果之前没接触过runti ...
- iOS 消息转发
消息转发 delegate和protocol 类别 消息转发 当向someObject发送某消息,但runtime system在当前类和父类中都找不到对应方法的实现时,runt ...
随机推荐
- -5.C++读入一行字符串
-5.C++读入一行字符串 cin.getline(char * str, int size_t, char delim) 头文件: #include<iostream> 参数1:字符数组 ...
- C#/VB.NET 获取Excel中图片所在的行、列坐标位置
本文以C#和vb.net代码示例展示如何来获取Excel工作表中图片的坐标位置.这里的坐标位置是指图片左上角顶点所在的单元格行和列位置,横坐标即顶点所在的第几列.纵坐标即顶点所在的第几行.下面是获取图 ...
- .Net Core Razor动态菜单实现
准备 1.框架 .netcore 版本 yishaadmin开源框架 2.模板 本文模板使用adminlte3.0,文档地址https://adminlte.io/docs/3.0/ 3.菜单表 关 ...
- 史上最全Linux面试题(2020最新版)
作者:ThinkWon 链接:https://blog.csdn.net/thinkwon/article/details/104588679 导读:本文整理了最新的Linux面试题,近3万字,约10 ...
- 2020年DevOps工程师入门指南
DevOps兴起于2010年代,到现在DevOps已经在行业中拥有了一席之地,并在继续发展壮大. 有兴趣成为一名DevOps工程师吗?如果想要成为一名DevOps工程师,需要做到以下五点: 要有开发者 ...
- SpringBoot从0到0.7——第四天
SpringBoot从0到0.7--第四天 今天进行实战开发一个小项目,SpringBoot和Thymeleaf集成的小项目 因为懒得写写前端,直接找的别人的项目在它的的基础上进行配置,进行修改.gi ...
- C#/VB.NET 实现Word和ODT文档相互转换
ODT文档格式一种开放文档格式(OpenDocument Text).通常,ODT格式的文件可以使用LibreOffice Writer.MS Word或其他一些文档编辑器来打开.我们在处理文档时,可 ...
- python PDF转图片,World转PDF
软件不用续费了... PDF转World暂时没需求,有需求了再搞 Python3.9 ---------------pip3 install PyMuPdf ---------------pip3 ...
- 在Windows2003 server 64位系统上使用ArcEngine开发的WCF服务
之前大篇文章提到,ESRI说AE10.0以后已经不支持WebService的发布,经过一段时间的测试,发现目前10.2.2版本开始的WCF服务都可以正常发布,且运行正常. 先说一下之前遇到的问题,本机 ...
- 行为参数化与lambda表达式 - 读《Java 8实战》
零. 概述 第一部分:1~3章 主要讲了行为参数化和Lambda表达式 第二部分:4~7章 主要讲了流的应用,包括流与集合差异,流的操作,收集器,注的并行执行 第三部分:8~12章 主要讲了怎样用Ja ...