該文章閱讀的AFNetworking的版本為3.2.0。
該類主要是用來檢測網路環境的變化。
1.介面檔案
1.1.列舉
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusUnknown = -1,
AFNetworkReachabilityStatusNotReachable = 0,
AFNetworkReachabilityStatusReachableViaWWAN = 1,
AFNetworkReachabilityStatusReachableViaWiFi = 2,
};
複製程式碼
這個列舉提供了可以監控的網路狀態:
AFNetworkReachabilityStatusUnknown
網路狀態未知
AFNetworkReachabilityStatusNotReachable
網路狀態無連線
AFNetworkReachabilityStatusReachableViaWWAN
網路狀態為蜂窩行動網路連線
AFNetworkReachabilityStatusReachableViaWiFi
網路狀態為無線區域網連線
1.2.屬性
/**
當前網路連線狀態
*/
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
/**
當前是否有網路連線
*/
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
/**
當前網路連線狀態是否為蜂窩行動網路連線
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
/**
當前網路連線狀態是否為無線區域網連線
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
複製程式碼
1.3.方法
/**
獲取單例物件
*/
+ (instancetype)sharedManager;
/**
例項化預設socket地址的物件,並主動監視預設socket地址的狀態
*/
+ (instancetype)manager;
/**
例項化指定域的物件,並主動監視指定域的狀態
*/
+ (instancetype)managerForDomain:(NSString *)domain;
/**
例項化指定socket地址的物件,並主動監視指定socket地址的狀態
*/
+ (instancetype)managerForAddress:(const void *)address;
/**
以指定SCNetworkReachabilityRef物件進行初始化,並主動監視指定SCNetworkReachabilityRef物件的狀態
*/
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
/**
初始化方法,這個方法被手動禁止了,如果強制呼叫這個方法就會報錯
*/
- (nullable instancetype)init NS_UNAVAILABLE;
/**
開始監控網路狀態
*/
- (void)startMonitoring;
/**
停止監控網路狀態
*/
- (void)stopMonitoring;
/**
獲得網路狀態的本地文字描述
*/
- (NSString *)localizedNetworkReachabilityStatusString;
/**
當網路狀態發生變化時,會回撥block
*/
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;
複製程式碼
1.4.全域性靜態常量
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;
複製程式碼
這兩個常量是用在通過通知來監聽網路狀態變化。當需要用通知來監聽網路狀態變化時,先監聽AFNetworkingReachabilityDidChangeNotification
通知,然後在通知呼叫的方法中獲取傳遞過來引數中的屬性userInfo
,這個屬性是NSDictionary
型別的,通過key值AFNetworkingReachabilityNotificationStatusItem
就可以獲得當前的網路狀態。
1.5.全域性方法
FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);
複製程式碼
這個方法的作用是將傳入的網路狀態列舉值轉成本地文字描述。
2.實現檔案
2.1.全域性靜態常量
NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";
複製程式碼
為.h
檔案中全域性靜態常量賦值
2.2.別名
typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status);
複製程式碼
為引數為AFNetworkReachabilityStatus
型別無返回值的block起名為AFNetworkReachabilityStatusBlock
2.3.私有方法
/**
這個方法就是.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);
}
}
/**
該方法是將SCNetworkReachabilityFlags型別的列舉轉換成AFNetworking自定義的網路狀態列舉
*/
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;
}
/**
為了保證網路狀態變化時block回撥和傳送通知的統一性,將兩者在主執行緒統一呼叫
*/
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
// 將flags轉成自定義網路狀態列舉值
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
// 主佇列非同步
dispatch_async(dispatch_get_main_queue(), ^{
// 回撥block
if (block) {
block(status);
}
// 傳送通知
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
});
}
/**
將上面的方法封裝,作為引數傳入網路狀態監控回撥方法SCNetworkReachabilitySetCallback中
*/
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}
/**
將block複製到堆中,並封裝成方法作為引數構建SCNetworkReachabilityContext結構體
*/
static const void * AFNetworkReachabilityRetainCallback(const void *info) {
return Block_copy(info);
}
/**
釋放複製到堆中的block,並封裝成方法作為引數構建SCNetworkReachabilityContext結構體
*/
static void AFNetworkReachabilityReleaseCallback(const void *info) {
if (info) {
Block_release(info);
}
}
複製程式碼
2.4.類擴充套件
/**
儲存傳入的SCNetworkReachabilityRef物件
*/
@property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;
/**
儲存當前網路狀態的列舉值
*/
@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
/**
儲存傳入的用於回撥的block
*/
@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock;
複製程式碼
2.5.方法實現
+ (instancetype)sharedManager {
// 呼叫以預設socket地址例項化物件的方法,生成單例物件
static AFNetworkReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedManager = [self manager];
});
return _sharedManager;
}
+ (instancetype)managerForDomain:(NSString *)domain {
// 利用傳入的域生成SCNetworkReachabilityRef物件
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
// 利用SCNetworkReachabilityRef物件例項化AFNetworkReachabilityManager物件
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
// 釋放SCNetworkReachabilityRef物件
CFRelease(reachability);
return manager;
}
+ (instancetype)managerForAddress:(const void *)address {
// 利用傳入的地址象例項化AFNetworkReachabilityManager物件
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
// 利用SCNetworkReachabilityRef物件例項化AFNetworkReachabilityManager物件
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
// 釋放SCNetworkReachabilityRef物件
CFRelease(reachability);
return manager;
}
+ (instancetype)manager
{
// 根據版本號適配IPv6和IPv4,獲得預設地址
#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];
}
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
self = [super init];
if (!self) {
return nil;
}
// 儲存或初始化屬性
_networkReachability = CFRetain(reachability);
self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
return self;
}
- (instancetype)init NS_UNAVAILABLE
{
return nil;
}
- (void)dealloc {
// 停止監控
[self stopMonitoring];
// 釋放SCNetworkReachabilityRef物件
if (_networkReachability != NULL) {
CFRelease(_networkReachability);
}
}
- (BOOL)isReachable {
return [self isReachableViaWWAN] || [self isReachableViaWiFi];
}
- (BOOL)isReachableViaWWAN {
return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN;
}
- (BOOL)isReachableViaWiFi {
return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi;
}
- (void)startMonitoring {
// 先停止之前的網路監控
[self stopMonitoring];
// 如果沒有傳入或者生成SCNetworkReachabilityRef物件就直接返回
if (!self.networkReachability) {
return;
}
// 構造網路監控所需的引數
__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};
// 設定網路監控回撥
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
// 放到執行迴圈中
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
// 在全域性併發佇列中非同步監聽SCNetworkReachabilityRef物件的網路狀態,如果發生變化則進行回撥和傳送通知
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
});
}
- (void)stopMonitoring {
// 如果沒有傳入或者生成SCNetworkReachabilityRef物件就直接返回
if (!self.networkReachability) {
return;
}
// 將SCNetworkReachabilityRef物件從執行迴圈中移除
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
- (NSString *)localizedNetworkReachabilityStatusString {
return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
}
- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {
self.networkReachabilityStatusBlock = block;
}
複製程式碼
這其中有一點是在使用GCD時傳入了引數DISPATCH_QUEUE_PRIORITY_BACKGROUND
,這個不常用的引數有著和AFURLRequestSerialization
類中的throttleBandwidthWithPacketSize:delay:
方法共同的知識點,感興趣的話可以看這篇文章
2.6.KVO
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
return [NSSet setWithObject:@"networkReachabilityStatus"];
}
return [super keyPathsForValuesAffectingValueForKey:key];
}
複製程式碼
新增依賴鍵,使reachable
、reachableViaWWAN
和reachableViaWiFi
屬性依賴於networkReachabilityStatus
屬性,當networkReachabilityStatus
屬性值發生變化時,reachable
、reachableViaWWAN
和reachableViaWiFi
屬性的觀察者也能得到通知。
原始碼閱讀系列:AFNetworking
原始碼閱讀:AFNetworking(二)——AFURLRequestSerialization
原始碼閱讀:AFNetworking(三)——AFURLResponseSerialization
原始碼閱讀:AFNetworking(四)——AFSecurityPolicy
原始碼閱讀:AFNetworking(五)——AFNetworkReachabilityManager
原始碼閱讀:AFNetworking(六)——AFURLSessionManager
原始碼閱讀:AFNetworking(七)——AFHTTPSessionManager
原始碼閱讀:AFNetworking(八)——AFAutoPurgingImageCache
原始碼閱讀:AFNetworking(九)——AFImageDownloader
原始碼閱讀:AFNetworking(十)——AFNetworkActivityIndicatorManager
原始碼閱讀:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking
原始碼閱讀:AFNetworking(十二)——UIButton+AFNetworking
原始碼閱讀:AFNetworking(十三)——UIImageView+AFNetworking
原始碼閱讀:AFNetworking(十四)——UIProgressView+AFNetworking