這個分析是我在讀AFNetworking 3.0 源碼解系列寫的,很多想法都是來自他的文章,非常感謝作者的分享精神!
1.這個類是基于SCNetworkReachabilityRef
這個系統類來實現的,提供了和聯網相關的函數。可參考這個。 2.打開和關閉監聽網絡變化的方法
3.網絡狀態改變時,監聽的回調有兩種方法: a.通過監聽AFNetworkingReachabilityDidChangeNotification
這個通知。 b.通過下面這個方法。
1.可以用一些特殊的格式注釋,或者PRagma mark等來進行分隔不同功能模塊的代碼。如下圖@name Initialization
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;///---------------------/// @name Initialization///---------------------/** Returns the shared network reachability manager. */+ (instancetype)sharedManager;2.BOOL
類型的值,若只允許訪問可設置成getter
,然后給予一個更明確的getter
、setter
方法名來替代默認生成的方法名(即變量名、set+變量名)。 在.h中聲明為readonly
的屬性,可以再類別中再聲明一次為readwrite
,這樣在.m中可以進行讀寫。
3.關于定義常量方面的格式一般有兩種,普通的就是#define kMyConstantString @"Hello"
,另一種如下:
使用第二種方式定義的字符串,在比較的時候速度更快,可以直接使用(stringInstance == MyFirstConstant)
來比較,而define則使用的是這種.([stringInstance isEqualToString:MyFirstConstant])
。 第二種直接比較的是指針地址,而第一個則是一一比較字符串的每一個字符是否相等。 這種定義可以多用于NSNotification
的字符串定義。
4.NS_DESIGNATED_INITIALIZER
和 NS_UNAVAILABLE
的配合使用:
NS_DESIGNATED_INITIALIZER
用來標記指定構造器,即你希望初始化對象時所使用的指定函數。如果子類指定了新的初始化器,那么在這個初始化器內部必須調用父類的Designated Initializer
。并且需要重寫父類的Designated Initializer
,將其指向子類新的初始化器。如下:
但是往往,我們是不希望子類用其他的初始化方法的,所以可以用NS_UNAVAILABLE
來標記繼承來的init
方法,讓它變為不可用,成為私有的方法。即外部調用alloc init
會報錯。
1.網絡狀態改變時的通知
//網絡環境發生改變時接收的通知NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";//上面的通知發送時,會攜帶一組狀態數據,根據這個key從userinfo中取出網絡statusNSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";//使用- (void)verifyReachabilityNotificationGetsPostedWithManager:(AFNetworkReachabilityManager *)manager{ [self expectationForNotification:AFNetworkingReachabilityDidChangeNotification object:nil handler:^BOOL(NSNotification *note) { AFNetworkReachabilityStatus status; status = [note.userInfo[AFNetworkingReachabilityNotificationStatusItem] integerValue]; BOOL isReachable = (status == AFNetworkReachabilityStatusReachableViaWiFi || status == AFNetworkReachabilityStatusReachableViaWWAN); return isReachable; }]; [manager startMonitoring]; [self waitForExpectationsWithCommonTimeout];}2.將SCNetworkReachabilityRef
文件中用來表示網絡狀態的SCNetworkReachabilityFlags
轉化成平時常用的狀態,即本類AFNetworkReachabilityStatus
的值。
3.監聽網絡變化有兩個方法,block和NSNotification, 為了讓這兩個方法的值保持一致,將兩個過程封裝到同一個函數中,一旦發生網絡變化,對調用這個函數
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) { AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); dispatch_async(dispatch_get_main_queue(), ^{ if (block) { //有block就調用block block(status); } //發通知 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; //用到了AFNetworkingReachabilityNotificationStatusItem作為key來存status,接收到通知時,再通過這個key取出status NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) }; [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo]; });}4.單例創建方法,注意宏定義,這里的宏定義可以減少書寫ifelse的判斷
+ (instancetype)sharedManager { static AFNetworkReachabilityManager *_sharedManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedManager = [self manager]; }); return _sharedManager;}+ (instancetype)manager { //涉及到IPv6所以要判斷版本 //__IPHONE_OS_VERSION_MIN_REQUIRED iphone系統 //__IPHONE_OS_VERSION_MIN_REQUIRED >= 90000 系統版本9.0及以上#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100) struct sockaddr_in6 address; bzero(&address, sizeof(address)); address.sin6_len = sizeof(address); address.sin6_family = AF_INET6;#else struct sockaddr_in address; bzero(&address, sizeof(address)); address.sin_len = sizeof(address); address.sin_family = AF_INET;#endif return [self managerForAddress:&address];}5.開啟網絡監聽 首先要了解一下SCNetworkReachabilityContext
,這是一個結構體:
retain和release 函數是下邊的這兩個函數,寫在.m文件最前面
static const void * AFNetworkReachabilityRetainCallback(const void *info) { return Block_copy(info);}static void AFNetworkReachabilityReleaseCallback(const void *info) { if (info) { Block_release(info); }}具體開啟監控的代碼:
- (void)startMonitoring { [self stopMonitoring]; if (!self.networkReachability) { return; } //這個block就是用來傳入SCNetworkReachabilityContext的info __weak __typeof(self)weakSelf = self; AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { __strong __typeof(weakSelf)strongSelf = weakSelf; strongSelf.networkReachabilityStatus = status; if (strongSelf.networkReachabilityStatusBlock) { strongSelf.networkReachabilityStatusBlock(status); } }; //新建上下文 SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; //設置回調,AFNetworkReachabilityCallback執行時可以傳入一個block作為參數,這里傳入的就是上面的callback,即context里的info SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context); //加入RunLoop池 SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ SCNetworkReachabilityFlags flags; if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) { AFPostReachabilityStatusChange(flags, callback); } });}其中AFNetworkReachabilityCallback
的定義如下,是根據SCNetworkReachabilitySetCallback
第二個參數類型來聲明的:
6.鍵值依賴,這段代碼在.m文件的最后,可以溫習一下kvc、kvo的相關知識。
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) { return [NSSet setWithObject:@"networkReachabilityStatus"]; } return [super keyPathsForValuesAffectingValueForKey:key];}1.NS_ENUM
和NS_OPTIONS
都是常用的枚舉聲明方法,NS_OPTIONS
是按位掩碼,NS_ENUM枚舉項的值為NSInteger,NS_OPTIONS枚舉項的值為NSUInteger;NS_ENUM定義通用枚舉,NS_OPTIONS定義位移枚舉。位移枚舉即是在你需要的地方可以同時存在多個枚舉值,而NS_ENUM定義的枚舉不能幾個枚舉項同時存在,只能選擇其中一項。 NS_OPTIONS
可以表示幾個選項的各種組合,用|
表示,當判斷這個組合是否包含某個單獨的枚舉值時可以進行&操作。如分析.m的第一點中代碼所示。
2.類中的私有方法可以用c函數來寫,eg:static void funcName()
a.在文件的最前方,比較容易查找 b. 可以適當的使用內聯函數(static inline),內聯函數在編譯的時候,會把代碼直接嵌入調用代碼中。就相當于用#define 宏定義來定義一個 函數,避免了使用call指令,提高效率
3.__unused
前綴,就可以免除警告。其原理是告訴編譯器,如果變量未使用就不參與編譯。
新聞熱點
疑難解答