13-iOS消息转发机制以及常用场景

简介: 13-iOS消息转发机制以及常用场景

通过前文objc_msgSend底层调用流程探究已经分析到消息发送的几个阶段,接着上文继续向下分析:消息转发

消息转发阶段1

动态方法解析中,如果方法没有实现,则进行消息转发,第一次转发,来到 +/- forwardingTargetForSelector: 方法,注意:实例方法来到-forwardingTargetForSelector:,类方法执行到+forwardingTargetForSelector:;如果forwardingTargetForSelector返回值不为空,找到方法,则程序正常执行,如果返回值为空,则进入消息转发的另外一个阶段;首先看下方示例代码:

@interface Cat : NSObject
@end
@implementation Cat
- (void)test {
    NSLog(@"%s", __func__);
}
@end
@interface Person : NSObject
- (void)test;
+ (void)test;
@end
@implementation Person
- (id)forwardingTargetForSelector:(SEL)aSelector {
    return [Cat new];
}
+ (id)forwardingTargetForSelector:(SEL)aSelector {
    return [super forwardingTargetForSelector:aSelector];
}
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [Person new];
        [person test];
// [Person test];
    }
    return 0;
}

打印结果:

-[Cat test]

原因是因为将消息接收者指向了Cat对象,故调用了Cat中的方法,注意类方法的调用需要在+forwardingTargetForSelector:去指定接收对象,如果不指定,则进入消息转发阶段2;

消息转发阶段2

如果上述方法不指定,则进入以下阶段,首先进入 +/- methodSignatureForSelector: 返回方法签名,如果返回为空,则直接崩溃,如果返回不为空,则进入 +/- forwardInvocation: 消息转发阶段(注意此处也要区分实例方法和类方法),看下方示例代码:

@interface Cat : NSObject
@end
@implementation Cat
- (int)test:(int)a {
    NSLog(@"收到参数: %d", a);
    return 10;
}
@end
@interface Person : NSObject
- (int)test:(int)a;
+ (void)test;
@end
@implementation Person
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSLog(@"%s", __func__);
    if (aSelector == @selector(test:)) {
        return [NSMethodSignature signatureWithObjCTypes:"i@:i"];
// return [[Cat new] methodSignatureForSelector:aSelector];
    }
    return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"%s", __func__);
    int p;
    [anInvocation getArgument:&p atIndex:2];
    NSLog(@"原始参数: %d", p);
    int a = 100; //此处将传入的参数改为了100
    [anInvocation setArgument:&a atIndex:2];
    [anInvocation invokeWithTarget:[Cat new]];
    int ret; //接收返回值
    [anInvocation getReturnValue:&ret];
    NSLog(@"返回值: %d", ret);
}
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [Person new];
        [person test:20];
// [Person test];
    }
    return 0;
}

打印结果:

-[Person methodSignatureForSelector:]
-[Person forwardInvocation:]

原始参数: 20

收到参数: 100

返回值: 10

应用场景

防止OC对象方法调用产生的崩溃

原理:通过hook NSObject中 +/- forwardingTargetForSelector: 方法,动态的创建类,并添加方法实现,为了不影响主类中原有已经实现了的消息转发流程对应方法,故先判断了原类中是否实现对应的方法,再去决定是否动态创建类并添加方法,完整代码实现请参考以下代码(仅交换了实例方法、类方法自行修改方法交换处即可):

