热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

iOSperformSelector多参数传递解决方案以及objc_msgSend的使用注意事项

1.iOSperformSelector多参数传递解决方案以及objc_msgSend的使用注意事项 https:blog.csdn.netglt_codearticledetai

1.iOS performSelector多参数传递解决方案以及objc_msgSend的使用注意事项

 

https://blog.csdn.net/glt_code/article/details/77584683

 

iOS performSelector多参数传递解决方案


以及objc_msgSend的使用注意事项

 


iOS中使用performSelector:withObject:withObject:方法最多传递两个参数

[self performSelectorOnMainThread:<#(nonnull SEL)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#>]

 


解决方案:1. 使用NSInvocation进行消息转发从而实现对performSelector的多参数传递

2. 使用runtime中的objc_msgSend进行消息的发送

 

方案一:

以下是对NSObject类的扩展方法:

NS_REQUIRES_NIL_TERMINATION:是对多参数传递值得一个宏

va_list args:定义一个指向个数可变的参数列表指针;

va_start(args,object):object为第一个参数,也就是最右边的已知参数,这里就是获取第一个可选参数的地址.使参数列表指针指向函数参数列表中的第一个可选参数,函数参数列表中参数在内存中的顺序与函数声明时的顺序是一致的。

va_arg(args,id):返回参数列表中指针所指的参数,返回类型为id,并使参数指针指向参数列表中下一个参数。

va_end(args):清空参数列表,并置参数指针args无效。

其他说明见代码注释 Object分类

-(id)glt_performSelector:(SEL)selector withObject:(id)object,...NS_REQUIRES_NIL_TERMINATION;
{
//根据类名以及SEL 获取方法签名的实例
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];
if (signature == nil) {
NSLog(@"--- 使用实例方法调用 为nil ---");
signature = [self methodSignatureForSelector:selector];
if (signature == nil) {
NSLog(@"使用类方法调用 也为nil, 此时return");
return nil;
}
}
//NSInvocation是一个消息调用类,它包含了所有OC消息的成分:target、selector、参数以及返回值。
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self;
invocation.selector = selector;
NSUInteger argCount = signature.numberOfArguments;
// 参数必须从第2个索引开始,因为前两个已经被target和selector使用
argCount = argCount > 2 ? argCount - 2 : 0;
NSMutableArray *objs = [NSMutableArray arrayWithCapacity:0];
if (object) {
[objs addObject:object];
va_list args;
va_start(args, object);
while ((object = va_arg(args, id))){
[objs addObject:object];
}
va_end(args);
}
if (objs.count != argCount){
NSLog(@"--- objs.count != argCount! please check it! ---");
return nil;
}
//设置参数列表
for (NSInteger i = 0; i id obj = objs[i];
if ([obj isKindOfClass:[NSNull class]]) {
continue;
}
[invocation setArgument:&obj atIndex:i+2];
}
[invocation invoke];
//获取返回值
id returnValue = nil;
if (signature.methodReturnLength != 0 && signature.methodReturnLength) {
[invocation getReturnValue:&signature];
}
return returnValue;
}


 


shareExtension调用

 UIResponder* respOnder= self;

    NSString *urlStr = [NSString stringWithFormat:@"scheme://host/path%@",url];

   while ((respOnder= [responder nextResponder]) != nil) {

       if ([responder respondsToSelector:@selector(openURL:)] == YES && [responder isKindOfClass:[UIApplication class]]) {

          [responder performSelector:@selector(openURL:) withObject:[NSURL URLWithString:urlStr]];

      }else if([responder isKindOfClass:[UIApplication class]] && [responder respondsToSelector:@selector(openURL: options: completionHandler:)]) {

//           [responder performSelector:@selector(openURL: options: completionHandler:) withObject:[NSURL URLWithString:urlStr] withObject:@{}];

          void (^block)(void) = ^{

                  

              };

          [responder glt_performSelector:@selector(openURL: options: completionHandler:) withObject:[NSURL URLWithString:urlStr], @{},block,nil];

       }

    }

    [self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];


注意:在Swift中没有NSMethodSignature和NSInvocation方法,暂时只能通过桥接解决,Swift中给出了这样一句:
NS_SWIFT_UNAVAILABLE("NSInvocation and related APIs not available")
如果在Swift中谁有好的方法可以扩展,请求大神告知!不胜感激!

方案二:

objc_msgSend :

objc_msgSend(<#id self#>, <#SEL op, ...#>)
概念:其实每个类中都有一张方法列表去存储这个类中有的方法,当发出objc_msgSend方法时候,就会顺着列表去找这个方法是否存在,如果不存在,则向该类的父类继续查找,直到找到位置。如果始终没有找到方法,那么就会进入到消息转发机制;objc_msgSend被分为2个过程:1)在cache中寻找SEL。2)在MethodTable寻找SEL。

具体使用介绍:

在一个列中有以下两个方法:

@implementation CustomClass

-(void)fun
{
NSLog(@"fun");
}

-(void)eat:(NSString *)food say:(NSString *)some
{
NSLog(@"%@ %@",food, some);
}

@end

当我们使用的时候,使用objc_msgSend进行调用,如下:这样便调用了类中的fun方法

TestClass *cls = [[TestClass alloc] init];

