AFNetworking主要包含四個部分:
1.Reachability
2.Security
3.Serialization
4.NSURLSession
其中前三個部分是彼此獨立的模組,互相無依賴,NSURLSession模組依賴於前三部分,
現在我們就一個模組一個模組的分別解析,本文首先解析Reachability網路監測部分的原始碼。
作為一個實用主義者,我本人是喜歡直接看程式碼,看不懂的地方再看解釋,所以下面會以程式碼為主。複製程式碼
AFNetworkReachabilityManager.h原始碼展示:
#import <Foundation/Foundation.h>
#if !TARGET_OS_WATCH
//SystemConfiguration.famework中提供和聯網相關的方法,可用來監測網路連線狀況
#import <SystemConfiguration/SystemConfiguration.h>
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusUnknown = -1,
AFNetworkReachabilityStatusNotReachable = 0,
AFNetworkReachabilityStatusReachableViaWWAN = 1,
AFNetworkReachabilityStatusReachableViaWiFi = 2,
};
NS_ASSUME_NONNULL_BEGIN
@interface AFNetworkReachabilityManager : NSObject
//當前的網路連線狀態
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
//當前網路是否連線
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
//當前網路連線的是否為蜂窩資料
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
//當前網路連線的是否為WiFi
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
#pragma 初始化方法-----------------------------------------------------------------------------
//單例物件,內部呼叫的是+ (instancetype)manager方法
+ (instancetype)sharedManager;
//根據預設的socket address建立一個manager物件
+ (instancetype)manager;
//根據指定域名初始化一個manager物件
+ (instancetype)managerForDomain:(NSString *)domain;
//根據指定的socket address初始化一個manager物件
+ (instancetype)managerForAddress:(const void *)address;
//根據指定的SCNetworkReachabilityRef物件來初始化一個manager物件,為指定初始化方法
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
//禁用new方法
+ (instancetype)new NS_UNAVAILABLE;
// 禁用init方法,如果呼叫這個方法,會丟擲異常
- (instancetype)init NS_UNAVAILABLE;
#pragma 提供的公開方法供外部呼叫-----------------------------------------------------------------------------
//開始檢測網路狀況,設定網路監測的回撥之前要先呼叫這個方法
- (void)startMonitoring;
//停止檢測網路狀況- (void)stopMonitoring;
// 返回一個本地化字串來展示當前的網路連線狀態
- (NSString *)localizedNetworkReachabilityStatusString;
//當網路檢測狀態發生改變時的回撥,也是我們最經常呼叫的方法
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;
@end
//網路狀態改變發出的通知
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;//AFNetworkingReachabilityDidChangeNotification通知的userInfo中status物件的key
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;// 返回一個本地化字串來展示當前的網路連線狀態 和- (NSString *)localizedNetworkReachabilityStatusString一樣
FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);
NS_ASSUME_NONNULL_END
#endif
複製程式碼
AFNetworkReachabilityManager.m原始碼展示:
#import "AFNetworkReachabilityManager.h"
#if !TARGET_OS_WATCH
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#import <netdb.h>
#pragma 定義的一些固定方法來方便操作===================================================================================
NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";
typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status);
//返回一個本地化字串來展示當前的網路連線狀態
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:儲存返回的測試連線狀態
將系統的SCNetworkReachabilityFlags轉換為我們自定義的AFNetworkReachabilityStatus
*/
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;
}
//網路監測狀態發生變化時的回撥,以及AFNetworkingReachabilityDidChangeNotification通知的建立和傳送
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];
});
}
//系統監測到網路狀態發生改變時的回撥
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}
//記憶體管理相關,retain
static const void * AFNetworkReachabilityRetainCallback(const void *info) {
return Block_copy(info);
}
//記憶體管理相關,release
static void AFNetworkReachabilityReleaseCallback(const void *info) {
if (info) {
Block_release(info);
}
}
@interface AFNetworkReachabilityManager ()
//用來儲存建立測試連線返回的引用
@property (readonly, nonatomic, assign) SCNetworkReachabilityRef networkReachability;
@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
@property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock;
@end
@implementation AFNetworkReachabilityManager
+ (instancetype)sharedManager {
static AFNetworkReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedManager = [self manager];
});
return _sharedManager;
}
+ (instancetype)managerForDomain:(NSString *)domain {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
+ (instancetype)managerForAddress:(const void *)address {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
+ (instancetype)manager
{
#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
{
@throw [NSException exceptionWithName:NSGenericException
reason:@"`-init` unavailable. Use `-initWithReachability:` instead"
userInfo:nil];
return nil;
}
- (void)dealloc {
[self stopMonitoring];
//銷燬時將_networkReachability物件釋放
if (_networkReachability != NULL) {
CFRelease(_networkReachability);
}
}
#pragma mark -
- (BOOL)isReachable {
return [self isReachableViaWWAN] || [self isReachableViaWiFi];
}
- (BOOL)isReachableViaWWAN {
return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN;
}
- (BOOL)isReachableViaWiFi {
return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi;
}
#pragma mark -
- (void)startMonitoring {
[self stopMonitoring];
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);
//加入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);
}
});
}
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
//停止網路監聽
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
#pragma mark -
- (NSString *)localizedNetworkReachabilityStatusString {
return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus);
}
#pragma mark -
- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {
self.networkReachabilityStatusBlock = block;
}
#pragma mark - NSKeyValueObserving
/*
這個是鍵值依賴,因為reachable,reachableViaWWAN,reachableViaWiFi的屬性都和networkReachabilityStatus的屬性有關,所以當你使用KVO監聽這三個屬性
時,networkReachabilityStatus的值一旦改變,也會觸發這三個屬性的改變,接下來我會專門寫一個專題為大家介紹KVO的詳情
*/
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
return [NSSet setWithObject:@"networkReachabilityStatus"];
}
return [super keyPathsForValuesAffectingValueForKey:key];
}
@end
#endif
複製程式碼
如何使用AFNetworkReachabilityManager這個類進行網路監聽:
第一種方法,使用block回撥:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
[manager startMonitoring];
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(@"%@",AFStringFromNetworkReachabilityStatus(status));
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
break;
case AFNetworkReachabilityStatusNotReachable:
break;
case AFNetworkReachabilityStatusUnknown:
default:
break;
}
}];
return YES;
}
複製程式碼
第二種方法,使用通知:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
[manager startMonitoring];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkStatuChange:) name:AFNetworkingReachabilityDidChangeNotification object:nil];
return YES;
}
-(void)networkStatuChange:(NSNotification *)noti
{
AFNetworkReachabilityStatus status =[noti.userInfo[AFNetworkingReachabilityNotificationStatusItem] integerValue];
NSLog(@"%@",AFStringFromNetworkReachabilityStatus(status));
}複製程式碼
其實AFNetworkReachabilityManager的原始碼還是比較容易理解的,只要有點耐心,邏輯還是很容易理通的。