相信接觸過OC的對NSLog都很熟悉,細心查看NSLog的原始定義,會發現,他的原型如下:
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
路徑在:OS X version/Frameworks/Foundation/NSObjCRuntime.h
注意到參數最后的...,這里是可變參數。這樣,在調用時就可以根據需要傳入相應個數的參數了。
PS:其實在C#中也有params指定可變參數,跟OC這個很類似。
那么,如何在自己寫的函數中實現可變參數呢?
要實現OC中的可變參數,需要幾個宏定義va_list、va_start、va_arg、va_end,先實現效果,以無限個整數相加為例:
RandomArgs.h
#import <Foundation/Foundation.h>@interface RandomArgs : NSObject-(int)add:(int)item,...;@end
RandomArgs.m
#import "RandomArgs.h"@implementation RandomArgs-(int)add:(int)item,...{ va_list list; va_start(list, item); int result=0; NSLog(@"第一個參數:%d",item); result+=item; int arg; while ((arg=va_arg(list,int))) { NSLog(@"當前參數:%d",arg); result+=arg; } va_end(list); return result;}@end
main.m
#import <Foundation/Foundation.h>#import "RandomArgs.h"int main(int argc, const char * argv[]) { @autoreleasepool { RandomArgs* rand=[[RandomArgs alloc]init]; int result=[rand add:4,5,6,nil]; NSLog(@"結果:%d",result); } return 0;}
效果
主要是通過循環va_arg來獲取,但是要注意的是,第一個參數必須是固定的,循環里面只能獲取第二個參數以后的參數。
主要參數解釋:
參數 | 路徑、原型、解釋 |
---|---|
va_list | OS X version/usr/include/sys/_types/_va_list.h/typedef ,__darwin_va_list va_list; ,這個變量是指向參數地址的指針,因為得到參數的地址之后,再結合參數的類型,才能得到參數的值。 |
va_start | stdarg.h ,#define va_start(ap, param) __builtin_va_start(ap, param) ,第一個可選參數地址 |
va_arg | stdarg.h ,#define va_arg(ap, type) __builtin_va_arg(ap, type) ,下一個參數地址 |
va_end | stdarg.h ,#define va_end(ap) __builtin_va_end(ap) ,將指針置為無效 |
參數在堆棧中分布,位置
在進程中,堆棧地址是從高到低分配的.當執行一個函數的時候,將參數列表入棧,壓入堆棧的高地址部分,然后入棧函數的返回地址,接著入棧函數的執行代碼,這個入棧過程,堆棧地址不斷遞減,一些黑客就是在堆棧中修改函數返回地址,執行自己的代碼來達到執行自己插入的代碼段的目的。
總之,函數在堆棧中的分布情況是:地址從高到低,依次是:函數參數列表,函數返回地址,函數執行代碼段。
堆棧中,各個函數的分布情況是倒序的.即最后一個參數在列表中地址最高部分,第一個參數在列表地址的最低部分.參數在堆棧中的分布情況如下:
最后一個參數
倒數第二個參數
...
第一個參數
函數返回地址
函數代碼段
轉載請注明:特維博客 » Objective-C可變參數的函數實現
新聞熱點
疑難解答