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
↑ ずばりメモリリークが発生していますね。
とりあえず上記を参考に修正を入れておきました。
今回も指摘ありがとうございます。
とても助かりました。