OC 可变参数

5G(NR)物理层面试问答 1.什么是参数集(Numerologies)? 答:在5G中由于其频段很宽,设置了不同的子载波带宽,另根据频段、循环前缀、帧结构等组成了参数集。 2.5G中的循环前缀有几种? 答:5G网络支持5种子载波间隔,有两种循环前缀(正常和扩展)。其中:参数集2,即子载波间隔60kHz支持常规循环前缀和扩展循环前缀。 3.5G无线网络在下行(DL)和上行(UL)均使用OFDM波形? 答:5G网络下行链路(DL)和上行链路(UL)使用的波形及处理见下图: 4.5G物理层帧和子帧是多长时间? 答:5G. 阅读详情

在iPhone中涉及数据库的操作的时候,在更新操作中要传递可变参数,这些可变参数其实就是数据库语句,这样在数据库涉及的过程中,不利于数据和业务的分离,对此,我们可以对其提供的函数再包装。

首先来看看Objective-C中对可变参数函数的定义、调用,eg:

Objective-C

.h文件
 
 
 

  1.  @interface sqlHelper : NSObject 
  2.  {  
  3.  }  
  4.  -(int) executeInsertWithSql:(NSString *) statement, ...; 
  5. @end  
  6.      .m文件 
  7. -(int) executeInsertWithSql:(NSString *) statement, ... 
  8. {  
  9.     PLSqliteDatabase* dbPointer = [SqliteDataBase setUp]; 
  10.     argsArray = [[NSMutableArray alloc] init]; 
  11.     id arg; 
  12.     va_list argList; 
  13.     if(statement) 
  14.             { 
  15.                    va_start(argList,statement); 
  16.                    while (arg = va_arg(argList,id)) 
  17.                    { 
  18.                          [argsArray addObject:arg]; 
  19.                    } 
  20.                    va_end(argList); 
  21.             }  
  22.                        BOOL bResult = [dbPointer executeUpdate:statement,[argsArray objectAtIndex:0],[argsArray  objectAtIndex:1]]; 
  23.              return bResult; 
  24.           }  

C语言库文件

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

va_start(argList,statement):使参数列表指针arg_ptr指向函数参数列表中的第一个可选参数,说明:argN是位于第一个可选参数之前的固定参数,(或者说,最后一个固定参数;…之前的一个参数),函数参数列表中参数在内存中的顺序与函数声明时的顺序是一致的。如果有一va函数的声明是voidva_test(char a, char b, char c,…),则它的固定参数依次是a,b,c,最后一个固定参数argN为c,因此就是va_start(arg_ptr, c)。

  1. va_arg(argList,id):返回参数列表中指针arg_ptr所指的参数,返回类型为type,并使指针arg_ptr指向参数列表中下一个参数。 
  2. va_end(arg_ptr):清空参数列表,并置参数指针arg_ptr无效。 

在调用的时候要在参数结尾的时候加nil

  1. sqlHelper *sqlCom = [[sqlHelper alloc] init]; 
  2. [sqlCom executeInsertWithSql:@"INSERT INTO authorInfo(author,age) VALUES (?,?)",@"cheungching",@"25", nil];



oc中消息传递机制-附:对performSelector方法的扩充

各种语言都有些传递函数的方法:C语言中可以使用函数指针,C++中有函数引用、仿函数和lambda,Objective-C里也有选择器(selector)和block。
不过由于iOS SDK中的大部分API都是selector的方式,所以本文就重点讲述selector了。

Objective-C和我接触过的其他面向对象的语言不同,它强调消息传递,而非方法调用。因此你可以对一个对象传递任何消息,而不需要在编译期声名这些消息的处理方法。
很显然,既然编译期并不能确定方法的地址,那么运行期就需要自行定位了。而Objective-C runtime就是通过“id objc_msgSend(id theReceiver, SEL theSelector, ...)”这个函数来调用方法的。其中theReceiver是调用对象,theSelector则是消息名,省略号就是C语言的不定参数了。

这里的消息名是SEL类型,它被定义为struct objc_selector *。不过文档中并没有透露objc_selector是什么东西,但提供了@selector指令来生成:

SEL selector = @selector(message);

@selector是在编译期计算的,所以并不是函数调用。更进一步的测试表明,它在Mac OS X 10.6和iOS下都是一个C风格的字符串(char*):

