天天看點

Object-C中編寫省略參數的多參函數

在object-c中,我們會遇到很多像nslog這樣的函數,其中參數的個數不确定,由程式員自由控制,在初始化數組,字典等方面應用廣泛,那麼,這類的函數是如何實作的呢?我們怎麼編寫我們自己的省略參數的函數呢?當然,這不是唯一的多參函數的處理方法,你也可以通過一個字典或者數組傳遞參數。但c為我們提供的這樣的一種機制,無疑是最友善的。

va_list

c語言中定義的一個指針,用于指向目前的參數。

va_start(ap,param)

這個宏是初始化參數清單,其中第一個參數是va_list對象,第二個參數是參數清單的第一個參數。

va_arg(ap, type)

一個用于取出參數的宏,這個宏的第一個參數是va_list對象,第二個參數是要取出的參數類型。

va_end(ap)

這個宏用于關閉取參清單

在編寫我們自己的多參函數之前,明白函數的取參原理是十分重要的,首先,函數的參數是被放入我們記憶體的棧段的,而且放入的順序是從後往前放入,比如如果一個函數參數如下:

void func(int a,int b,int c,int d)

那麼傳遞參數的時候參數d先入棧,接着是c、b、a。如此這樣,在取參的時候,根據堆棧的取值原則,則取值順序為a、b、c、d。是以在原理上,隻要我們知道第一個參數的位址和每個參數的類型,我們就可以将參數都取出來。而上面介紹的幾個宏,就是幫助我們做這些的。

"..."這個符号就是我們用來實作省略參數函數的符号。例如我們模拟實作一個log函數如下:

<a href="http://my.oschina.net/u/2340880/blog/410279#">?</a>

1

2

3

4

5

6

7

8

9

10

<code>-(</code><code>void</code><code>)mylog:(nsstring *)str,...{</code><code>//省略參數的寫法</code>

<code>    </code><code>va_list</code> <code>list;</code><code>//建立一個清單指針對象</code>

<code>    </code><code>va_start</code><code>(list, str);</code><code>//進行清單的初始化,str為省略前的第一個參數,及...之前的那個參數</code>

<code>    </code><code>nsstring * temstr = str;</code>

<code>    </code><code>while</code> <code>(temstr!=nil) {</code><code>//如果不是nil,則繼續取值</code>

<code>         </code><code>nslog(@</code><code>"%@"</code><code>,temstr);</code>

<code>         </code><code>temstr = </code><code>va_arg</code><code>(list, nsstring*);</code><code>//傳回取到的值,并且讓指針指向下一個參數的位址</code>

<code>    </code><code>}</code>

<code>    </code><code>va_end</code><code>(list);</code><code>//關閉清單指針</code>

<code>}</code>

注意,調用時,我們必須在參數的最後加上nil這個判斷結束的條件:

<code>[self mylog:@</code><code>"312"</code><code>,@</code><code>"321"</code><code>, nil];</code><code>//必須有nil</code>

細心的你可能發現了,這裡的nil是我們在調用函數時手動加上的,可是系統的許多函數在我們調用時,系統直接幫我們加上了參數結尾的那個nil,例如

 nsarray * array = [nsarray arraywithobjects:(id), nil]

這是如何做到的呢?我們隻需要在函數的聲明裡加上一個宏,就可以實作這個功能,修改如下:

<code>-(</code><code>void</code><code>)mylog:(nsstring *)str,...ns_requires_nil_termination{</code><code>//這裡加上一個宏</code>

<code>    </code><code>va_list</code> <code>list;</code>

<code>    </code><code>va_start</code><code>(list, str);</code>

<code>    </code><code>while</code> <code>(temstr!=nil) {</code>

<code>         </code><code>temstr = </code><code>va_arg</code><code>(list, nsstring*);</code>

<code>    </code><code>va_end</code><code>(list);</code>

顧名思義,這個宏的作用就是在結束位置加上我們需要的nil。

繼續閱讀