【iOS】SDK开发之自定义反射实现不定参数、不定数据类型方法的调用

前言

一般的开发者对“反射”这个词很陌生,事实上他们自己用过了都不知道。但是在做聚合SDK开发当中,用得最多的就是反射了。
现在我想实现反射调用该方法:

+ (NSString *)test:(id)test p1:(NSString *)p1 p2:(unsigned int)p2 p3:(double)p3 p4:(CGSize)p4 p5:(NSNull *)p5 p6:(void(^)(void))p6 p7:(BOOL)p7 p8:(char)p8 p9:(char *)p9 p10:(YY)p10 p11:(SEL)p11 p12:(void **)p12 p13:(void ***)p13 p14:(Class)p14 p15:(IMP)p15; // YY是自定义结构体;p9是C字符串

然而遗憾的是,系统提供的反射最多只能带2个参数,而且参数只能是对象,但事实上并不是所有的参数都是对象的(比如 void **)。
虽然网上有很多的资料,但我都试过,都不咋地,总有缺陷。没办法,只能自己动手撸一个了。
实现多参数反射目前我只知道有2种方式:

  • runtime
  • NSInvocation

实现方式的选择

虽然runtime号称是万能的,但事实上在64位里并不能直接使用objc_msgSend,必须强制转换成对应类型。可是,既然是不定参数、不定数据类型,所以我们根本无法去确定类型,换句话说,objc_msgSend是不可行的。
所以我们只能选择NSInvocation了。

一些坑和需要解决的问题

众所周知,OC是基于C的,事实上OC只是C的一层封装,所以我们使用的东西也是C的东西。
而不定参数的实现,也是用C的va_list。但va_list在取值之前是需要知道数据类型的,因为每次取值都要给个数据类型,然后va_list会根据该类型的长度去截取数据,如果给的数据类型不对,那截取的长度也会不对,就会导致之后的数据都不对了。
当然有人会说,那全部转成一个类型不就好了?
我开始也是这么想的,事实上网上大多数人的做法也是这样子。
但是,并不是所有类型都能转的。比如全部用对象,那么void **和C字符串怎么转呢?也有人会说C字符串包装成NSString啊,嗯,这是没问题的,可是你怎么还原呢?人家参数需要的是C字符串啊,你给个OC字符串??
另外,void *是一种类型,void **也是一种类型,char *也不一样,怎么处理呢?
对于结构体也是,每个自定义的结构体的类型都是不一致的。
此外,NSInvocation是不支持自动解包的。也就是说,如果你传了NSNumber对象当做参数,而调用的方法需求参数是int,你不自己解包成int的话就会数据不对。
所以,当下我们需要解决的问题就是数据类型的问题。

网上的一些实现方法

  • 网上很多方法都是通过把所有参数加入到一个数组里,然后遍历数组把参数直接写到NSInvocation,但我之前说了,并不是所有的类型都能转成对象的,而且NSInvocation不支持自动解包,这样子当遇到基本数据类型参数的时候,势必会出错。
  • 有些人使用不定参数来,然后全部转成对象,实际上和上面的大同小异。
  • 另外我还发现了挺多人为了调用方便,用分类来实现。但是做SDK最怕的就是重名了,分类的话,如果重名导致的问题还不好搞。因此,我封装为类,如果有命名问题,只需要简单的改个前缀即可。

我的实现思路

我观察了NSString的声明,得到了一些启发:

+ (instancetype)stringWithFormat:(NSString *)format, ...

在NSString的创建方法里,系统使用了format来提供信息,也就是说,当匹配到%d的时候,就会认为是int,这个时候在va_list里取值int不就好了?
不过我并不打算这样子做,因为不单自己搞得麻烦,调用者也麻烦,每次都要写一串的%d%s什么的。
那么,有没有别的方法可以预知所需要的参数类型呢?
当然有了,NSMethodSignature里就有一个getArgumentTypeAtIndex:方法,该方法可以获取到index位置参数的数据类型。到了这里,数据类型的问题我们就解决了一半了,也不需要想着怎么包装成对象了,直接按照实际类型传参岂不是更舒服?


接下来我们来解决void *、void **的问题。
对于指针类型,从一开始我就掉进了一个坑。众所周知,指针是一种派生类型,只要在某种类型后面加个*号,就能派生出该类型的指针。换句话说,指针的类型的无数的,所以我们不可能是判断无数的类型,但是,指针的储存空间是固定的,也就是说我们取值只需要取指针的长度即可。


剩下的就是结构体的问题了。
结构体是基本数据类型的一个包装,能实现无数的结构体类型。所以除了内置的常用结构体,其它自定义的是无法识别的,因此,可以通过包装成NAValue解决,而取值的时候,使用指针去取值就好了。

获取返回值的问题

事实上返回值的类型也是各种各种的。但是我们并不需要判断类型,我们只需要给一个指针就可以了,因为- (void)getReturnValue:(void *)retLoc;这个方法只是简单的把指针指向返回值的地址而已。
不过需要注意的是,在ARC环境下,如果不使用__weak或者__unsafe_unretained修饰对象的话就会崩溃,因为从invocation获取的返回值并没有为我们增加引用计数,而我们定义变量时,默认就是__strong类型的,ARC就会假设该内存块已被retain(实际没有),在出了定义域时就会release一次,导致返回值已经是销毁状态了,从而导致崩溃。

当使用__weak时控制台会打印该错误:Attempted to unregister unknown __weak variable at 0x7ffee9909f70. This is probably incorrect use of objc_storeWeak() and objc_loadWeak(). Break on objc_weak_error to debug.
该错误可以无视,上面的地址0x7ffee9909f70其实就是我们给getReturnValue参数的地址,对此感兴趣的同学可以用符号捕获来调试(全局异常断点是没用的)。即在Symbolic BreakpointSymbolic里输入objc_weak_error即可调试。


代码就不贴了,这里只说实现的思路,想看具体的实现请到GitHub上下载源码。
获取源码请点击这里:ZTReflectionDemo
造个轮子不易,对你有用的请给个星星,谢谢了

iOS OC Swift Flutter开发群 139322447

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • OC语言基础 1.类与对象 类方法 OC的类方法只有2种:静态方法和实例方法两种 在OC中,只要方法声明在@int...
    奇异果好补阅读 4,245评论 0 11
  • 前天,富士康的同事给我打电话,相互之间调侃各自目前的状况。通过他我了解到,成都富士康目前在做华为手机的订单,这间接...
    反馈精灵阅读 451评论 0 1
  • 世界就是一个无穷无尽的纠葛场,人,事,物,都处在其无限的纠缠中,看得到或看不到……
    幽兰达人阅读 173评论 0 0
  • 一、目的地:广州MAG环球魔幻世界 交通:1.地铁APM线“黄埔大道”站下,出站后乘坐扶手电梯上一层,转左往b区方...
    kokojunua阅读 708评论 0 0
  • 学习不顺,甜品暖心。仙草南路的芋圆,想起来就自动分泌唾液and不由自主的想做咀嚼运动(´・_・`)实在的芋头的添加...
    火星民政局阅读 290评论 0 0