@interface NSObject (Cate)
@end
@implementation NSObject (Cate)
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self hookMethod:[NSObject class] originSelector:@selector(forwardingTargetForSelector:) swizzledSelector:@selector(hook_forwardingTargetForSelector:)];
    });
}
+ (void)hookMethod:(Class)cls originSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector {
    Method originalMethod = class_getInstanceMethod(cls, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector);
    BOOL didAddMethod = class_addMethod(cls, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    if (didAddMethod) {
        class_replaceMethod(cls, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}
- (BOOL)isImplementation:(SEL)selector {
    return class_getInstanceMethod([NSObject class], selector) != class_getInstanceMethod([self class], selector);
}
- (id)hook_forwardingTargetForSelector:(SEL)selector {
    if ([self isImplementation:@selector(forwardingTargetForSelector:)]) {
        return [self hook_forwardingTargetForSelector:selector];
    }
    if ([self isImplementation:@selector(methodSignatureForSelector:)]) {
        return [self hook_forwardingTargetForSelector:selector];
    }
    if ([self isImplementation:@selector(forwardInvocation:)]) {
        return [self hook_forwardingTargetForSelector:selector];
    }
    NSLog(@"-[%@ %@] - 方法未实现", NSStringFromClass([self class]), NSStringFromSelector(selector));
    NSString *className = @"LTReportClass";
    Class registerClass = NSClassFromString(className);
    IMP imp = imp_implementationWithBlock(^(id _) { });
    if (!registerClass) {
        registerClass = objc_allocateClassPair([NSObject class], className.UTF8String, 0);
        objc_registerClassPair(registerClass);
    }
    class_addMethod(registerClass, selector, imp, nil);
    return [registerClass new];
}
@end
@interface Person : NSObject
@property(assign, nonatomic) NSInteger age;
- (void)test;
- (void)personMethod;
@end
@implementation Person
//@synthesize age = _age; //为age生成_age的成员变量,以及getter和setter
@dynamic age; // 系统默认不再实现getter和setter方法
- (void)test {
    NSLog(@"%s - %ld", __func__, self.age);
}
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [Person new];
        [person test];
        person.age = 10;
        [person personMethod];
    }
    return 0;
}

打印结果:

-[Person age] - 方法未实现
-[Person test] - 0
-[Person setAge:] - 方法未实现
-[Person personMethod] - 方法未实现

多代理消息分发实现

多代理的应用场景一般为,比如IM消息,打开了多个页面,然而底层消息接收器只有一个,多个页面中都需要接收到消息,使用多代理进行分发比较方便,另外跨页面传值也可以使用多代理,可以解决页面事件一层一层“成串”传递的问题;

实现原理:使用链表存储代理对象,以及当前代理分发所在的队列,然后利用消息转发,批量的根据代理对象是否可以响应selecotr,来进行协议方法的调用,核心源码实现如下(因篇幅原因,此处仅列出部分代码,完整代码请参考文末链接):

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    LTDelegateNode *curNode = self.headNode;
    while (curNode) {
        if ([curNode.delegate respondsToSelector:aSelector]) {
            NSMethodSignature *signature = [curNode.delegate methodSignatureForSelector:aSelector];
            if (signature) {
                return signature;
            }
        }
        curNode = curNode.next;
    }
    return [super methodSignatureForSelector:@selector(_avoidCrash)];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    LTDelegateNode *curNode = self.headNode;
    while (curNode) {
        if ([curNode.delegate respondsToSelector:anInvocation.selector]) {
            dispatch_async(curNode.queue, ^{
                @autoreleasepool {
                    [anInvocation invokeWithTarget:curNode.delegate];
                }
            });
        }
        curNode = curNode.next;
    }
}

调用测试代码:

[[LTElegantAgentManager sharedManager] addDelegate:self delegateQueue:dispatch_get_main_queue()];
[[LTElegantAgentManager sharedManager] removeDelegate:self];
[(id <LTAgentManager>)[LTElegantAgentManager sharedManager] test:123];
[(id <OtherDelegate>)[LTElegantAgentManager sharedManager] otherMethod];

总结

消息转发:

  • 第一阶段: +/- forwardingTargetForSelector: 可以指定消息接收者,如果返回nil,则进入下一阶段;
  • 第二阶段: +/- methodSignatureForSelector: 如果返回nil,则直接崩溃(unrecognized selector sent to instance),如果返回的有方法签名,则执行 +/- forwardInvocation:方法

消息转发使用场景:

  • 防止崩溃
  • 多代理分发
  • 利用forwardInvocation:拿到class和selector可以做一些hook工作,比如热更等

@synthesize与@dynamic修饰词的作用?

  • @dynamic系统默认不再实现getter和setter方法
  • @synthesize1. 同时重写了属性的getter和setter时,系统就不再会生成ivar,使用 @synthesize age = _age,可以来关联属性和ivar;2. 重写了只读属性的getter时,可以利用@synthesize来进行关联,否则无法访问_变量;3. 可以对@protocol和category中定义的属性,利用@synthesize来进行关联;4. 重载的属性(继承),当在子类中继承了父类的属性,必须使用@synthesize来手动合成ivar;