objc_msgSend(cls, @selector(fun)); //错误写法(arm64崩溃偶尔发生)

((void (*)(id, SEL))objc_msgSend)(cls, @selector(fun)); //正确写法

//具体原因见下面解释 objc_msgSend arm64 崩溃问题


使用注意1:使用objc_msgSend crash解决方案

 

 

使用注意2:objc_msgSend arm64 崩溃问题
之前一直用objc_msgSend,但是没注意apple的文档提示,所以突然objc_msgSend crash了。
按照文档 64-Bit Transition Guide for Cocoa Touch 给出了以下代码片段:

- (int) doSomething:(int) x { ... }
- (void) doSomethingElse {
int (*action)(id, SEL, int) = (int (*)(id, SEL, int)) objc_msgSend;
action(self, @selector(doSomething:), 0);
}
所以必须先定义原型才可以使用,这样才不会发生崩溃,调用的时候则如下:
void (*glt_msgsend)(id, SEL, NSString *, NSString *) = (void (*)(id, SEL, NSString *, NSString *))objc_msgSend;

glt_msgsend(cls, @selector(eat:say:), @"123", @"456");
————————————————
版权声明:本文为CSDN博主「高刘通」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/glt_code/article/details/77584683

 



推荐阅读
  • 本文深入解析了JDK 8中HashMap的源代码,重点探讨了put方法的工作机制及其内部参数的设定原理。HashMap允许键和值为null,但键为null的情况只能出现一次,因为null键在内部通过索引0进行存储。文章详细分析了capacity(容量)、size(大小)、loadFactor(加载因子)以及红黑树转换阈值的设定原则,帮助读者更好地理解HashMap的高效实现和性能优化策略。 ... [详细]
  • 在软件开发过程中,经常需要将多个项目或模块进行集成和调试,尤其是当项目依赖于第三方开源库(如Cordova、CocoaPods)时。本文介绍了如何在Xcode中高效地进行多项目联合调试,分享了一些实用的技巧和最佳实践,帮助开发者解决常见的调试难题,提高开发效率。 ... [详细]
  • 本文介绍了一种自定义的Android圆形进度条视图,支持在进度条上显示数字,并在圆心位置展示文字内容。通过自定义绘图和组件组合的方式实现,详细展示了自定义View的开发流程和关键技术点。示例代码和效果展示将在文章末尾提供。 ... [详细]
  • 在Cisco IOS XR系统中,存在提供服务的服务器和使用这些服务的客户端。本文深入探讨了进程与线程状态转换机制,分析了其在系统性能优化中的关键作用,并提出了改进措施,以提高系统的响应速度和资源利用率。通过详细研究状态转换的各个环节,本文为开发人员和系统管理员提供了实用的指导,旨在提升整体系统效率和稳定性。 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 本文详细介绍了一种利用 ESP8266 01S 模块构建 Web 服务器的成功实践方案。通过具体的代码示例和详细的步骤说明,帮助读者快速掌握该模块的使用方法。在疫情期间,作者重新审视并研究了这一未被充分利用的模块,最终成功实现了 Web 服务器的功能。本文不仅提供了完整的代码实现,还涵盖了调试过程中遇到的常见问题及其解决方法,为初学者提供了宝贵的参考。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • [转]doc,ppt,xls文件格式转PDF格式http:blog.csdn.netlee353086articledetails7920355确实好用。需要注意的是#import ... [详细]
  • 实验九:使用SharedPreferences存储简单数据
    本实验旨在帮助学生理解和掌握使用SharedPreferences存储和读取简单数据的方法,包括程序参数和用户选项。 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
  • 如何将Python与Excel高效结合:常用操作技巧解析
    本文深入探讨了如何将Python与Excel高效结合,涵盖了一系列实用的操作技巧。文章内容详尽,步骤清晰,注重细节处理,旨在帮助读者掌握Python与Excel之间的无缝对接方法,提升数据处理效率。 ... [详细]
  • Flowable 流程图路径与节点展示:已执行节点高亮红色标记,增强可视化效果
    在Flowable流程图中,通常仅显示当前节点,而路径则需自行获取。特别是在多次驳回的情况下,节点可能会出现混乱。本文重点探讨了如何准确地展示流程图效果,包括已结束的流程和正在执行的流程。具体实现方法包括生成带有高亮红色标记的图片,以增强可视化效果,确保用户能够清晰地了解每个节点的状态。 ... [详细]
  • 本文详细解析了使用C++实现的键盘输入记录程序的源代码,该程序在Windows应用程序开发中具有很高的实用价值。键盘记录功能不仅在远程控制软件中广泛应用,还为开发者提供了强大的调试和监控工具。通过具体实例,本文深入探讨了C++键盘记录程序的设计与实现,适合需要相关技术的开发者参考。 ... [详细]
  • Squaretest:自动生成功能测试代码的高效插件
    本文将介绍一款名为Squaretest的高效插件,该工具能够自动生成功能测试代码。使用这款插件的主要原因是公司近期加强了代码质量的管控,对各项目进行了严格的单元测试评估。Squaretest不仅提高了测试代码的生成效率,还显著提升了代码的质量和可靠性。 ... [详细]
author-avatar
Lanboream
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有