此文章對AFNetworkReachabilityManager
的源碼做一些閱讀記錄和個人的理解.
AFNetworkReachabilityManager
個人理解為是對SCNetworkReachabilityRef
的相關封裝實現的. 在講解AFNetworkReachabilityManager
之前, 需要先對SCNetworkReachabilityRef
有一些了解, 附上SCNetworkReachabilityRef監測網絡狀態.
AFNetworkReachabilityManager
對網絡的監測使用如下:
AFNetworkReachabilityManager *networkReachManager = [AFNetworkReachabilityManager sharedManager];
[networkReachManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(@"%zd", status);
}];
// 開始監測網絡
[networkReachManager startMonitoring];
當網絡狀態發生變化時就會執行block
, 將網絡狀態AFNetworkReachabilityStatus
返回給我們.
AFNetworkReachabilityManager.h文件
/**
網絡類型
- AFNetworkReachabilityStatusUnknown: 未知網絡
- AFNetworkReachabilityStatusNotReachable: 網絡不可達, 無網絡
- AFNetworkReachabilityStatusReachableViaWWAN: 手機網絡
- AFNetworkReachabilityStatusReachableViaWiFi: WiFi
*/
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusUnknown = -1,
AFNetworkReachabilityStatusNotReachable = 0,
AFNetworkReachabilityStatusReachableViaWWAN = 1,
AFNetworkReachabilityStatusReachableViaWiFi = 2,
};
對外提供四種網絡狀態的枚舉值.
/**
The current network reachability status.
當前網絡狀態
*/
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
/**
Whether or not the network is currently reachable.
網絡是否可用
*/
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
/**
Whether or not the network is currently reachable via WWAN.
當前連接是否是WWAN
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
/**
Whether or not the network is currently reachable via WiFi.
當前連接是否是WiFi
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
在.h文件中對外提供了4個只讀屬性, 并且給出了對應的getter
方法.
+ (instancetype)sharedManager;
+ (instancetype)manager;
+ (instancetype)managerForDomain:(NSString *)domain;
+ (instancetype)managerForAddress:(const void *)address;
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)init NS_UNAVAILABLE;
提供幾個初始化方法, 創建實例對象.
/**
Starts monitoring for changes in network reachability status.
開始監聽
*/
- (void)startMonitoring;
/**
Stops monitoring for changes in network reachability status.
結束監聽
*/
- (void)stopMonitoring;
/**
Returns a localized string representation of the current network reachability status.
返回一個網絡狀態的字符串
*/
- (NSString *)localizedNetworkReachabilityStatusString;
/**
Sets a callback to be executed when the network availability of the `baseURL` host changes.
@param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`.
網絡狀態改變的回調
監聽網絡狀態的改變有兩種方法:1.是實現這個block 2.是監聽通知
*/
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;
/**
Posted when network reachability changes.
This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability.
@warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import <SystemConfiguration/SystemConfiguration.h>` to the header prefix of the project (`Prefix.pch`).
網絡狀態改變時 發送的通知
在userInfo下有以AFNetworkingReachabilityNotificationStatusItem為key的一個NSNumber類型的值, 這個值對應著AFNetworkReachabilityStatus枚舉, 反應網絡狀態
FOUNDATION_EXPORT主要用于定義常量
*/
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;
/**
Returns a localized string representation of an `AFNetworkReachabilityStatus` value.
這個是定義的一個C語言函數, 返回本地化的status字符串
*/
FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);
定義了兩個通知的名稱, 當網絡狀態改變時發出的通知, 接收的通知中會有一個userInfo
, 可根據key
為AFNetworkingReachabilityNotificationStatusItem
取出通知的內容.
我們可以借鑒AFN
中通知的實現, 在開發中可以將通知的key
定義到一個專門管理常量的文件中, 按照不同的模塊進行劃分.
AFNetworkReachabilityManager.m文件
// 一個靜態字符串, 網絡狀態發生變化時發出的通知 對應.h
NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
// 網絡狀態發生變化時發出通知, 攜帶的數據
NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";
// 將枚舉類型轉換成字符串(這是對在.h中聲明的實現)
NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusNotReachable:
return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil);
case AFNetworkReachabilityStatusReachableViaWWAN:
return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil);
case AFNetworkReachabilityStatusReachableViaWiFi:
return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil);
case AFNetworkReachabilityStatusUnknown:
default:
return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil);
}
}
對在.h
文件中定義的常量的賦值和函數的實現.
// 定義block類型, 當網絡狀態改變時調用的block
typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status);
static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown;
if (isNetworkReachable == NO) {
status = AFNetworkReachabilityStatusNotReachable;
}
#if TARGET_OS_IPHONE
else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
status = AFNetworkReachabilityStatusReachableViaWWAN;
}
#endif
else {
status = AFNetworkReachabilityStatusReachableViaWiFi;
}
return status;
}
根據SCNetworkReachabilityFlags
這個網絡標記轉換成AFN中的網絡狀態
static修飾全局, 只能在當前文件使用
在我們開發的過程中經常會使用私有方法, 將私有方法寫成c語言函數的形式的好處(個人理解)(static void functionName(){}):
1.在文件的最前邊, 方便查找
2.可以使用內聯函數, 提高效率
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block(status);
}
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
});
}
監聽網絡狀態的改變有兩種, 一種是實現setReachabilityStatusChangeBlock:
中的block, 另一種是監聽通知AFNetworkingReachabilityDidChangeNotification
, 此方法將監聽網絡狀態改變的兩種方式封裝到一個函數中, 在主隊列中異步執行. 這種方法也是值得學習的地方.
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}
這個函數直接調用上邊的函數.
static const void * AFNetworkReachabilityRetainCallback(const void *info) {
return Block_copy(info);
}
static void AFNetworkReachabilityReleaseCallback(const void *info) {
if (info) {
Block_release(info);
}
}
void *
個人理解為OC
中的id
類型, 可以指向任何類型. block
其實也是對象, 我們可以對其進行retain
操作, 在block
做為屬性的時候我們通常用copy
去修飾block
, 將block
拷貝到堆內存中.
這兩個block
用于創建SCNetworkReachabilityContext
結構體, 下邊會講到.
// SCNetworkReachabilityRef 網絡連接引用
@property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;
// 網絡狀態, 枚舉類型
@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
// 網絡狀態切換block
@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock;
在.m
文件中定義的三個屬性.
- (void)startMonitoring {
[self stopMonitoring];
// 句柄
if (!self.networkReachability) {
return;
}
// callback回調
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
/**
typedef struct {
CFIndex version;
void * __nullable info;// void * 相當于oc中的id類型, 可以指向任何類型的參數
const void * __nonnull (* __nullable retain)(const void *info);// 接收一個函數, 目的是對info做retain操作
void (* __nullable release)(const void *info);// 接收一個函數, 目的是對info做release操作
CFStringRef __nonnull (* __nullable copyDescription)(const void *info);// 接收一個函數, 根據info獲取description字符串
} SCNetworkReachabilityContext;
context是一個結構體
*/
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
// 根據上下文設置回調
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
// 加入線程池中 mainRunLoop commonModes
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);
}
});
}
這個是主要講解的方法, AFNetworkReachabilityManager
監測網絡狀態的核心就在此方法.
SCNetworkReachabilityContext
是一個結構體(個人理解為結構體主要用于存儲數據), void * __nullable info
是指向需要執行的block的指針, 包含了用戶指定的數據和用于SCNetworkReachabilitySetCallback
方法的回調函數.
SCNetworkReachabilitySetCallback
設置回調, 可以看我的另一篇文章有簡要介紹.
SCNetworkReachabilityScheduleWithRunLoop
將networkReachability
網絡連接引用加入到運行循環中. 個人理解為加入運行循環以后會一直監測networkReachability
網絡狀態, 如果網絡狀態有變化就會調用AFNetworkReachabilityCallback
.
在異步線程中發送一次網絡狀態, 調用SCNetworkReachabilityGetFlags
獲取網絡狀態, AFPostReachabilityStatusChange
發送網路狀態.
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
從指定的運行循環和模式中移除網絡連接引用的調度. 也就是說不再監聽networkReachability
的網絡狀態.
#pragma mark - NSKeyValueObserving
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
return [NSSet setWithObject:@"networkReachabilityStatus"];
}
return [super keyPathsForValuesAffectingValueForKey:key];
}
鍵值依賴, 返回一個鍵集合, 這些屬性的值會影響指定的key的值, 當集合中鍵的值發生變化時, 就會觸發指定key的監聽通知.
當networkReachabilityStatus
的值發生變化時, 就會觸發指定的key
的鍵值監聽方法.
利用一天的時間整理了一下, 如果有錯誤的地方, 希望能夠指出, 共同進步.