說明
模仿系統(tǒng)的快速生成字典的方法NSDictionaryOfVariableBindings并過濾掉值為nil
的對象或內(nèi)容全為空格字符串欠动。
推薦適用場合:網(wǎng)絡(luò)請求生成參數(shù)字典徐块,無需判空绞铃。<BR>
其他創(chuàng)建字典的地方也可以使用师坎,注意此方法會過濾掉全為空格及@""
字符串蟀悦,如不需要可自行修改沟于。
使用
NSString *testStr = @"test";
NSString *nilStr = nil;
NSString *blankStr = @" ";
NSNumber *integerNumber = @124;
NSNumber *NONumber = @(NO);
NSNumber *zeroNumer = @0;
People *peo = [[People alloc] init];
People *peo_nil = nil;
NSArray *array = @[];
NSDictionary *dic = @{};
NSDictionary *param = ZXDictionaryOfVariableBindings(testStr, nil, nilStr, blankStr, integerNumber, NONumber,zeroNumer, peo, peo_nil, array, dic);
param
值:
{
NONumber = 0;
array = (
);
dic = {
};
integerNumber = 124;
peo = "<People: 0x1c0452510>";
testStr = test;
zeroNumer = 0;
}
可以看到傳入的參數(shù)可為nil
不會崩潰噪窘,且生成字典后自動去除了值為nil
和全是空格的NSString
。
源碼
//.h
#define ZXDictionaryOfVariableBindings(...) [Tool _ZXDictionaryOfVariableBindings:@"" # __VA_ARGS__, __VA_ARGS__]
/**
模仿系統(tǒng)的對象生成字典的宏定義:NSDictionaryOfVariableBindings(...)
if v1 = @"something"; v2 = nil; v3 = @"something"; v4 = @"";
ZXDictionaryOfVariableBindings(v1, v2, v3) is equivalent to [NSDictionary dictionaryWithObjectsAndKeys:v1, @"v1", v3, @"v3", nil];
并且參數(shù)的值可為nil,@"", 會自動去除值為nil, @"", @" "等的對象
*/
+ (NSDictionary *)_ZXDictionaryOfVariableBindings:(NSString *)firstArg, ...;
//.m
+ (NSDictionary *)_ZXDictionaryOfVariableBindings:(NSString *)firstArg, ... {
firstArg = [firstArg stringByReplacingOccurrencesOfString:@" " withString:@""];
NSArray *keys = [firstArg componentsSeparatedByString:@","];
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:keys.count];
va_list list;
if (firstArg) {
va_start(list, firstArg);
id arg;
for (NSString *key in keys) {
arg = va_arg(list, id);
if (!arg || [arg isKindOfClass:[NSNull class]]) {
continue;
}
if ([arg isKindOfClass:[NSString class]]) {
if ([[arg stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] != 0) {
[dic setObject:arg forKey:key];
}
} else {
[dic setObject:arg forKey:key];
}
}
va_end(list);
}
return dic;
}
原理
廢話可不看↓
在開發(fā)經(jīng)常需要創(chuàng)建字典對象泞遗,但是創(chuàng)建字典時存入的對象值不能為nil
衰猛,否則會崩潰。
尤其是在進(jìn)行網(wǎng)絡(luò)請求時刹孔,更是經(jīng)常需要對存入字典的對象判空,于是作為一個能少寫一行代碼絕不多寫一個字母的懶癌晚期患者,就想要是創(chuàng)建字典時能自動對傳入的對象判空并去除空值對象多好髓霞。
于是我就研究了系統(tǒng)的字典快捷創(chuàng)建方法NSDictionaryOfVariableBindings(...)
卦睹,發(fā)現(xiàn)要解決此需求需要了解宏定義的使用,可變參數(shù)的使用方库。
宏定義
NSDictionaryOfVariableBindings初始化字典
創(chuàng)建字典的方法大家都比較熟悉结序,這里就不再說。
但是有一個根據(jù)對象名稱創(chuàng)建字典的方法很方便纵潦,這里要說一下:
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
這個方法是使用Autolayout時經(jīng)常使用的一個宏徐鹤,這個宏可以生成一個變量名到變量值映射的Dictionary
。具體使用很簡單邀层,不再細(xì)說返敬,不知道的同學(xué)推薦你們可以使用此方法創(chuàng)建字典,很方便寥院。
使用此宏依舊需要對字典中的對象判空劲赠,防止傳入空值崩潰,要想自動去除值為nil
的對象秸谢,要參數(shù)傳入之后入手凛澎,下面就來改造此宏定義自動去除傳入的空值。
宏定義中的參數(shù)含義
要想改造系統(tǒng)創(chuàng)建字典的方法估蹄,首先要知道系統(tǒng)創(chuàng)建字典的原理塑煎。
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
中有3個參數(shù),下面先搞清楚這三個參數(shù)的含義,也就知道了該方法的原理臭蚁。
@"" # __VA_ARGS__
中間#
的作用:單個井號的作用是字符串化最铁,將后面的宏參數(shù) 用雙引號引起來,轉(zhuǎn)為一個C字符串刊棕。如:
define GET_NAME(X) #X
int a = 0;
NSLog(@”%s”,GET_NAME(a)); //output: “a”
NSLog(@”%s”,GET_NAME(a+3)); //output: “a+3”
將會得到以下輸出:
a
a+3
可以看出#
炭晒,將參數(shù)原樣轉(zhuǎn)換成字符串常量,如果參數(shù)是一個表達(dá)式甥角,那么輸出這個表達(dá)式的原樣字符串常量网严。
前面的@
是objc
的編譯符號,不屬于宏操作的對象嗤无。如果有宏定義@#expression
,出來后就是一個內(nèi)容是expression
的內(nèi)容的NSString
震束。
__VA_ARGS__
表示的是宏定義中的...
中的所有參數(shù)〉狈福可變參數(shù)將被統(tǒng)一處理垢村,在這里展開的時候編譯器會將__VA_ARGS__
直接替換為輸入中的所有參數(shù)。
回頭再看看NSDictionaryOfVariableBindings的定義:
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
如果這樣生成兩個button
的映射:
NSDictionaryOfVariableBindings(button1, button2);
那么預(yù)編譯時就會轉(zhuǎn)換成:
_NSDictionaryOfVariableBindings(@"" "button1, button2", button1, button2, nil);
由于兩個常量字符串放在一起就是字符串常量串聯(lián)嚎卫,將變成兩個字符串常量組合在一起的字符串常量嘉栓,也就是上面是一個空字符串@""
和"button1, button2"
串聯(lián),所以上面的代碼等價于:
_NSDictionaryOfVariableBindings(@"button1, button2", button1, button2, nil);
那么_NSDictionaryOfVariableBindings
函數(shù)就可以將它的第一個參數(shù)按逗號,分割開作為key
侵佃,后面就是各個key
對應(yīng)的值了麻昼。因此這段代碼就創(chuàng)建了一個內(nèi)容為{ @"button1" = button1, @"button2" = button2 }
的Dictionary
。
可變參數(shù)
再回看宏定義NSDictionaryOfVariableBindings(...)
,其中...
即可變參數(shù)馋辈,其實可變參數(shù)并不少見抚芦,比如:
// 日志輸出
NSLog(NSString *format, ...);
// NSString實例的創(chuàng)建
+(instancetype)stringWithFormat:(NSString *)format, ...;
// NSArray實例的創(chuàng)建
+(instancetype)arrayWithObjects:(ObjectType)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
可變參數(shù)解析也很簡單,直接拿封裝的源碼舉例迈螟,注釋寫的很清楚了:
//.m
+ (NSDictionary *)_ZXDictionaryOfVariableBindings:(NSString *)firstArg, ... {
// 取出第一個參數(shù)
firstArg = [firstArg stringByReplacingOccurrencesOfString:@" " withString:@""];// 去除空格
NSArray *keys = [firstArg componentsSeparatedByString:@","];
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:keys.count];
// 定義一個指向個數(shù)可變的參數(shù)列表指針
va_list list;
if (firstArg) {
// 初始化變量剛定義的va_list變量叉抡,這個宏的第二個參數(shù)是第一個可變參數(shù)的前一個參數(shù),是一個固定的參數(shù)
va_start(list, firstArg);
// 用于存放取出的參數(shù),C語言的字符指針, 指針根據(jù)offset來指向需要的參數(shù),從而讀取參數(shù)
id arg;
for (NSString *key in keys) {
// 遍歷全部參數(shù) va_arg返回可變的參數(shù)(va_arg的第二個參數(shù)是你要返回的參數(shù)的類型)
arg = va_arg(list, id);
if (!arg || [arg isKindOfClass:[NSNull class]]) {
continue;
}
if ([arg isKindOfClass:[NSString class]]) {
if ([[arg stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] != 0) {
[dic setObject:arg forKey:key];
}
} else {
[dic setObject:arg forKey:key];
}
}
// 清空參數(shù)列表答毫,并置參數(shù)指針args無效
va_end(list);
}
return dic;
}
其中重點是不像其他字典初始化方法以nil
作為傳參結(jié)束判斷的標(biāo)準(zhǔn)褥民。
- 以
nil
作為傳參結(jié)束的變參方法:需要在定義方法時標(biāo)識NS_REQUIRES_NIL_TERMINATION
,則初始化時未尾一定要加上nil
烙常,如:
+ (instancetype)arrayWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
因為這樣的方法中沒有提供對參數(shù)個數(shù)的檢測轴捎,需要判斷參數(shù)為nil
時結(jié)束遍歷,銷毀指針偏移量蚕脏,否則會崩潰侦副。
- 另外還有不以
nil
作為結(jié)束的變參方法,如NSLog
:
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
//注意后方的宏定義:NS_FORMAT_FUNCTION(1,2)驼鞭,我們點擊過去之后查看一下
#define NS_FORMAT_FUNCTION(F,A) __attribute__((format(__NSString__, F, A)))
看一下這句代碼
__attribute__((format(__NSString__,F, A)))
這句的意思是秦驯,參數(shù)的第F位是格式化字符串,從A位開始我們開始檢查
所以NSLog
的第一個參數(shù)是一個格式化字符串挣棕,通過這個字條串就能獲得后面的參數(shù)個數(shù)译隘,而且能檢查參數(shù)數(shù)量錯誤。
所以本文創(chuàng)建字典的方法的重點就是用已知個數(shù)的可變參數(shù)洛心,去除為nil
值的對象固耘。
參考鏈接
- 宏定義:
IOS 宏NSDictionaryOfVariableBindings中的#
宏定義的黑魔法 - 宏菜鳥起飛手冊
- 可變參數(shù):
iOS可變參數(shù)(va_list)處理
iOS開發(fā)中使用可變參數(shù)
iOS:在objective-c 使用可變參數(shù)
-
NS_REQUIRES_NIL_TERMINATION/NS_FORMAT_FUNCTION
:
OC中的 attribute
覺得好用的點個贊?~