源码地址:https://github.com/gltwy/public

本文部分内容来可能来源于网络,发布的内容如果侵犯了您的权益,请联系我们尽快删除!

相关文章
|
存储 缓存 算法
iOS 常见触发离屏渲染场景及优化方案总结
iOS 常见触发离屏渲染场景及优化方案总结
903 0
iOS 常见触发离屏渲染场景及优化方案总结
|
1月前
|
存储 安全 Android开发
探索Android与iOS的隐私保护机制
在数字化时代,移动设备已成为我们生活的一部分,而隐私安全是用户最为关注的问题之一。本文将深入探讨Android和iOS两大主流操作系统在隐私保护方面的策略和实现方式,分析它们各自的优势和不足,以及如何更好地保护用户的隐私。
|
29天前
|
Linux Android开发 iOS开发
深入探索Android与iOS的多任务处理机制
在移动操作系统领域,Android和iOS各有千秋,尤其在多任务处理上展现出不同的设计理念和技术实现。本文将深入剖析两大平台在后台管理、资源分配及用户体验方面的策略差异,揭示它们如何平衡性能与电池寿命,为用户带来流畅而高效的操作体验。通过对比分析,我们不仅能够更好地理解各自系统的工作机制,还能为开发者优化应用提供参考。
|
1月前
|
存储 安全 算法
深入探索iOS系统安全机制:保护用户隐私的前沿技术
本文旨在探讨苹果公司在其广受欢迎的iOS操作系统中实施的先进安全措施,这些措施如何共同作用以保护用户的隐私和数据安全。我们将深入了解iOS的安全架构,包括其硬件和软件层面的创新,以及苹果如何通过持续的软件更新来应对新兴的安全威胁。此外,我们还将讨论iOS系统中的一些关键安全功能,如Face ID、加密技术和沙箱环境,以及它们如何帮助防止未经授权的访问和数据泄露。
|
1月前
|
安全 数据安全/隐私保护 Android开发
深入探索iOS系统安全机制:从基础到高级
本文旨在全面解析iOS操作系统的安全特性,从基础的权限管理到高级的加密技术,揭示苹果如何构建一个既开放又安全的移动平台。我们将通过实例和分析,探讨iOS系统如何保护用户数据免受恶意软件、网络攻击的威胁,并对比Android系统在安全性方面的差异。
|
2月前
|
存储 安全 数据安全/隐私保护
探索安卓与iOS的隐私保护机制####
【10月更文挑战第15天】 本文深入剖析了安卓和iOS两大操作系统在隐私保护方面的策略与技术实现,旨在揭示两者如何通过不同的技术手段来保障用户数据的安全与隐私。文章将逐一探讨各自的隐私控制功能、加密措施以及用户权限管理,为读者提供一个全面而深入的理解。 ####
78 1
|
5月前
|
调度 Swift Android开发
苹果iOS新手开发之Swift中的并发任务和消息机制
Swift的消息机制类似Android的Handler,实现任务调度有三种方式: 1. **Grand Central Dispatch (GCD)**:使用`DispatchQueue`在主线程或后台线程执行任务。 2. **OperationQueue**:提供高级接口管理`Operation`对象。 3. **RunLoop**:处理事件如输入源、计时器,类似Android的`Looper`和`Handler`。 **示例**: - GCD:在不同线程执行代码块。 - OperationQueue:创建操作并执行。 - RunLoop:用Timer添加到RunLoop中。
109 2
|
6月前
|
安全 算法 数据安全/隐私保护
探索iOS与Android的隐私保护机制
【6月更文挑战第5天】在数字时代,隐私保护已成为用户最关心的问题之一。iOS和Android作为两大主流操作系统,各自发展出了独特的隐私保护技术。本文将深入探讨这两个平台在隐私保护方面的策略、技术和挑战。
165 3
|
API iOS开发
iOS 沙盒机制
iOS 沙盒机制
155 0
|
JSON 搜索推荐 API
盒马 iOS Live Activity &“灵动岛”配送场景实践
盒马 iOS Live Activity &“灵动岛”配送场景实践
2703 0
盒马 iOS Live Activity &“灵动岛”配送场景实践