模仿系統的快速生成字典的方法NSDictionaryOfVariableBindings並過濾掉值爲nil
的對象或內容全爲空格字符串。objective-c
推薦適用場合:網絡請求生成參數字典,無需判空。
其餘建立字典的地方也可使用,注意此方法會過濾掉全爲空格及@""
字符串,如不須要可自行修改。express
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;
}
複製代碼
能夠看到傳入的參數可爲nil
不會崩潰,且生成字典後自動去除了值爲nil
和全是空格的NSString
。函數
//.h
#define ZXDictionaryOfVariableBindings(...) [Tool _ZXDictionaryOfVariableBindings:@"" # __VA_ARGS__, __VA_ARGS__]
/** 模仿系統的對象生成字典的宏定義:NSDictionaryOfVariableBindings(...) if v1 = @"something"; v2 = nil; v3 = @"something"; v4 = @""; ZXDictionaryOfVariableBindings(v1, v2, v3) is equivalent to [NSDictionary dictionaryWithObjectsAndKeys:v1, @"v1", v3, @"v3", nil]; 而且參數的值可爲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;
}
複製代碼
在開發常常須要建立字典對象,可是建立字典時存入的對象值不能爲nil
,不然會崩潰。
尤爲是在進行網絡請求時,更是常常須要對存入字典的對象判空,因而做爲一個能少寫一行代碼毫不多寫一個字母的懶癌晚期患者,就想要是建立字典時能自動對傳入的對象判空並去除空值對象多好。
ui
因而我就研究了系統的字典快捷建立方法NSDictionaryOfVariableBindings(...)
,發現要解決此需求須要瞭解宏定義的使用,可變參數的使用。spa
建立字典的方法你們都比較熟悉,這裏就再也不說。
可是有一個根據對象名稱建立字典的方法很方便,這裏要說一下:.net
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
複製代碼
這個方法是使用Autolayout時常用的一個宏,這個宏能夠生成一個變量名到變量值映射的Dictionary
。具體使用很簡單,再也不細說,不知道的同窗推薦大家可使用此方法建立字典,很方便。
指針
使用此宏依舊須要對字典中的對象判空,防止傳入空值崩潰,要想自動去除值爲nil
的對象,要參數傳入以後入手,下面就來改造此宏定義自動去除傳入的空值。
日誌
要想改造系統建立字典的方法,首先要知道系統建立字典的原理。
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
中有3個參數,下面先搞清楚這三個參數的含義,也就知道了該方法的原理。code
@"" # __VA_ARGS__
中間#
的做用:單個井號的做用是字符串化,將後面的宏參數 用雙引號引發來,轉爲一個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
複製代碼
能夠看出#
,將參數原樣轉換成字符串常量,若是參數是一個表達式,那麼輸出這個表達式的原樣字符串常量。
前面的@
是objc
的編譯符號,不屬於宏操做的對象。若是有宏定義@#expression
,出來後就是一個內容是expression
的內容的NSString
。
__VA_ARGS__
表示的是宏定義中的...
中的全部參數。可變參數將被統一處理,在這裏展開的時候編譯器會將__VA_ARGS__
直接替換爲輸入中的全部參數。
回頭再看看NSDictionaryOfVariableBindings的定義:
#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
複製代碼
若是這樣生成兩個button
的映射:
NSDictionaryOfVariableBindings(button1, button2);
複製代碼
那麼預編譯時就會轉換成:
_NSDictionaryOfVariableBindings(@"" "button1, button2", button1, button2, nil);
複製代碼
因爲兩個常量字符串放在一塊兒就是字符串常量串聯,將變成兩個字符串常量組合在一塊兒的字符串常量,也就是上面是一個空字符串@""
和"button1, button2"
串聯,因此上面的代碼等價於:
_NSDictionaryOfVariableBindings(@"button1, button2", button1, button2, nil);
複製代碼
那麼_NSDictionaryOfVariableBindings
函數就能夠將它的第一個參數按逗號,分割開做爲key
,後面就是各個key
對應的值了。所以這段代碼就建立了一個內容爲{ @"button1" = button1, @"button2" = button2 }
的Dictionary
。
再回看宏定義NSDictionaryOfVariableBindings(...)
,其中...
便可變參數,其實可變參數並很多見,好比:
// 日誌輸出
NSLog(NSString *format, ...);
// NSString實例的建立
+(instancetype)stringWithFormat:(NSString *)format, ...;
// NSArray實例的建立
+(instancetype)arrayWithObjects:(ObjectType)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
複製代碼
可變參數解析也很簡單,直接拿封裝的源碼舉例,註釋寫的很清楚了:
//.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_list變量,這個宏的第二個參數是第一個可變參數的前一個參數,是一個固定的參數
va_start(list, firstArg);
// 用於存放取出的參數,C語言的字符指針, 指針根據offset來指向須要的參數,從而讀取參數
id arg;
for (NSString *key in keys) {
// 遍歷所有參數 va_arg返回可變的參數(va_arg的第二個參數是你要返回的參數的類型)
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];
}
}
// 清空參數列表,並置參數指針args無效
va_end(list);
}
return dic;
}
複製代碼
其中重點是不像其餘字典初始化方法以nil
做爲傳參結束判斷的標準。
nil
做爲傳參結束的變參方法:須要在定義方法時標識NS_REQUIRES_NIL_TERMINATION
,則初始化時未尾必定要加上nil
,如:+ (instancetype)arrayWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
複製代碼
由於這樣的方法中沒有提供對參數個數的檢測,須要判斷參數爲nil
時結束遍歷,銷燬指針偏移量,不然會崩潰。
nil
做爲結束的變參方法,如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)))
複製代碼
這句的意思是,參數的第F位是格式化字符串,從A位開始咱們開始檢查
因此NSLog
的第一個參數是一個格式化字符串,經過這個字條串就能得到後面的參數個數,並且能檢查參數數量錯誤。
因此本文建立字典的方法的重點就是用已知個數的可變參數,去除爲nil
值的對象。
NS_REQUIRES_NIL_TERMINATION/NS_FORMAT_FUNCTION
:以爲好用的點個贊❤~