NSLog (@"%s", (char *)selector);
你会发现结果是“message”这个消息名。

下面就写个测试类:

@interface Test : NSObject
@end

@implementation Test

- (NSString *)intToString:(NSInteger)number {
return [NSString stringWithFormat:@"%d", number];
}

- (NSString *)doubleToString:(double *)number {
return [NSString stringWithFormat:@"%f", *number];
}

- (NSString *)pointToString:(CGPoint)point {
return [NSString stringWithFormat:@"{%f, %f}", point.x, point.y];
}

- (NSString *)intsToString:(NSInteger)number1 second:(NSInteger)number2 third:(NSInteger)number3 {
return [NSString stringWithFormat:@"%d, %d, %d", number1, number2, number3];
}

- (NSString *)doublesToString:(double)number1 second:(double)number2 third:(double)number3 {
return [NSString stringWithFormat:@"%f, %f, %f", number1, number2, number3];
}

- (NSString *)combineString:(NSString *)string1 withSecond:string2 withThird:string3 {
return [NSString stringWithFormat:@"%@, %@, %@", string1, string2, string3];
}

@end
再来测试下objc_msgSend:

#import <objc/message.h>
//要使用objc_msgSend的话,就要引入这个头文件

Test *test = [[Test alloc] init];
CGPoint point = {123, 456};
NSLog(@"%@", objc_msgSend(test, @selector(pointToString:), point));
[test release];
结果是“{123.000000, 456.000000}”。而且与之前猜想的一样,下面这样调用也是可以的:
NSLog(@"%@", objc_msgSend(test, (SEL)"pointToString:", point));

看到这里你应该发现了,这种实现方式只能确定消息名和参数数目,而参数类型和返回类型就给抹杀了。所以编译器只能在编译期警告你参数类型不对,而无法阻止你传递类型错误的参数。


接下来再看看NSObject协议提供的一些传递消息的方法:
  • - (id)performSelector:(SEL)aSelector
  • - (id)performSelector:(SEL)aSelector withObject:(id)anObject
  • - (id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObject
也没有觉得很无语?为什么参数必须是对象?为什么最多只支持2个参数?

好在selector本身也不在乎参数类型,所以传个不是对象的玩意也行:
NSLog(@"%@", [test performSelector:@selector(intToString:) withObject:(id)123]);

可是double和struct就不能这样传递了,因为它们占的字节数和指针不一样。如果非要用performSelector的话,就只能修改参数类型为指针了:

- (NSString *)doubleToString:(double *)number {
return [NSString stringWithFormat:@"%f", *number];
}

double number = 123.456;
NSLog(@"%@", [test performSelector:@selector(doubleToString:) withObject:(id)(&number)]);
参数类型算是搞定了,可是要支持多个参数,还得费番气力。理想状态下,我们应该可以实现这2个方法:
@interface NSObject (extend)

- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects;
- (id)performSelector:(SEL)aSelector withParameters:(void *)firstParameter, ...;

@end
先看看前者,NSArray要求所有的元素都必须是对象,并且不能为nil,所以适用的范围仍然有限。不过你可别小看它,因为你会发现根本没法用objc_msgSend来实现,因为你在写代码时没法预知参数个数。
这时候就轮到NSInvocation登场了:
@implementation NSObject (extend)

- (id)performSelector:(SEL)aSelector withObjects:(NSArray *)objects {
NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self];
[invocation setSelector:aSelector];

NSUInteger i = 1;
for (id object in objects) {
[invocation setArgument:&object atIndex:++i];
}
[invocation invoke];

if ([signature methodReturnLength]) {
id data;
[invocation getReturnValue:&data];
return data;
}
return nil;
}

@end


NSLog(@"%@", [test performSelector:@selector(combineString:withSecond:withThird:) withObjects:[NSArray arrayWithObjects:@"1", @"2", @"3", nil]]);
这里有3点要注意的:
  1. 因为方法调用有self(调用对象)和_cmd(选择器)这2个隐含参数,因此设置参数时,索引应该从2开始。
  2. 因为参数是对象,所以必须传递指针,即&object。
  3. methodReturnLength为0时,表明返回类型是void,因此不需要获取返回值。返回值是对象的情况下,不需要我们来创建buffer。但如果是C风格的字符串、数组等类型,就需要自行malloc,并释放内存了。

