2010-12-13 続きのブログを書きました。そちらも合わせて読んでみてください。
Cocoaの日々: ネットワーク接続状況を知る[2] SCNetworkReachabilityGetFlags はブロックする
Cocoaの日々: ネットワーク接続状況を知る[2] SCNetworkReachabilityGetFlags はブロックする
ネットワーク接続状況を知るための API等を調べてみた。前半はアップル提供のサンプルコードについて、後半は自作サンプルについて(ソースコードは GitHubで公開)。
アップル提供のサンプルコード Reachability
SystemConfiguration フレームワークを使うとネットワークの接続状況(WiFi利用または3G回線利用か、接続不可か)を知ることができる。
iOS Reference Library に Reachability というサンプルがある。
Reachability: Classes/Reachability.m
WiFi接続時
3G使用時
フライトモード時
この程度の情報が取れることがわかる。
利用は、まず SCNetworkReachabilityRef を取得する。
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);用途に応じて何種類かの関数が用意されている。
SCNetworkReachabilityCreateWithAddress SCNetworkReachabilityCreateWithAddressPair SCNetworkReachabilityCreateWithName
次に取得した SCNetworkReachabilityRef を SCNetworkReachabilityGetFlags()へ渡して SCNetworkReachabilityFlags を得る。この値はビットで接続状況を表している。
enum {
kSCNetworkReachabilityFlagsTransientConnection = 1<<0,
kSCNetworkReachabilityFlagsReachable = 1<<1,
kSCNetworkReachabilityFlagsConnectionRequired = 1<<2,
kSCNetworkReachabilityFlagsConnectionOnTraffic = 1<<3,
kSCNetworkReachabilityFlagsInterventionRequired = 1<<4,
kSCNetworkReachabilityFlagsConnectionOnDemand = 1<<5,
kSCNetworkReachabilityFlagsIsLocalAddress = 1<<16,
kSCNetworkReachabilityFlagsIsDirect = 1<<17,
kSCNetworkReachabilityFlagsIsWWAN = 1<<18,
kSCNetworkReachabilityFlagsConnectionAutomatic
= kSCNetworkReachabilityFlagsConnectionOnTraffic
};
typedef uint32_t SCNetworkReachabilityFlags;これらのビットをチェックすることで 3G経由、WiFi経由、非接続の判断ができる。自作サンプル
Apple提供のサンプルは若干複雑なので、簡略化したサンプルを作ってみる。サンプル作成にあたっては下記が参考になった。 How to check for local Wi-Fi (not just cellular connection) using iPhone SDK? - Stack Overflow これを参考に簡単なサンプルを作ってみた。NetworkReachability というクラスを用意し、このクラスに接続状況のチェックをさせる。
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
enum {
kNetworkReachableNon = 0,
kNetworkReachableWiFi,
kNetworkReachableWWAN
};
#define NetworkReachabilityChangedNotification
@"NetworkReachabilityChangedNotification"
@interface NetworkReachability : NSObject {
SCNetworkReachabilityRef reachability_;
}
- (id)initWithHostname:(NSString*)hostname;
- (NSInteger)getConnectionMode;
- (BOOL) startNotifier;
- (void) stopNotifier;
@end接続判定箇所。 - (int)getConnectionMode
{
if (reachability_) {
SCNetworkReachabilityFlags flags = 0;
SCNetworkReachabilityGetFlags(reachability_, &flags);
BOOL isReachable = ((flags & kSCNetworkFlagsReachable) != 0);
BOOL needsConnection = ((flags & kSCNetworkFlagsConnectionRequired) != 0);
if (isReachable && !needsConnection) {
if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
return kNetworkReachableWWAN;
}
if ([self getWiFiIPAddress]) {
return kNetworkReachableWiFi;
}
}
}
return kNetworkReachableNon;
}WiFi かどうかの判断箇所。 - (NSString*)getWiFiIPAddress
{
BOOL success;
struct ifaddrs * addrs;
const struct ifaddrs * cursor;
success = getifaddrs(&addrs) == 0;
if (success) {
cursor = addrs;
while (cursor != NULL) {
if (cursor->ifa_addr->sa_family == AF_INET
&& (cursor->ifa_flags & IFF_LOOPBACK) == 0) {
NSString *name =
[NSString stringWithUTF8String:cursor->ifa_name];
if ([name isEqualToString:@"en0"]) { // found the WiFi adapter
return [NSString stringWithUTF8String:
inet_ntoa(((struct sockaddr_in *)cursor->ifa_addr)->sin_addr)];
NSString* addressString = [NSString stringWithUTF8String:
inet_ntoa(((struct sockaddr_in *)cursor->ifa_addr)->sin_addr)];
freeifaddrs(addrs);
return addressString;
}
}
cursor = cursor->ifa_next;
}
freeifaddrs(addrs);
}
return NULL;
}コードはほぼ下記から拝借した。 Matt Brown - Journal of a Software Engineer: How to get the IP address of an iPhone OS v2.2.1 シミュレータ上で実行した場合、WiFiに割り当てられているネットワークが必ずしも en0 ではない(大抵は en1)ので、接続なし表示になることがある。 接続状況に変化があった場合は Notificationをポストする。これはアップルのサンプルから拝借した。もともと用意されている SCNetworkReachabilitySetCallback() を使い Notificationポストへ変換している。 static void ReachabilityCallback(
SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
NSAutoreleasePool* myPool = [[NSAutoreleasePool alloc] init];
NetworkReachability* noteObject = (NetworkReachability*)info;
[[NSNotificationCenter defaultCenter]
postNotificationName:NetworkReachabilityChangedNotification object:noteObject];
[myPool release];
}
- (BOOL)startNotifier
{
BOOL ret = NO;
SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL};
if(SCNetworkReachabilitySetCallback(reachability_, ReachabilityCallback, &context))
{
if(SCNetworkReachabilityScheduleWithRunLoop(
reachability_, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
{
ret = YES;
}
}
return ret;
}
- (void) stopNotifier
{
if(reachability_!= NULL)
{
SCNetworkReachabilityUnscheduleFromRunLoop(
reachability_, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
}実行するとこんな感じ。シミュレータを使って状態の変化を知るには実行後に Macの Wifiをオフにする。 Notificationがポストされて表示が切り替わる。実機の場合は、設定画面を開き WiFiを切ったり、フライトモードにしたりすると確認できる。ただしバックグラウンド実行に準拠していないので Notificationが受け取れるのはサンプルアプリを再び起動した時のみ。また検出までは数秒かかる。
- - - -
ソースコードは GitHub からどうぞ。
|
|2010-10-21 訂正
↓
|
|2010-11-14 訂正
↓
NetworkReachable/Classes at 2010-08-13c from xcatsan's iOS-Sample-Code - GitHub
(差分)
xcatsan's iOS-Sample-Code at 2010-08-13c - GitHub
参考
- SCNetworkReachability Reference
- SCNetworkReachabilityのリファレンス






のむら says:
2010年10月20日 16:58
こんにちは。git のソースコードを DL させていただきました。ありがとうございます。
NetworkReachability.m の dealloc の中で CFRetain となっている箇所は、CFRelease ではないでしょうか?
のむら says:
2010年10月20日 16:58
こんにちは。git のソースコードを DL させていただきました。ありがとうございます。
NetworkReachability.m の dealloc の中で CFRetain となっている箇所は、CFRelease ではないでしょうか?
xcatsan says:
2010年10月21日 15:06
Shinichi NOMURA さん、こんにちは。
CFRetain => CFRelease の件は指摘の通りです。以前、別の方にも指摘されて直したつもりが直っていませんでした;;
下記を使ってください。
http://github.com/xcatsan/iOS-Sample-Code/tree/2010-08-13b/NetworkReachable/
ブログも後ほど修正しておきます。
指摘ありがとうございました。
xcatsan says:
2010年10月21日 15:06
Shinichi NOMURA さん、こんにちは。
CFRetain => CFRelease の件は指摘の通りです。以前、別の方にも指摘されて直したつもりが直っていませんでした;;
下記を使ってください。
http://github.com/xcatsan/iOS-Sample-Code/tree/2010-08-13b/NetworkReachable/
ブログも後ほど修正しておきます。
指摘ありがとうございました。
のむら says:
2010年11月13日 23:04
度々恐れ入ります…ソースを使わせていただいていて気づいたのですが、最新版 NetworkReachability.m の57行目でメモリリークがあるように思います。
return の前に freeifaddrs(addrs);
が必要ではないでしょうか?
なお別件ではありますが、Core Data の記事等も大変参考になりました。この場を借りて御礼申し上げます(笑)
のむら says:
2010年11月13日 23:04
度々恐れ入ります…ソースを使わせていただいていて気づいたのですが、最新版 NetworkReachability.m の57行目でメモリリークがあるように思います。
return の前に freeifaddrs(addrs);
が必要ではないでしょうか?
なお別件ではありますが、Core Data の記事等も大変参考になりました。この場を借りて御礼申し上げます(笑)
xcatsan says:
2010年11月14日 8:52
NOMURAさん、おはようございます。
freeifaddrs() は、指摘の通りのようです。
探してみると NOMURAさん同様、指摘している記事が見つかりました。
「[iPhone]Leakを使ってエリカ本のリークを発見」
http://ameblo.jp/k-power2/archive1-201004.html
↑ ずばりメモリリークが発生していますね。
とりあえず上記を参考に修正を入れておきました。
今回も指摘ありがとうございます。
とても助かりました。
xcatsan says:
2010年11月14日 8:52
NOMURAさん、おはようございます。
freeifaddrs() は、指摘の通りのようです。
探してみると NOMURAさん同様、指摘している記事が見つかりました。
「[iPhone]Leakを使ってエリカ本のリークを発見」
http://ameblo.jp/k-power2/archive1-201004.html
↑ ずばりメモリリークが発生していますね。
とりあえず上記を参考に修正を入れておきました。
今回も指摘ありがとうございます。
とても助かりました。