AFNetworking 3.0 源码解读(1)

AFNetworkReachabilityManager (监控网络环境变化的类)

这个分析是我在读AFNetworking 3.0 源码解系列写的,很多想法都是来自他的文章,非常感谢作者的分享精神!

//AFNetworkReachabilityManager.h

分析

1.这个类是基于SCNetworkReachabilityRef这个系统类来实现的,提供了和联网相关的函数。可参考这个
2.打开和关闭监听网络变化的方法

/**
 Starts monitoring for changes in network reachability status.
 */
- (void)startMonitoring;

/**
 Stops monitoring for changes in network reachability status.
 */
- (void)stopMonitoring;

3.网络状态改变时,监听的回调有两种方法:
a.通过监听AFNetworkingReachabilityDidChangeNotification这个通知。
b.通过下面这个方法。

- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;

启发

1.可以用一些特殊的格式注释,或者pragma mark等来进行分隔不同功能模块的代码。如下图@name Initialization

@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;

///---------------------
/// @name Initialization
///---------------------

/**
 Returns the shared network reachability manager.
 */
+ (instancetype)sharedManager;

2.BOOL类型的值,若只允许访问可设置成getter,然后给予一个更明确的gettersetter方法名来替代默认生成的方法名(即变量名、set+变量名)。
在.h中声明为readonly的属性,可以再类别中再声明一次为readwrite,这样在.m中可以进行读写。

//.h
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
//注意这里是`readonly`
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;

//.m
@interface AFNetworkReachabilityManager ()
//这里是`readwrite`
@property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
@end

- (BOOL)isReachable {
    return [self isReachableViaWWAN] || [self isReachableViaWiFi];
}

3.关于定义常量方面的格式一般有两种,普通的就是#define kMyConstantString @"Hello",另一种如下:

//.h
///--------------------
/// @name Notifications
///--------------------
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityNotificationStatusItem;

///--------------------
/// @name Functions
///--------------------

/**
 Returns a localized string representation of an `AFNetworkReachabilityStatus` value.
 */
FOUNDATION_EXPORT NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status);

//.m
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);
    }
}

使用第二种方式定义的字符串,在比较的时候速度更快,可以直接使用(stringInstance == MyFirstConstant)来比较,而define则使用的是这种.([stringInstance isEqualToString:MyFirstConstant])
第二种直接比较的是指针地址,而第一个则是一一比较字符串的每一个字符是否相等。
这种定义可以多用于NSNotification的字符串定义。

4.NS_DESIGNATED_INITIALIZERNS_UNAVAILABLE的配合使用:

- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;

/**
 *  Initializes an instance of a network reachability manager
 *
 *  @return nil as this method is unavailable
 */
- (nullable instancetype)init NS_UNAVAILABLE;

NS_DESIGNATED_INITIALIZER用来标记指定构造器,即你希望初始化对象时所使用的指定函数。如果子类指定了新的初始化器,那么在这个初始化器内部必须调用父类的Designated Initializer。并且需要重写父类的Designated Initializer,将其指向子类新的初始化器。如下:

// .h
- (instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;

// .m

- (instancetype)init{
    return [self initWithName:@""];
}

- (instancetype)initWithName:(NSString *)name{

    self = [super init];
    if (self) {
        // do something
    }
    return self;
}

但是往往,我们是不希望子类用其他的初始化方法的,所以可以用NS_UNAVAILABLE来标记继承来的init方法,让它变为不可用,成为私有的方法。即外部调用alloc init会报错。

//AFNetworkReachabilityManager.m

分析

1.网络状态改变时的通知

//网络环境发生改变时接收的通知
NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
//上面的通知发送时,会携带一组状态数据,根据这个key从userinfo中取出网络status
NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem";

//使用
- (void)verifyReachabilityNotificationGetsPostedWithManager:(AFNetworkReachabilityManager *)manager
{
    [self expectationForNotification:AFNetworkingReachabilityDidChangeNotification
                              object:nil
                             handler:^BOOL(NSNotification *note) {
                                 AFNetworkReachabilityStatus status;
                                 status = [note.userInfo[AFNetworkingReachabilityNotificationStatusItem] integerValue];
                                 BOOL isReachable = (status == AFNetworkReachabilityStatusReachableViaWiFi
                                                     || status == AFNetworkReachabilityStatusReachableViaWWAN);
                                 return isReachable;
                             }];

    [manager startMonitoring];

    [self waitForExpectationsWithCommonTimeout];
}

2.将SCNetworkReachabilityRef文件中用来表示网络状态的SCNetworkReachabilityFlags 转化成平时常用的状态,即本类AFNetworkReachabilityStatus的值。

/**
 *  根据SCNetworkReachabilityFlags这个网络标记来转换成我们在开发中经常使用的网络状态
     1.不能连接网络
     2.蜂窝连接
     3.WiFi连接
     4.未知连接
 */
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);

    // 是否可以联网的条件 1.能够到达 2.不需要建立连接或者不需要用户手动设置连接 就表示能够连接到网络
    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;
}