再来实现第2个方法:
- (id)performSelector:(SEL)aSelector withParameters:(void *)firstParameter, ... {
NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
NSUInteger length = [signature numberOfArguments];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self];
[invocation setSelector:aSelector];

[invocation setArgument:&firstParameter atIndex:2];
va_list arg_ptr;
va_start(arg_ptr, firstParameter);
for (NSUInteger i = 3; i < length; ++i) {
void *parameter = va_arg(arg_ptr, void *);
[invocation setArgument:&parameter atIndex:i];
}
va_end(arg_ptr);

[invocation invoke];

if ([signature methodReturnLength]) {
id data;
[invocation getReturnValue:&data];
return data;
}
return nil;
}

NSLog(@"%@", [test performSelector:@selector(combineString:withSecond:withThird:) withParameters:@"1", @"2", @"3"]);

NSInteger number1 = 1, number2 = 2, number3 = 3;
NSLog(@"%@", [test performSelector:@selector(intsToString:second:third:) withParameters:number1, number2, number3]);
和前面的实现差不多,不过由于参数长度是未知的,所以用到了[signature numberOfArguments]。当然也可以把SEL转成字符串(可用NSStringFromSelector()),然后查找:的数量。
处理可变参数时用到了va_start、va_arg和va_end,熟悉C语言的一看就明白了。

不过由于不知道参数的类型,所以只能设为void *。而这个程序也报出了警告,说void *和NSInteger类型不兼容。而如果把参数换成double,那就直接报错了。遗憾的是我也不知道怎么判别一个void *指针究竟是指向C数据类型,还是指向一个Objective-C对象,所以最好是封装成Objective-C对象。如果只需要兼容C类型的话,倒是可以将setArgument的参数的&去掉,然后直接传指针进去:

NSInteger number1 = 1, number2 = 2, number3 = 3;
NSLog(@"%@", [test performSelector:@selector(intsToString:second:third:) withParameters:&number1, &number2, &number3]);

double number4 = 1.0, number5 = 2.0, number6 = 3.0;
NSLog(@"%@", [test performSelector:@selector(doublesToString:second:third:) withParameters:&number4, &number5, &number6]);
[test release];
至于NSObject类添加的performSelector:withObject:afterDelay:等方法,也可以用这种方式来支持多个参数。

接下来再说说刚才略过的_cmd,它还可以用来实现递归调用。下面就以斐波那契数列为例:

- (NSInteger)fibonacci:(NSInteger)n {
if (n > 2) {
return [self fibonacci:n - 1] + [self fibonacci:n - 2];
}
return n > 0 ? 1 : 0;
}

改成用_cmd实现就变成了这样:

return (NSInteger)[self performSelector:_cmd withObject:(id)(n - 1)] + (NSInteger)[self performSelector:_cmd withObject:(id)(n - 2)];

或者直接用objc_msgSend:

return (NSInteger)objc_msgSend(self, _cmd, n - 1) + (NSInteger)objc_msgSend(self, _cmd, n - 2);
但是每次都通过objc_msgSend来调用显得很费劲,有没有办法直接进行方法调用呢?答案是有的,这就需要用到IMP了。IMP的定义为“id (*IMP) (id, SEL, …)”,也就是一个指向方法的函数指针。

NSObject提供methodForSelector:方法来获取IMP,因此只需稍作修改就行了:

- (NSInteger)fibonacci:(NSInteger)n {
static IMP func;
if (!func) {
func = [self methodForSelector:_cmd];
}

if (n > 2) {
return (NSInteger)func(self, _cmd, n - 1) + (NSInteger)func(self, _cmd, n - 2);
}
return n > 0 ? 1 : 0;
}
现在运行时间比刚才减少了1/4,还算不错。

顺便再展现一下Objective-C强大的动态性,给Test类添加一个sum:and:方法:

NSInteger sum(id self, SEL _cmd, NSInteger number1, NSInteger number2) {
return number1 + number2;
}

class_addMethod([Test class], @selector(sum:and:), (IMP)sum, "i@:ii");
NSLog(@"%d", [test sum:1 and:2]);

class_addMethod的最后那个参数是函数的返回值和参数类型,详细内容可以参考Type Encodings文档。


AI视频生成本地搭建图文详细过程 在自己的电脑上搭建一个AI,主要用于通过自己输入的关键字来自动生成视频文案,把视频文案自动生成视频,再自动给视频配音配字幕。 阅读详情

相关推荐

