相信不少開發者,都被NSNull坑過,最常見的是伺服器傳回的json裡面,說好的字典、數組、數字,結果傳回的是空值。 這個時候,NSJSONSerialization 會自動把他們換成 NSNull。當我們再去用dict[@“hello”]的時候,就會出觸發exception,導緻程式崩潰。 那麼如何處理它呢? 我曾經的做法 寫了個宏,判斷傳回的這個類是不是NSNull類,即 isKindOfClass 最簡單的做法 相信大家都知道,[NSNull null] 并不是一個 工廠方法,而是一個 單例模式,那麼我們直接去判斷指派的這個指針是不是[NSNull null] 就好了。 那麼問題來了,編譯器會多了一個warning,很煩人。 在 這篇文章裡面介紹了各種做法:
- - (void)someMethod
- {
- NSString *aString = @"loremipsum";
- // This will complain: "Comparison of distinct pointer types ('NSString *' and 'NSNull *')"
- if (aString != [NSNull null])
- {
- }
- // This works (at least for strings), but isEqual: does different things
- // for different classes, so it's not ideal
- if ([aString isEqual:[NSNull null]])
- {
- }
- // If you cast it to the class you're comparing against
- // then you're good to go
- if (aString != (NSString *)[NSNull null])
- {
- }
- // But we can also just cast it to id and
- // that works generically
- if (aString != (id)[NSNull null])
- {
- }
- // The thing that would be really cool,
- // would be [NSNull null] returning
- // id (like in the sample category below).
- // Wouldn't count on that one though.
- if (aString != [NSNull idNull])
- {
- }
- }
這些都不是非常漂亮的解決方案,這篇文章的作者推薦:
- @interface NSNull (idNull)
- + (id)idNull;
- @end
- @implementation NSNull (idNull)
- + (id)idNull { return [NSNull null]; }
- @end
或者呢
- if ([[NSNull null] isEqual:aString])
- {
- }
最終解決方案 上面的做法,都需要判斷一次,還是很不優雅,為什麼呢,我們還是不能像NULL,nil一樣,直接拿來用,還是需要判斷一下,這裡推薦一套最漂亮的作法。 陳航提供了一個 gist 我發現我的octopress的gist插件挂了,直接貼出來好了。
- #define NSNullObjects @[@"",@0,@{},@[]]
- @interface NSNull (InternalNullExtention)
- @end
- @implementation NSNull (InternalNullExtention)
- - (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
- {
- NSMethodSignature* signature = [super methodSignatureForSelector:selector];
- if (!signature) {
- for (NSObject *object in NSNullObjects) {
- signature = [object methodSignatureForSelector:selector];
- if (signature) {
- break;
- }
- }
- }
- return signature;
- }
- - (void)forwardInvocation:(NSInvocation *)anInvocation
- {
- SEL aSelector = [anInvocation selector];
- for (NSObject *object in NSNullObjects) {
- if ([object respondsToSelector:aSelector]) {
- [anInvocation invokeWithTarget:object];
- return;
- }
- }
- [self doesNotRecognizeSelector:aSelector];
- }
- @end
很高端霸氣上檔次的做法,通過處理異常情況,來實作這個功能。 這裡還提供一個 日本人的封裝方案:
- #import "NSNull+OVNatural.h"
- @implementation NSNull (OVNatural)
- - (void)forwardInvocation:(NSInvocation *)invocation
- {
- if ([self respondsToSelector:[invocation selector]]) {
- [invocation invokeWithTarget:self];
- }
- }
- - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
- {
- NSMethodSignature *sig = [[NSNull class] instanceMethodSignatureForSelector:selector];
- if(sig == nil) {
- sig = [NSMethodSignature signatureWithObjCTypes:"@^v^c"];
- }
- return sig;
- }
- @end
關于[NSMethodSignature signatureWithObjCTypes:“@^vc”]的功能,可以參考以下兩篇文章: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html http://nshipster.com/type-encodings/
//判斷對象不空
if(object) {}
//判斷對象為空
if(object == nil) {}
//數組初始化,空值結束
NSArray *pageNames=[[NSArray alloc] initWithObjects:@"DocumentList",@"AdvancedSearch",@"Statistics",nil];
//判斷數組元素是否為空
UIViewController *controller=[NSArray objectAtIndex:i];
if((NSNull *)controller == [NSNull null])
{
//
}
//判斷字典對象的元素是否為空
NSString *userId=[NSDictionary objectForKey:@"UserId"];
if(userId == [NSNull null])
{
//
}