AutoreleasingUnsafeMutablePointer<String?> crash - bug in compiler?

1.2k Views Asked by At

I'm slowly migrating my codebase to Swift and came across a weird crash, where I'm populating an AutoreleasingUnsafeMutablePointer<String?> with a non-nil string. Here is some scaled down code:

extension String {
    func getRegexVariableNamed(name: String, forRegexString regexString: String) -> String? {
        /** ... **/
        return "TO BE IMPLEMENTED".lowercaseString // Using lowercase to prevent the compiler from inlining
    }
}

class XYZ {
    func extractInformation(info1: AutoreleasingUnsafeMutablePointer<String?>, info2: AutoreleasingUnsafeMutablePointer<String?>, info3: AutoreleasingUnsafeMutablePointer<String?>, info4: AutoreleasingUnsafeMutablePointer<String?>, fromSource source: String) -> Bool {
        guard let vp = source.getRegexVariableNamed("ID", forRegexString: "vp=(?P<ID>\\d+)") else {
            return false
        }

        info4.memory = vp
        info1.memory = "ABC"
        info2.memory = "DEF"
        info3.memory = "GHI" + vp
        return true
    }
}

// Code in playground
let obj = XYZ()

let info1 = AutoreleasingUnsafeMutablePointer<String?>()
let info2 = AutoreleasingUnsafeMutablePointer<String?>()
let info3 = AutoreleasingUnsafeMutablePointer<String?>()
let info4 = AutoreleasingUnsafeMutablePointer<String?>()
if !obj.extractInformation(info1, info2: info2, info3: info3, info4: info4, fromSource: "") {
    print("NO")
}else{
    print("YES")
}

The app (and the same applies for the playground) crashes with error: Playground execution aborted: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).

This exception happens on setting a string to the AutoreleasingUnsafeMutablePointer in the method extractInformation(...).

I wanted to check if I'm not doing something wrong here before I report this as a bug to Apple.

BTW I'm using the latest Xcode 7 beta (6) on OS X 10.10.5.

P.S.: I know the more Swift-ly approach would be to make a method that returns a struct instead of using these autoreleasing pointers, but as mentioned, I am slowly moving from ObjC, so I need it (for now) to be backward-compatible.

1

There are 1 best solutions below

1
On

With Xcode 7.0.1, I can make your code work, with some changes.

First of all, in the Using Swift with Cocoa and Objective-C (Swift 2) guide ("Autoreleasing Pointers" chapter), it says that a function declared as taking an AutoreleasingUnsafeMutablePointer<Type> can accept an inout expression of the same Type.

So, the code could be changed as following:

let info1: String?
let info2: String?
let info3: String?
let info4: String?
if !obj.extractInformation(&info1, info2: &info2, info3: &info3, info4: &info4, fromSource: "") {
    print("NO")
}else{
    print("YES")
}

But, it's still not working.

However, in the same page, it says that AutoreleasingUnsafeMutablePointer<Type> is applied for class types mapping. I don't know if this is the reason it's not working, difficult to guess what's happening behind, but if you replace String with NSString, you'll get a result.

extension String {
    func getRegexVariableNamed(name: String, forRegexString regexString: String) -> String? {
        /** ... **/
        return "TO BE IMPLEMENTED".lowercaseString // Using lowercase to prevent the compiler from inlining
    }
}

class XYZ {
    func extractInformation(info1: AutoreleasingUnsafeMutablePointer<NSString?>,
                            info2: AutoreleasingUnsafeMutablePointer<NSString?>,
                            info3: AutoreleasingUnsafeMutablePointer<NSString?>,
                            info4: AutoreleasingUnsafeMutablePointer<NSString?>,
                            fromSource source: String) -> Bool {
        guard let vp = source.getRegexVariableNamed("ID", forRegexString: "vp=(?P<ID>\\d+)") else {
            return false
        }

        info4.memory = vp
        info1.memory = "ABC"
        info2.memory = "DEF"
        info3.memory = "GHI" + vp
        return true
    }
}

// Code in playground
let obj = XYZ()

var info1: NSString?
var info2: NSString?
var info3: NSString?
var info4: NSString?
if !obj.extractInformation(&info1, info2: &info2, info3: &info3, info4: &info4, fromSource: "") {
    print("NO")
}else{
    print("YES")
    info1                // "ABC"
    info2                // "DEF"
    info3                // "GHIto be implemented"
    info4                // "to be implemented"
}