2016-10-04 38 views
3

POSIX getaddrinfo分配內存,必須稍後使用freeaddrinfo釋放內存。 見http://manpages.ubuntu.com/manpages/xenial/en/man3/getaddrinfo.3.htmlSwift getaddrinfo

爲了簡化API,我創建了這個功能:

import Foundation 

enum SystemError: Swift.Error { 
    case getaddrinfo(Int32, Int32?) 
} 

public func getaddrinfo(node: String?, service: String?, hints: addrinfo?) throws -> [addrinfo] { 
    var err: Int32 
    var res: UnsafeMutablePointer<addrinfo>? 
    if var hints = hints { 
     err = getaddrinfo(node, service, &hints, &res) 
    } else { 
     err = getaddrinfo(node, service, nil, &res) 
    } 
    if err == EAI_SYSTEM { 
     throw SystemError.getaddrinfo(err, errno) 
    } 
    if err != 0 { 
     throw SystemError.getaddrinfo(err, nil) 
    } 
    defer { 
     freeaddrinfo(res) 
    } 
    var result = [addrinfo]() 
    var ai = res?.pointee 
    while ai != nil { 
     result.append(ai!) 
     ai = ai!.ai_next?.pointee 
    } 
    return result 
} 

我不覺得功能是正確的,雖然。

  • Swift內存模型如何知道getaddrinfo分配內存,並且Swift不應該用自己的東西覆蓋該內存?
  • Swift如何知道freeaddrinfo刪除了整個列表,並且它應該複製已分配給結果數組的ai信息?

什麼是與getaddrinfo接口的正確方法?通過getaddrinfo(例如通過malloc)分配

回答

3

存儲器將不給出任何其它 動態存儲器分配功能在相同的運行中的進程,直到 釋放由freeaddrinfo(例如通過free)。 因此,Swift運行時不會在該內存上執行任何操作(如果我們 認爲它沒有編程錯誤,例如錯誤的指針計算)。

另外struct addrinfo是值類型,所以

result.append(ai!) 

將附加的拷貝指向的結構,以陣列。

但還是有問題。struct addrinfo 一些成員是指針

public var ai_canonname: UnsafeMutablePointer<Int8>! /* canonical name for hostname */ 
public var ai_addr: UnsafeMutablePointer<sockaddr>! /* binary address */ 

這可能指向成getaddrinfo分配的內存中,因此 freeaddrinfo無效後,和您的 函數返回後提領它們會導致不確定的行爲。

因此,您必須推遲freeaddrinfo,直到不再需要 地址列表或複製信息。 這有點麻煩,因爲ai_addr可能指向一個 具有不同長度的IPv4或IPv6套接字地址結構。

下面的代碼說明地址列表如何可以被複制到 sockaddr_storage結構(其是足夠 大以容納任何IP地址)的陣列。此代碼尚未經過全面測試,因此請謹慎使用。

public func getaddrinfo(node: String, service: String, hints: addrinfo?) throws -> [sockaddr_storage] { 
    var err: Int32 
    var res: UnsafeMutablePointer<addrinfo>? 
    if var hints = hints { 
     err = getaddrinfo(node, service, &hints, &res) 
    } else { 
     err = getaddrinfo(node, service, nil, &res) 
    } 
    if err == EAI_SYSTEM { 
     throw SystemError.getaddrinfo(err, errno) 
    } 
    if err != 0 { 
     throw SystemError.getaddrinfo(err, nil) 
    } 
    defer { 
     freeaddrinfo(res) 
    } 
    guard let firstAddr = res else { 
     return [] 
    } 

    var result = [sockaddr_storage]() 
    for addr in sequence(first: firstAddr, next: { $0.pointee.ai_next }) { 
     var sockAddr = sockaddr_storage() 
     memcpy(&sockAddr, addr.pointee.ai_addr, Int(addr.pointee.ai_addrlen)) 
     result.append(sockAddr) 
    } 
    return result 
} 

備註:

+0

這與您的答案是相切的,但是:您在哪裏得到了'addrinfo'字段的Swift版本?你只是手工重寫它們,還是有一種工具可以將C++代碼的Swift界面顯示出來? (本週我正在處理Swift中的C,並且很想知道。) –

+0

@JoshCaswell:我不確定我是否理解你的問題。在導入Swift時,按住Command鍵點擊'addrinfo'就會顯示標題。 - 您還可以將#import '添加到任何C文件中,通過命令單擊「netdb.h」跳轉到該文件,然後選擇「生成的接口」。 –

+0

Xcode中的「生成的接口」是我缺少的一部分,謝謝! –