Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift get value from UnsafeMutablePointer<Void> using UnsafePointer<String>

I am trying to pass contextInfo of typeUnsafeMutablePointer<Void> to UISaveVideoAtPathToSavedPhotosAlbum and use it in the callback function. For some reason I am unable to access contextInfo as a string using UnsafePointer<String>(x).memory when I am in the callback function.

I am pretty sure it is something simple I am missing but have spent way to many hours trying to figure this out.

Below is some code that I have tried.

The following code works.

var testStr:String = "hello"
takesAMutableVoidPointer(&testStr)

func takesAMutableVoidPointer(x: UnsafeMutablePointer<Void>){
    var pStr:String = UnsafePointer<String>(x).memory
    println("x = \(x)")
    println("pStr = \(pStr)")
}

However the following code does not work.

var testStr:String = "hello"

if UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(filePath){ //the filePath is compatible
    println("Compatible")
    //UISaveVideoAtPathToSavedPhotosAlbum(filePath, self, nil, nil)
    UISaveVideoAtPathToSavedPhotosAlbum(filePath, self, "video:didFinishSavingWithError:contextInfo:", &testStr)
}
else{
    println("Not Compatible")
}

func video(video: NSString, didFinishSavingWithError error:NSError, contextInfo:UnsafeMutablePointer<Void>){
    var pStr:String = UnsafePointer<String>(contextInfo).memory
    println("contextInfo = \(contextInfo)")
    println("pStr = \(pStr)")
}

Once I get to the following line:

var pStr:String = UnsafePointer<String>(contextInfo).memory

I keep getting the following error:

Thread 1: EXC_BAD_ACCESS(code=1, address=0x0)

Any help with this would be greatly appreciated.

Thanks.

Update

Rintaro commented that testStr needs to be top level but the following code works.

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        var testStr:String = "hello"
        takesAMutableVoidPointer(&testStr)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func takesAMutableVoidPointer(x: UnsafeMutablePointer<Void>){
        var answer = UnsafePointer<String>(x).memory
        println("x = \(x)")
        println("answer = \(answer)")
    }
}

I am trying not to use global variables unless I have to. I may have to but since I am able to execute the above code, it seems as though I do not need to use a global variable.

like image 945
user2517182 Avatar asked Nov 06 '14 10:11

user2517182


1 Answers

As discussed in OP comments, testStr has already been freed.

Is there any way to force the retaining of a variable that has been created in a function? Then release it later?

It's not impossible, but I don't know this is the best way to do that.

Anyway, try this with Playground or OS X "Command Line Tool" template:

import Foundation

func foo() {
    var str:NSString = "Hello World"
    let ptr = UnsafePointer<Void>(Unmanaged<NSString>.passRetained(str).toOpaque())
    bar(ptr)
}

func bar(v:UnsafePointer<Void>) {
    let at = dispatch_time(
        DISPATCH_TIME_NOW,
        Int64(2.0 * Double(NSEC_PER_SEC))
    )
    dispatch_after(at, dispatch_get_main_queue()) {
        baz(v)
    }
}

func baz(v:UnsafePointer<Void>) {
    println("notified")
    let str = Unmanaged<NSString>.fromOpaque(COpaquePointer(v)).takeRetainedValue()
    println("info: \(str)")
}


foo()

println("started")

dispatch_main()
  • Unmanaged<NSString>.passRetained(str) increments the retain count.
  • Unmanaged<NSString>.fromOpaque(...).takeRetainedValue() decrements it, and extract the object.

I think, using pure Swift String is impossible. because String is struct and is allocated in stack memory. Maybe the buffer of it is allocated in heap, but we cannot access it directly.

like image 154
rintaro Avatar answered Sep 23 '22 15:09

rintaro