3.监听网络变化有两个方法,block和NSNotification, 为了让这两个方法的值保持一致,将两个过程封装到同一个函数中,一旦发生网络变化,对调用这个函数

static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
    AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
    dispatch_async(dispatch_get_main_queue(), ^{
        if (block) {
            //有block就调用block
            block(status);
        }
        //发通知
        NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
        //用到了AFNetworkingReachabilityNotificationStatusItem作为key来存status,接收到通知时,再通过这个key取出status
        NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
        [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
    });
}

4.单例创建方法,注意宏定义,这里的宏定义可以减少书写ifelse的判断

+ (instancetype)sharedManager {
    static AFNetworkReachabilityManager *_sharedManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedManager = [self manager];
    });

    return _sharedManager;
}
+ (instancetype)manager {
    //涉及到IPv6所以要判断版本
    //__IPHONE_OS_VERSION_MIN_REQUIRED iphone系统
    //__IPHONE_OS_VERSION_MIN_REQUIRED >= 90000 系统版本9.0及以上
#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];
}

5.开启网络监听
首先要了解一下SCNetworkReachabilityContext,这是一个结构体:

typedef struct {
    CFIndex     version;
    void *      __nullable info;
    const void  * __nonnull (* __nullable retain)(const void *info);
    void        (* __nullable release)(const void *info);
    CFStringRef __nonnull (* __nullable copyDescription)(const void *info);
} SCNetworkReachabilityContext;
//1. 第一个参数接受一个signed long 的参数

//2. 第二个参数info接受一个void * 类型的值,相当于oc的id类型,void * 可以指向任何类型的参数,本类中的info是一个block.

//3. 第三个参数 是一个函数 目的是对info做retain操作,

//4. 第四个参数是一个函数,目的是对info做release操作

//5. 第五个参数是 一个函数,根据info获取Description字符串

retain和release 函数是下边的这两个函数,写在.m文件最前面

static const void * AFNetworkReachabilityRetainCallback(const void *info) {
    return Block_copy(info);
}

static void AFNetworkReachabilityReleaseCallback(const void *info) {
    if (info) {
        Block_release(info);
    }
}

具体开启监控的代码:

- (void)startMonitoring {
    [self stopMonitoring];

    if (!self.networkReachability) {
        return;
    }

    //这个block就是用来传入SCNetworkReachabilityContext的info
    __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};
    //设置回调,AFNetworkReachabilityCallback执行时可以传入一个block作为参数,这里传入的就是上面的callback,即context里的info
    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);
        }
    });
}

其中AFNetworkReachabilityCallback的定义如下,是根据SCNetworkReachabilitySetCallback第二个参数类型来声明的:

//target其实在这里并没有使用到,这是为了满足SCNetworkReachabilitySetCallback参数的定义而声明的,所以用__unused声明
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
    AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}

6.键值依赖,这段代码在.m文件的最后,可以温习一下kvckvo的相关知识。

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
    if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) {
        return [NSSet setWithObject:@"networkReachabilityStatus"];
    }

    return [super keyPathsForValuesAffectingValueForKey:key];
}

启发

1.NS_ENUMNS_OPTIONS都是常用的枚举声明方法,NS_OPTIONS是按位掩码,NS_ENUM枚举项的值为NSInteger,NS_OPTIONS枚举项的值为NSUInteger;NS_ENUM定义通用枚举,NS_OPTIONS定义位移枚举。位移枚举即是在你需要的地方可以同时存在多个枚举值,而NS_ENUM定义的枚举不能几个枚举项同时存在,只能选择其中一项。
NS_OPTIONS可以表示几个选项的各种组合,用|表示,当判断这个组合是否包含某个单独的枚举值时可以进行&操作。如分析.m的第一点中代码所示。

typedef NS_OPTIONS(NSUInteger, UISwipeGestureRecognizerDirection) {
    UISwipeGestureRecognizerDirectionNone = 0,  //值为0
    UISwipeGestureRecognizerDirectionRight = 1 << 0,  //值为2的0次方
    UISwipeGestureRecognizerDirectionLeft = 1 << 1,  //值为2的1次方
    UISwipeGestureRecognizerDirectionUp = 1 << 2,  //值为2的2次方
    UISwipeGestureRecognizerDirectionDown = 1 << 3  //值为2的3次方
};

2.类中的私有方法可以用c函数来写,eg:static void funcName()
a.在文件的最前方,比较容易查找
b. 可以适当的使用内联函数(static inline),内联函数在编译的时候,会把代码直接嵌入调用代码中。就相当于用#define 宏定义来定义一个 函数,避免了使用call指令,提高效率

//.m
static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {...}

static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {...}

@interface AFNetworkReachabilityManager ()
//.......
@end

@implementation AFNetworkReachabilityManager
//......
@end

3.__unused前缀,就可以免除警告。其原理是告诉编译器,如果变量未使用就不参与编译。

static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
    AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值