標籤:
1.前言
相信接觸過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這個很類似。
那麼,如何在自己寫的函數中實現可變參數呢?
2.實現
要實現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;}
效果
3.總結
主要是通過迴圈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) ,將指標置為無效 |
4.原理
參數在堆棧中分布,位置
在進程中,堆棧地址是從高到低分配的.當執行一個函數的時候,將參數列表入棧,壓入堆棧的高地址部分,然後入棧函數的返回地址,接著入棧函數的執行代碼,這個入棧過程,堆棧地址不斷遞減,一些駭客就是在堆棧中修改函數返回地址,執行自己的代碼來達到執行自己插入的程式碼片段的目的。
總之,函數在堆棧中的分布情況是:地址從高到低,依次是:函數參數列表,函數返回地址,函數執行程式碼片段。
堆棧中,各個函數的分布情況是倒序的.即最後一個參數在列表中地址最高部分,第一個參數在列表地址的最低部分.參數在堆棧中的分布情況如下:
最後一個參數
倒數第二個參數
...
第一個參數
函數返回地址
函數程式碼片段
轉載請註明:特維部落格 » Objective-C可變參數的函數實現
Objective-C可變參數的函數實現