我正在尝试将this代码段转换为Swift。由于一些困难,我正在努力开始起飞。
- (BOOL) connectedToNetwork
{
// Create zero addy
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
// Recover reachability flags
SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
SCNetworkReachabilityFlags flags;
BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
CFRelease(defaultRouteReachability);
if (!didRetrieveFlags)
{
return NO;
}
BOOL isReachable = flags & kSCNetworkFlagsReachable;
BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
return (isReachable && !needsConnection) ? YES : NO;
}
我遇到的第一个和主要问题是如何定义和使用C结构。在上面代码的第一行(struct sockaddr_in zeroAddress;
)中,我认为它们是从struct sockaddr_in(?)定义一个名为zeroAddress
的实例,我假设。我试着像这样宣布一个var
。
var zeroAddress = sockaddr_in()
但是我在调用中得到参数'sin_len'的错误缺失参数,这是可以理解的,因为该结构需要许多参数。所以我又试了一次。
var zeroAddress = sockaddr_in(sin_len: sizeof(zeroAddress), sin_family: AF_INET, sin_port: nil, sin_addr: nil, sin_zero: nil)
正如预期的那样,我在其自己的初始值中使用了一些其他错误变量。我也理解这个错误的原因。在C中,它们首先声明实例,然后填充参数。据我所知,它在Swift中是不可能的。所以我真的迷失了这一点。
我在Swift中阅读了Apple的官方document与C API的交互,但它没有使用结构的例子。
有人可以帮帮我吗?我真的很感激。
谢谢。
更新:感谢Martin,我能够解决最初的问题。但斯威夫特仍然没有让我更容易。我收到了多个新错误。
func connectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(UnsafePointer<Void>, UnsafePointer<zeroAddress>) // 'zeroAddress' is not a type
var flags = SCNetworkReachabilityFlags()
let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, UnsafeMutablePointer<flags>) // 'flags' is not a type
defaultRouteReachability.dealloc(1) // 'SCNetworkReachabilityRef' does not have a member named 'dealloc'
if didRetrieveFlags == false {
return false
}
let isReachable: Bool = flags & kSCNetworkFlagsReachable // Cannot invoke '&' with an argument list of type '(@lvalue UInt32, Int)'
let needsConnection: Bool = flags & kSCNetworkFlagsConnectionRequired // Cannot invoke '&' with an argument list of type '(@lvalue UInt32, Int)'
return (isReachable && !needsConnection) ? true : false
}
编辑1:好的,我改变了这一行,
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(UnsafePointer<Void>(), &zeroAddress)
我在这一行得到的新错误是'UnsafePointer'无法转换为'CFAllocator'。你怎么在斯威夫特传递NULL
?
我也改变了这一行,错误现在消失了。
let didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags)
编辑2:在看到nil
问题后,我在这条线上通过了this。但这个答案与答案here相矛盾。它说在斯威夫特没有相当于NULL
。
var defaultRouteReachability: SCNetworkReachabilityRef = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress)
无论如何,我得到一个新的错误,说'sockaddr_in'与上面一行的'sockaddr'不同。
(由于Swift语言的变化,这个答案反复扩展,这使得它有点令人困惑。我现在已经重写了它并删除了所有引用Swift 1.x的内容。如果有人需要,可以在编辑历史中找到旧代码它。)
这就是你在Swift 2.0(Xcode 7)中的表现:
import SystemConfiguration
func connectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
guard let defaultRouteReachability = withUnsafePointer(&zeroAddress, {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}) else {
return false
}
var flags : SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
return false
}
let isReachable = flags.contains(.Reachable)
let needsConnection = flags.contains(.ConnectionRequired)
return (isReachable && !needsConnection)
}
说明:
var zeroAddress = sockaddr_in()
sizeofValue()
给出了这个结构的大小,这必须转换为UInt8
为sin_len
:
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
AF_INET
是一个Int32
,这必须转换为sin_family
的正确类型:
zeroAddress.sin_family = sa_family_t(AF_INET)
withUnsafePointer(&zeroAddress) { ... }
将结构的地址传递给闭包,在那里它被用作SCNetworkReachabilityCreateWithAddress()
的参数。需要进行UnsafePointer($0)
转换,因为该函数需要指向sockaddr
的指针,而不是sockaddr_in
。withUnsafePointer()
返回的值是来自SCNetworkReachabilityCreateWithAddress()
的返回值,其类型为SCNetworkReachability?
,即它是可选的。 guard let
语句(Swift 2.0中的一个新特性)将未包装的值分配给defaultRouteReachability
变量(如果它不是nil
)。否则执行else
块并返回该函数。SCNetworkReachabilityCreateWithAddress()
返回一个托管对象。您不必明确释放它。SCNetworkReachabilityFlags
符合OptionSetType
,它具有类似集合的界面。您使用创建一个空标志变量
var flags : SCNetworkReachabilityFlags = []
并检查标志
let isReachable = flags.contains(.Reachable)
let needsConnection = flags.contains(.ConnectionRequired)
SCNetworkReachabilityGetFlags
的第二个参数的类型为UnsafeMutablePointer<SCNetworkReachabilityFlags>
,这意味着你必须传递flags变量的地址。另请注意,从Swift 2开始,可以注册通知程序回调,比较Working with C APIs from Swift和Swift 2 - UnsafeMutablePointer<Void> to object。
Swift 3/4更新:
不安全的指针不能简单地转换为不同类型的指针(参见 - SE-0107 UnsafeRawPointer API)。这里更新的代码:
import SystemConfiguration
func connectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
zeroAddress.sin_family = sa_family_t(AF_INET)
guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
}) else {
return false
}
var flags: SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
return false
}
let isReachable = flags.contains(.reachable)
let needsConnection = flags.contains(.connectionRequired)
return (isReachable && !needsConnection)
}
Swift 3,IPv4,IPv6
根据Martin R的回答:
import SystemConfiguration
func isConnectedToNetwork() -> Bool {
guard let flags = getFlags() else { return false }
let isReachable = flags.contains(.reachable)
let needsConnection = flags.contains(.connectionRequired)
return (isReachable && !needsConnection)
}
func getFlags() -> SCNetworkReachabilityFlags? {
guard let reachability = ipv4Reachability() ?? ipv6Reachability() else {
return nil
}
var flags = SCNetworkReachabilityFlags()
if !SCNetworkReachabilityGetFlags(reachability, &flags) {
return nil
}
return flags
}
func ipv6Reachability() -> SCNetworkReachability? {
var zeroAddress = sockaddr_in6()
zeroAddress.sin6_len = UInt8(MemoryLayout<sockaddr_in>.size)
zeroAddress.sin6_family = sa_family_t(AF_INET6)
return withUnsafePointer(to: &zeroAddress, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
})
}
func ipv4Reachability() -> SCNetworkReachability? {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
zeroAddress.sin_family = sa_family_t(AF_INET)
return withUnsafePointer(to: &zeroAddress, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
})
}
这与Swift无关,但最好的解决方案是不使用Reachability来确定网络是否在线。如果失败,只需建立连接并处理错误。建立连接有时可以启动休眠的离线无线电。
可达性的一个有效用途是使用它来在网络从离线转换到在线时通知您。此时,您应该重试失败的连接。
更新了juanjo的创建单例实例的答案
Swift 2
用法
SCNetworkReachabilityRef
这是在Swift 4.0中
我正在使用这个框架let reachability = Reachability.reachabilityForInternetConnection()
reachability?.whenReachable = { reachability in
// keep in mind this is called on a background thread
// and if you are updating the UI it needs to happen
// on the main thread, like this:
dispatch_async(dispatch_get_main_queue()) {
if reachability.isReachableViaWiFi() {
print("Reachable via WiFi")
} else {
print("Reachable via Cellular")
}
}
}
reachability?.whenUnreachable = { reachability in
// keep in mind this is called on a background thread
// and if you are updating the UI it needs to happen
// on the main thread, like this:
dispatch_async(dispatch_get_main_queue()) {
print("Not reachable")
}
}
reachability?.startNotifier()
并安装Pod ..
在AppDelegate中
import Foundation
import SystemConfiguration
final class Reachability {
private init () {}
class var shared: Reachability {
struct Static {
static let instance: Reachability = Reachability()
}
return Static.instance
}
func isConnectedToNetwork() -> Bool {
guard let flags = getFlags() else { return false }
let isReachable = flags.contains(.reachable)
let needsConnection = flags.contains(.connectionRequired)
return (isReachable && !needsConnection)
}
private func getFlags() -> SCNetworkReachabilityFlags? {
guard let reachability = ipv4Reachability() ?? ipv6Reachability() else {
return nil
}
var flags = SCNetworkReachabilityFlags()
if !SCNetworkReachabilityGetFlags(reachability, &flags) {
return nil
}
return flags
}
private func ipv6Reachability() -> SCNetworkReachability? {
var zeroAddress = sockaddr_in6()
zeroAddress.sin6_len = UInt8(MemoryLayout<sockaddr_in>.size)
zeroAddress.sin6_family = sa_family_t(AF_INET6)
return withUnsafePointer(to: &zeroAddress, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
})
}
private func ipv4Reachability() -> SCNetworkReachability? {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
zeroAddress.sin_family = sa_family_t(AF_INET)
return withUnsafePointer(to: &zeroAddress, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
})
}
}
如果没有互联网,将显示可访问性ViewController屏幕