利用Arcgis提取nc数据并计算区域年均温

1.下载数据。2.解压数据至不含中文的路径中。打开工具箱中的多维分析工具-创建NetCDF栅格图层,导入数据。3.打开工具箱中的数据管理工具-图层和表视图-创建栅格图层,导入数据,对输出数据进行命名,得到月均温栅格数据。4.选择工具箱-Spatial Analyst工具(空间分析工具)-地图代数-栅格计算器,计算全国一年年均温。​5.将其导出为.tif格式数据,方便后期使用。6.选择工具箱-Spatial Analyst工具(空间分析工具)-区域分析--以表格显示分区统计,得到分区域的气温数据。

什么都学亿点点的博客 7989

OC performSelector和传递参数

performSelector和直接调用方法的区别 performSelector: withObject:是在iOS中的一种方法调用方式。他可以向一个对象传递任何消息,而不需要在编译的时候声明这些方法。所以这也是runtime的一种应用方式。 所以performSelector和直接调用方法的区别就在与runtime。直接调用编译是会自动校验。如果方法不存在,那么直接调用 在编译时候就能够发现,编译器会直接报错。 但是使用performSelector的话一定是在运行时候才能发现,如果此方...

迦南的专栏 2698

华三模拟器实战:IPSec VPN穿越NAT的完整配置与调试(附常见问题排查)

本文详细解析了华三模拟器中IPSec VPN穿越NAT的完整配置与调试方法,包括基础环境准备、分步配置、ACL协调策略及高级故障排查。通过实战案例和关键参数解析,帮助网络工程师解决IPSec与NAT协同工作的典型问题,提升企业网络安全性。

weixin_33724046的博客 460

performSelector详解

