2

I've just thrown myself into iOS development and I'm current getting a runtime error in the function CFRelease just at the end of the queryServer function (I put a comment on the line that get's highlighted) and I don't have an error if I comment out the function call to extractIPFromQuery.

The code below is taking the name of a server and returning a list of ip addresses to that server.

func extractIPFromQuery(query: NSArray) -> [String] {
    var addresses = [String]()

    for x in 0...query.count - 1{
        let adr = "\(query[x])"
        let adrStart = adr.startIndex.advancedBy(10)
        let adrEnd = adr.startIndex.advancedBy(18)
        let address = adr.substringWithRange(Range<String.Index>(start: adrStart, end: adrEnd))
        var final = ""

        // Convert the hex version of the address into
        // a human readable version

        for seg in 0...3{
            let start = address.startIndex.advancedBy(seg * 2)
            let end = address.startIndex.advancedBy((seg * 2) + 2)
            let hexRange = Range<String.Index>(start: start, end: end)
            let hexPair = address.substringWithRange(hexRange)

            final += "\(UInt8(strtoul(hexPair, nil, 16)))"
            if(seg != 3){
                final += "."
            }
        }

        addresses.append(final)
    }
    return addresses;
}

func queryServer(hostName: String) -> [String]{
    var ips = [String]()
    if  hostName != "\0" {
        let hostRef = CFHostCreateWithName(kCFAllocatorDefault, hostName).takeRetainedValue()
        while(CFHostStartInfoResolution(hostRef, CFHostInfoType.Addresses, nil) == false){}
        ips += extractIPFromQuery(CFHostGetAddressing(hostRef, nil)!.takeRetainedValue() as NSArray)
    } // Code breaks here
    return ips
}
aaisataev
  • 1,643
  • 3
  • 22
  • 38
Joshua Waring
  • 619
  • 7
  • 23
  • 1
    Do you pass a valid server name when calling `queryServer`? – Cristik Dec 28 '15 at 06:57
  • The code does work and ips is populated with a bunch of addresses – Joshua Waring Dec 28 '15 at 06:58
  • As this crashes when leaving the `if` scope, then it means it has to do with either one of the variables used within the scope: `hostRef` or the result of `CFHostGetAddressing`. – Cristik Dec 28 '15 at 07:03
  • Have also a look at http://stackoverflow.com/questions/25890533/how-can-i-get-a-real-ip-address-from-dns-query-in-swift for an easier method to convert the IP addresses to strings, using `getnameinfo()`. – Martin R Dec 28 '15 at 07:09

2 Answers2

3

CFHostGetAddressing does not have "Create" or "Copy" in its name, which means that it does not return a (+1) retained object. Therefore you have to use takeUnretainedValue() to get the value of the unmanaged reference.

For more information about these naming conventions, see "The Create Rule" and "The Get Rule" in Ownership Policy in the "Memory Management Programming Guide for Core Foundation".

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
2

You are taking ownership of the array returned by CFHostGetAddressing, which means ARC will insert a release call to balance the retain call that thinks needs to be balanced, when in fact it doesn't need to do this. You should be using takeUnretainedValue() instead of takeRetainedValue() as the name semantics of CFHostGetAddressing don't imply you are required to take ownership of the result.

At a basic level, the difference between takeRetainedValue() and takeUnretainedValue() is that the former will instruct ARC to insert a release call when the variable get's out of scope, while the latter one will not. At a semantical level, the former one tells that you want, or need to take ownership of the variable, usually meaning that there's an unbalanced retain call on that object, which ARC will balance.

Cristik
  • 30,989
  • 25
  • 91
  • 127