I've noticed something strange in the behavior of code I'm currently writing and thought I would ask here to see if I'm doing something silly that would cause this to happen.
Basically, when I assign a variable to the return value of my class method, instead of the variable holding a reference to the return value, it's holding a reference to the class. See the code below:
NSArray * newAddresses = [MyHost addressesForHostname: @"google.com"];
Which has a method signature of
+ (NSArray *) addressesForHostname: (NSString *)hostname
And returns
return (__bridge_transfer NSArray *) ipAddresses; // ipAddresses is a CFMutableArrayRef
As you can see, I'm using toll-free bridging to use CoreFoundation objects as I'm collecting a list of IP addresses for some network interfaces.
After newAddresses has been assigned to, I look at the class of the newAddresses array in LLDB and get:
(lldb) po [newAddresses class]
MyHost
Am I mistaken in my assumptions about how I'm using __bridge_transfer? All of the objects use to make up ipAddresses are CFStringRefs.
EDIT: I was asked for the whole method, so here it is!
+ (NSArray *) addressesForHostname: (NSString *)hostname {
    CFMutableArrayRef ipAddresses;
    DLog(@"Getting addresses for host name %@", hostname);
    CFHostRef hostRef = CFHostCreateWithName(kCFAllocatorDefault, (__bridge CFStringRef)(hostname));
    CFStreamError error;
    BOOL didResolve = CFHostStartInfoResolution(hostRef, kCFHostNames, &error); // synchronously get the host.
    if (didResolve) {
        CFArrayRef responseObjects = CFHostGetAddressing(hostRef, NULL);
        long numberOfResponses = CFArrayGetCount(responseObjects);
        ipAddresses = CFArrayCreateMutable(kCFAllocatorDefault, numberOfResponses, &kCFTypeArrayCallBacks);
        for ( int i = 0 ; i < numberOfResponses; ++i ) {
            char * ipAddress;
            CFDataRef responseObject = CFArrayGetValueAtIndex(responseObjects, i);
            struct sockaddr * currentAddress = (struct sockaddr *) CFDataGetBytePtr(responseObject); // Unwrap the CFData wrapper aound the sockaddr struct
            switch (currentAddress->sa_family) {
                case AF_INET: { // Internetworking AKA IPV4
                    DLog(@"Extracting IPV4 address");
                    struct sockaddr_in * socketAddress = (struct sockaddr_in *) currentAddress;
                    ipAddress = malloc(sizeof(INET_ADDRSTRLEN));
                    inet_ntop(AF_INET,
                              &(socketAddress->sin_addr),
                              ipAddress,
                              INET_ADDRSTRLEN);
                    CFStringRef ipAddressString = CFStringCreateWithCString(kCFAllocatorDefault, ipAddress, kCFStringEncodingASCII);
                    CFArrayInsertValueAtIndex(ipAddresses, i, ipAddressString);
                    free(ipAddress);
                    break;
                }
                case AF_INET6: { // IPV6
                    DLog(@"Extracting IPV6 address");
                    struct sockaddr_in6 * socketAddress = (struct sockaddr_in6 *) currentAddress;
                    ipAddress = malloc(sizeof(INET6_ADDRSTRLEN));
                    inet_ntop(AF_INET6,
                              &(socketAddress->sin6_addr),
                              ipAddress,
                              INET6_ADDRSTRLEN);
                    CFStringRef ipAddressString = CFStringCreateWithCString(kCFAllocatorDefault, ipAddress, kCFStringEncodingASCII);
                    CFArrayInsertValueAtIndex(ipAddresses, i, ipAddressString);
                    free(ipAddress);
                    break;
                }
                default:
                    DLog(@"Unsupported addressing protocol encountered. Gracefully ignoring and continuing.");
                    break;
            }
        }
        CFRelease(responseObjects);
    }
    CFRelease(hostRef);
    return (__bridge_transfer NSArray *) ipAddresses;
}
				
                        
So I found the solution, and it lies in me forgetting to initialize
ipAddresses = nilbefore anything happens. The way this code is written, it won't assign a value toipAddressesif it's unable to resolve thehostRefgiven toCFHostStartInfoResolution. With no value inipAddresses, it returns an uninitialized pointer that gets casted and has its ownership transferred.I can't find formal documentation that states this, but I believe this would be undefined behavior.
I should state that if anyone is using this code as reference, I'm experiencing inconsistent crashes on the line where I release
hostRef. This is unrelated to the issue that I created for this thread, but is worthwhile to point out.