iOS懶人開發:自動去除字典空值對象,仿系統字典建立方法NSDictionaryOfVariableBindings

說明

模仿系統的快速生成字典的方法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

宏定義

NSDictionaryOfVariableBindings初始化字典

建立字典的方法你們都比較熟悉,這裏就再也不說。
可是有一個根據對象名稱建立字典的方法很方便,這裏要說一下:.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值的對象。

參考連接

以爲好用的點個贊❤~

相關文章
相關標籤/搜索