AFNetworkReachabilityManager 閱讀筆記
AFNetworkReachabilityManager 是 AFNetworking 中用來監(jiān)聽網(wǎng)絡(luò)可達(dá)性的組件,(有沒有網(wǎng)絡(luò),什么網(wǎng)絡(luò))之類的奕坟。
ps:不能根據(jù)這個狀態(tài)來阻止用戶發(fā)送網(wǎng)絡(luò)請求泳桦。
我們項目中碱璃,對網(wǎng)絡(luò)狀態(tài)變化做了一層封裝菩鲜,如下:
@interface PTVNetworkStatus()
@property(strong,nonatomic)AFHTTPSessionManager* session;
@end
@implementation PTVNetworkStatus
-(id)init
{
if (self = [super init]) {
_session = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://api.m.panda.tv"]];
_session.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
[_session.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"-------AFNetworkReachabilityStatusReachableViaWWAN------");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"-------AFNetworkReachabilityStatusReachableViaWiFi------");
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"-------AFNetworkReachabilityStatusNotReachable------");
break;
default:
break;
}
if (_status != status) {
_status = status;
[[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:nil];
}
}];
[_session.reachabilityManager startMonitoring];
}
return self;
}
我覺得這個包裝有問題览徒。所以決定深入看一下代碼事哭,
_session = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://xxxxx.xxxxx"]];
_session.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
這里initWithBaseUrl 的參數(shù) https://xxxxx.xxxxx 對網(wǎng)絡(luò)狀態(tài)監(jiān)聽并沒有影響漫雷。
糾錯
我們看看 AFHTTPSessionManager 的初始化函數(shù):
在 AFURLSessionManager 的 init 函數(shù)中有默認(rèn)的 reachabilityManager 實(shí)現(xiàn)。
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
默認(rèn)的實(shí)現(xiàn)是不會用到剛才傳入的參數(shù)的鳍咱。
+ (instancetype)sharedManager {
static AFNetworkReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
_sharedManager = [self managerForAddress:&address];
});
return _sharedManager;
}
所以項目中的正確用法應(yīng)該是使用 managerForDomain 函數(shù)進(jìn)行初始化 AFNetworkReachabilityManager 對象降盹。然后使用 setReachabilityStatusChangeBlock 監(jiān)聽網(wǎng)絡(luò)狀態(tài)。谤辜。蓄坏。
比如:
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager managerForDomain:@"www.google.com"];
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status){
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
case AFNetworkReachabilityStatusReachableViaWiFi:
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"Never called");
break;
default:
NSLog(@"Never called");
break;
}
}];
[manager startMonitoring];
如果項目要包裝 AFNetworkReachabilityManager 也不建議在 block 中直接發(fā)送通知价捧,因?yàn)槎荚谕粋€類,如果通知忘記清空涡戳,倒是還有可能導(dǎo)致崩潰结蟋。
建議使用target/action 的方式,包裝渔彰!一個列表嵌屎,足以維護(hù)某個類需要的網(wǎng)絡(luò)請求。
AFNetworkReachabilityManager 實(shí)現(xiàn)原理恍涂。
主要是基于 SCNetworkReachabilityRef 對網(wǎng)絡(luò)狀態(tài)進(jìn)行監(jiān)聽的宝惰,系統(tǒng)本身已經(jīng)支持監(jiān)聽網(wǎng)絡(luò)狀態(tài),只是C 語言的形式再沧,加上函數(shù)指針等等欧芽,對iOS 開發(fā)者族铆,并不是很友好使用起來。
所以弄懂 AFNetworkReachabilityManager 只要點(diǎn)擊看看 SCNetworkReachabilityRef 的相關(guān)函數(shù)以及文檔就好。抚垃。
+ (instancetype)managerForDomain:(NSString *)domain {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
manager.networkReachabilityAssociation = AFNetworkReachabilityForName;
return manager;
}
上面的函數(shù)式依據(jù)domain 創(chuàng)建一個 監(jiān)聽網(wǎng)絡(luò)的對象紫岩。首先先創(chuàng)建一個 SCNetworkReachabilityRef 引用火邓。
SCNetworkReachabilityRef的官方說明如下:
The SCNetworkReachability API allows an application to
determine the status of a system's current network
configuration and the reachability of a target host.
In addition, reachability can be monitored with notifications
that are sent when the status has changed.
重點(diǎn)在于 函數(shù) startMonitoring
- (void)startMonitoring {
//先停止當(dāng)前的監(jiān)聽
[self stopMonitoring];
///如果沒有 SCNetworkReachabilityRef 不能監(jiān)聽
if (!self.networkReachability) {
return;
}
///創(chuàng)建網(wǎng)絡(luò)變化回調(diào)的block
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
id networkReachability = self.networkReachability;
///創(chuàng)建 SCNetworkReachabilityContext 結(jié)構(gòu)袖订,結(jié)構(gòu)包含了用戶指定的信息,和回調(diào)函數(shù)
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
///設(shè)置網(wǎng)絡(luò)變化的回調(diào)
SCNetworkReachabilitySetCallback((__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context);
///指定網(wǎng)絡(luò)監(jiān)聽的runloop
SCNetworkReachabilityScheduleWithRunLoop((__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
switch (self.networkReachabilityAssociation) {
case AFNetworkReachabilityForName:
break;
case AFNetworkReachabilityForAddress:
case AFNetworkReachabilityForAddressPair:
default: {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags);
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
dispatch_async(dispatch_get_main_queue(), ^{
callback(status);
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:@{ AFNetworkingReachabilityNotificationStatusItem: @(status) }];
});
});
}
break;
}
}
開始監(jiān)聽函數(shù)的實(shí)現(xiàn)過程屎即。
停止監(jiān)聽的過程如下:
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
直接從runloop中移除當(dāng)前的網(wǎng)絡(luò)監(jiān)聽對象庙睡、
小結(jié)
使用第三方功能的時候,一定要對源碼有足夠的了解才行技俐。