一、基础用法 performSelector响应了OC语言的动态性,延迟到运行时才绑定方法。意味着可以传递一个变量选择器作为参数,向接收方发送指定的消息并返回消息的结果 [obj performSelector:@selector(play)]; [obj performSelector:@selector(play) withObject:@“xx”]; [obj performSelecto...

两滴水的博客 1201

比较器和运算放大器-2

今天继续了解输入阻抗问题,观察下图:首先考虑在我们没有接运放时,A点输出电压为6V,当接入如图所示的阻抗时电压还是6V吗?共模电阻和红框内对地电阻并联,阻值变小,相当于5k,通过计算可以求得A点电压变为4V,这样A的值发生了改变,试想一下这样的信号接入到运放的两端是不是相当于比较的两端信号发生了改变,那结果一定会发生偏离。信号源一般内阻是不确定的,一般也不是很小,如果不想改变输入阻抗,那唯一的办法就是改变运放的内阻了,比如内阻改为100K,1M等,越大越好,是不是A点电压就会无限接近6V。

weixin_42855218的博客 1311

半桥LLC谐振变换器及同步整流MATLAB仿真(二)

接上文,半桥LLC谐振变换器及同步整流MATLAB仿真

qq_55203246的博客 4328

Objective-C的消息传递机制

http://www.keakon.net/2011/08/10/Objective-C%E7%9A%84%E6%B6%88%E6%81%AF%E4%BC%A0%E9%80%92%E6%9C%BA%E5%88%B6 各种语言都有些传递函数的方法:C语言中可以使用函数指针,C++中有函数引用、仿函数和lambda,Objective-C里也有选择器(selector)和block。

qhexin的专栏 715

地图坐标系大全:常用地图坐标系详解与转换指南

地图坐标系是用于描述地图上位置的数学模型。它可以用来表示地球表面上的任意一个点,使得这个点的位置可以在地图上精确定位。不同的地图坐标系采用不同的基准面和投影方式,因此会有不同的坐标系参数,不同的坐标系之间也需要进行坐标转换。在地图制图、导航、GIS等领域中,地图坐标系是一个非常重要的概念。

weixin_39888010的博客 1万+

iOS笔记-performSelector的探索

官方文档介绍 - (id)performSelector:(SEL)aSelector; Description Sends a specified message to the receiver and returns the result of the message. The performSelector: method is equivalent to sending an aSel...

zww学习笔记 420

STM32F103C8T6飞控入门:从零开始,用HAL库和MPU6050搭建你的第一架四轴无人机

本文详细介绍了如何使用STM32F103C8T6和MPU6050模块从零开始搭建四轴无人机飞控系统。通过硬件选型、电路设计、HAL库配置、姿态解算算法实现以及PID控制等步骤,帮助创客和嵌入式爱好者快速入门飞控开发。文章还提供了实用的调试技巧和常见问题解决方案,适合想要掌握四旋翼无人机核心技术的开发者。

weixin_42524750的博客 266

【PG高可用】一 PostgreSQL高可用方案repmgr部署(非常详细)

repmgr安装部署

DBA之路的博客 7052

Socket,TCP,UDP,HTTP基本通信原理和OC版本Demo

非常通俗的例子->寄快递 TCP和UDP是传输方式—> 例如空运,轮渡,陆运 HTTP,XMPP则是数据传输格式协议 –> 寄送的东西(吃的,用的,玩的。。。) socket理解为传输层和应用层之前的抽象对象,通过对TCP/IP传世方式的封装,调用其封装好的方法就可实现通信,但是必须知道双方的IP,端口和协议–>就好比知道双方的寄件地址 什么是Socket?TCP、UDP,HTTP 底层通信都是通过

Deft_MKJing的博客 4146

Lumerical 教程,包含作者个人理解及相关设计案例 (目前共计184篇,持续更新中)

4138。

u011699626的博客 1606

OC的可变参数

OC中对数据库进行操作、输出日志的时候,常常要用到可变参数。在定义可变参数函数之前要 (1)引入头文件stdarg.h头文件; (2)用…来表示可变函数参数; (3)获取参数前需要定义一个va_list类型的变量,例如 va_list persons, (4)接下来我们就要取出可变参数:va_start(persons,可变参数前面的一个变量的变量名); ... ... f = va(pe

David2427的专栏 914

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

OC方法可变参数

OC可变参数: - (void)executeSql:(NSString *)sql,... __attribute__((sentinel)){ NSMutableString *result = [NSMutableString stringWithFormat:@"%@",sql]; va_list params;//定义一个指向个...

ZHANGBO9477的博客 370

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

OC处理可变参数的方法

Objective-C 处理可变参数 va_list 这是一个类型, 用于定义指向可变参数列表的指针变量 va_start 这是一个函数, 指定开始处理可变形参的列表, 并让指针变量指向可变形参的第一个参数 va_end 结束处理可变形参, 并释放指针 va_arg 该函数返回指针变量当前指向的参数的值, 并让指针变量指向下一个参数 - (void)t...

dimen8349的博客 411

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

OC中实现可变参数

通过哨兵参数实现,相当于nil. 1.c语言中 #import #import int addemUp(int firstNum,...) { va_list args;//指向参数的指针 int sum = firstNum; int number; va_start(args, firstNum);//firstNum是

forrhuen的专栏 404

oc 可变参数传递

- (id)initWithFrame:(CGRect)frame delegate:(id)delegate focusImageItems:(SGFocusImageItem *)firstItem, ... { self = [super initWithFrame:frame]; if (self) { NSMutableArray *imageItems

zhangping871的专栏 2290

oc 可变參数传递

- (id)initWithFrame:(CGRect)frame delegate:(id<SGFocusImageFrameDelegate>)delegate focusImageItems:(SGFocusImageItem *)firstItem, ... { self = [super initWithFrame:frame]; if (self) ...

weixin_30588827的博客 84

OC可变参数的函数实现va_start、va_end、va_list的使用

一、简介 我们经常在编程的时候看见类似这样的代码,如图1.1 图1.1 或者是这样的可变参数,如图1.2 图1.2 二、基本知识介绍 在学习如何写这种格式的函数前,先简单介绍几个常用的宏: 以下摘自:http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html

果汁的专栏 4785

oc遍历所有可变参数

- (void)functionName:(NSString*)string, ...{     va_list args;     va_start(args, string);     if (string)     {         NSString *otherString;                  while ((otherStrin

BianHuanShiZhe的专栏 638

iOS自定义警告框 OC 如何写可变参数的函数

自定义警报框先看看OC中的一些用法,一般是在初始化数组的时候, 也可以像下面deotherButtonTitles一样 ,输入一组字符串,以nil结尾,然后输入的字符串就会变成数组了,UIAlertView *alert = [[UIAlertView alloc]initWithTitle:<#(NSString *)#> message:<#(NSString *)#> delegate:<

淡暗 786
上一篇: Objective-C概述
下一篇: Mac下装mysql
focusjava
博客等级 码龄14年 127粉丝 528原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值