AFNetworking原始碼解析系列(1)

lmg4819發表於2018-05-31

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的原始碼還是比較容易理解的,只要有點耐心,邏輯還是很容易理通的。


相關文章