Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it necessary to deallocate an AutoreleasingUnsafeMutablePointer? If so, how?

With ARC, I can just set all of an object's strong references to nil to deallocate it.

With an UnsafePointer or UnsafeMutablePointer, I need to manage its memory explicitly:

let buffer = sizeof(Int8) * 4
var ptr = UnsafeMutablePointer<Void>.alloc(buffer)
defer {
    ptr.destroy()
    ptr.dealloc(someVal)
    ptr = nil
}

But the documentation is ambiguous for AutoreleasingUnsafeMutablePointer objects. I cannot explicitly call destroy or dealloc on a AutoreleasingUnsafeMutablePointer.

var ptr: AutoreleasingUnsafeMutablePointer<Void> = nil
defer {
    ptr = nil
}

// assign something to ptr

The name implies that it is autoreleased after it falls out of scope, but do I need to set a AutoreleasingUnsafeMutablePointer to nil in order for it to be autoreleased?

Here's an example where I use an AutoreleasingUnsafeMutablePointer to get a list of all of the classes currently loaded by the runtime. Note that when invoking the power of the Objective-C runtime some functions require a AutoreleasingUnsafeMutablePointer rather than just a UnsafeMutablePointer:

var numClasses: Int32 = 0
var allClasses: AutoreleasingUnsafeMutablePointer<AnyClass?> = nil
defer {
    allClasses = nil // is this required?
}

numClasses = objc_getClassList(nil, 0)

if numClasses > 0 {
    var ptr = UnsafeMutablePointer<AnyClass>.alloc(Int(numClasses))
    defer {
        ptr.destroy()
        ptr.dealloc(Int(numClasses))
        ptr = nil
    }
    allClasses = AutoreleasingUnsafeMutablePointer<AnyClass?>.init(ptr)
    numClasses = objc_getClassList(allClasses, numClasses)

    for i in 0 ..< numClasses {
        if let currentClass: AnyClass = allClasses[Int(i)] {
            print("\(currentClass)")
        }
    }
}
like image 860
JAL Avatar asked Mar 23 '16 18:03

JAL


1 Answers

You don't need to set it to nil. It is supposed to be constructed from an autoreleased pointer (assuming it was constructed correctly, it will release itself). At the same time, don't hold onto it past the current stack frame. The AutoreleasingUnsafeMutablePointer does not keep the object alive. When the enclosing autorelease pool is popped, the wrapped object will be released and probably deallocated. Like the name says: it is unsafe.

Avoid problems by never creating AutoreleasingUnsafeMutablePointer yourself in Swift (edit: except when it's really an UnsafeMutablePointer and the C header import has made a mistake, see below). If you're using it correctly, it should be transparent glue between Swift's inout and an Objective-C return-by-pointer parameter.

You generally create a var matching the contained type and pass it in by inout.

e.g. if you want to call the function:

func someFunction(obj: AutoreleasingUnsafeMutablePointer<AnyObject?>)

then you invoke it like this:

var myObject: AnyObject? = nil
someFunction(&AnyObject)

and everything will work out.

I'm not aware of any other situation where you should hold an AutoreleasingUnsafeMutablePointer. I don't think you should be manually constructing one at all on the Swift side except as nil. It is precariously difficult in Swift to construct an AutoreleasingUnsafeMutablePointer with non-nil contents since the only way to autorelease is using Unmanaged.

Responding to your update...

The objc_getClassList function signature is a glitch in Swift's automatic C importing. It incorrectly assumes that a Class * parameter should be imported as AutoreleasingUnsafeMutablePointer<AnyObject?>. You really just need an UnsafeMutablePointer which you can get from an array:

var allClasses = Array<AnyClass?>(count: Int(objc_getClassList(nil, 0)), repeatedValue: nil)
allClasses.withUnsafeMutableBufferPointer { (inout bp: UnsafeMutableBufferPointer<AnyClass?>) in
    objc_getClassList(AutoreleasingUnsafeMutablePointer(bp.baseAddress), Int32(allClasses.count))
}
like image 85
Matt Gallagher Avatar answered Sep 22 '22 11:09

Matt Gallagher