这个分析是我在读AFNetworking 3.0 源码解系列写的,很多想法都是来自他的文章,非常感谢作者的分享精神!
1.这个类是基于SCNetworkReachabilityRef
这个系统类来实现的,提供了和联网相关的函数。可参考这个。 2.打开和关闭监听网络变化的方法
3.网络状态改变时,监听的回调有两种方法: a.通过监听AFNetworkingReachabilityDidChangeNotification
这个通知。 b.通过下面这个方法。
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
,然后给予一个更明确的getter
、setter
方法名来替代默认生成的方法名(即变量名、set+变量名)。 在.h中声明为readonly
的属性,可以再类别中再声明一次为readwrite
,这样在.m中可以进行读写。
3.关于定义常量方面的格式一般有两种,普通的就是#define kMyConstantString @"Hello"
,另一种如下:
使用第二种方式定义的字符串,在比较的时候速度更快,可以直接使用(stringInstance == MyFirstConstant)
来比较,而define则使用的是这种.([stringInstance isEqualToString:MyFirstConstant])
。 第二种直接比较的是指针地址,而第一个则是一一比较字符串的每一个字符是否相等。 这种定义可以多用于NSNotification
的字符串定义。
4.NS_DESIGNATED_INITIALIZER
和 NS_UNAVAILABLE
的配合使用:
NS_DESIGNATED_INITIALIZER
用来标记指定构造器,即你希望初始化对象时所使用的指定函数。如果子类指定了新的初始化器,那么在这个初始化器内部必须调用父类的Designated Initializer
。并且需要重写父类的Designated Initializer
,将其指向子类新的初始化器。如下:
但是往往,我们是不希望子类用其他的初始化方法的,所以可以用NS_UNAVAILABLE
来标记继承来的init
方法,让它变为不可用,成为私有的方法。即外部调用alloc init
会报错。
1.网络状态改变时的通知
//网络环境发生改变时接收的通知NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";//上面的通知发送时,会携带一组状态数据,根据这个key从userinfo中取出网络statusNSString * 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
的值。
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
,这是一个结构体:
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
第二个参数类型来声明的:
6.键值依赖,这段代码在.m文件的最后,可以温习一下kvc、kvo的相关知识。
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) { return [NSSet setWithObject:@"networkReachabilityStatus"]; } return [super keyPathsForValuesAffectingValueForKey:key];}1.NS_ENUM
和NS_OPTIONS
都是常用的枚举声明方法,NS_OPTIONS
是按位掩码,NS_ENUM枚举项的值为NSInteger,NS_OPTIONS枚举项的值为NSUInteger;NS_ENUM定义通用枚举,NS_OPTIONS定义位移枚举。位移枚举即是在你需要的地方可以同时存在多个枚举值,而NS_ENUM定义的枚举不能几个枚举项同时存在,只能选择其中一项。 NS_OPTIONS
可以表示几个选项的各种组合,用|
表示,当判断这个组合是否包含某个单独的枚举值时可以进行&操作。如分析.m的第一点中代码所示。
2.类中的私有方法可以用c函数来写,eg:static void funcName()
a.在文件的最前方,比较容易查找 b. 可以适当的使用内联函数(static inline),内联函数在编译的时候,会把代码直接嵌入调用代码中。就相当于用#define 宏定义来定义一个 函数,避免了使用call指令,提高效率
3.__unused
前缀,就可以免除警告。其原理是告诉编译器,如果变量未使用就不参与编译。
新闻热点
疑难解答