依賴注入(Dependency Injection)
這個詞,源於java,但在Cocoa框架中也是十分常見的。
舉例來講:
UIView的初始化方法initWithFramejava
- (id)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER;
這裏的frame傳入值,就是所謂的依賴(Dependency)
,這個View實例化是根據frame注入實現的。
但這種用法有很大的侷限性後端
咱們不知道究竟依賴注入的屬性有哪些框架
不可能無限加長方法長度來知足更多的依賴屬性前後端分離
因此咱們準備採用字典容器對NSObject類進行依賴注入擴展。測試
@interface NSObject (XXXDependencyInjection) - (nullable id)initWithParams:(nonnull NSDictionary *)params; - (void)injection:(nonnull NSDictionary*)params; @end
- (id)initWithParams:(NSDictionary *)params { self = [self init]; if (self) { [self injection:params]; } return self; } - (void)injection:(NSDictionary*)params { [params.allKeys enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { SEL selector = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",[[obj substringToIndex:1] uppercaseString],[obj substringFromIndex:1]]); id value = [params objectForKey:obj]; if ([self respondsToSelector:selector]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [self performSelector:selector withObject:value]; #pragma clang diagnostic pop } else { @try { [self setValue:value forKeyPath:obj]; } @catch (NSException *exception) { NSLog(@"%@",exception); [exception raise]; } @finally { } } }]; }
咱們將須要注入的屬性,封裝到一個字典裏,例如:code
UIViewController* controller = [[UIViewController alloc] initWithParams:@{ @"title":@"測試", @"view.backgroundColor":[UIColor whiteColor] }];
咱們給這個VC注入了兩個屬性,一個是其title,一個是其View的backgroundColor屬性。
字典傳入之後,咱們讀區params.allKeys
進行遍歷,拼裝set+參數名的selector,這裏用的是NSSelectorFromString方法:orm
SEL selector = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",[[obj substringToIndex:1] uppercaseString],[obj substringFromIndex:1]]);
而後咱們判斷實例是否能夠響應這個set方法,若是能夠,則給其賦值。對象
if ([self respondsToSelector:selector]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [self performSelector:selector withObject:value]; #pragma clang diagnostic pop }
這裏的三行clang宏是爲了消除編譯器的內存泄漏警告,這裏由於咱們進行了驗證,因此不會出現leak。內存
咱們注意到上例中還有一句給VC的View改變背景顏色開發
@"view.backgroundColor":[UIColor whiteColor]
這裏就用到了KVC的點語法特性,在咱們判斷到實例不能響應 if ([self respondsToSelector:selector])
的時候,經過點語法,進行賦值
@try { [self setValue:value forKeyPath:obj]; } @catch (NSException *exception) { NSLog(@"%@",exception); [exception raise]; } @finally { }
這裏添加了異常捕獲,由於點語法對屬性名稱拼寫要求是全匹配,不然拋異常,因此要注意。
這樣改造過的init方法,優勢很是明顯,就是綁定更加集中便捷,若是使用的是storyboard
則能夠輕鬆實現先後端分離。目前的缺點也很明顯,不能告訴開發者哪些屬性是必需依賴,另外還不能支持非對象屬性的賦值,但願拋磚引玉,你們來改進這段代碼。