My contrived example was a bit flimsy. I’d better unpack the full story. The 
real
code I had problems with was based around the following Java instance wrapper:

open class JNIObject: JNIObjectProtocol {

    var _javaObject: jobject?

    open var javaObject: jobject? {
        get {
            return _javaObject
        }
        set(newValue) {
            if newValue != _javaObject {
                let oldValue = _javaObject
                if newValue != nil {
                    _javaObject = JNI.api.NewGlobalRef( JNI.env, newValue )
                }
                else {
                    _javaObject = nil
                }
                if oldValue != nil {
                    JNI.api.DeleteGlobalRef( JNI.env, oldValue )
                }
            }
        }
    }

    deinit {
        javaObject = nil
    }

As a result the following transfer of a Java instance always worked:

    init(imageProducer:ImageProducer) {
        let supr = CanvasBase()
        super.init( javaObject: supr.javaObject )
        image = createImage(imageProducer)
    }

But the following only worked for debug compiles:

    init(imageProducer:ImageProducer) {
        super.init( javaObject: CanvasBase().javaObject )
        image = createImage(imageProducer)
    }

Felt like a bit of a bear trap is all. Statement scope would avoid problems 
like this.

John


> On 21 Sep 2016, at 23:34, Joe Groff <jgr...@apple.com> wrote:
> 
> 
>> On Sep 21, 2016, at 3:14 PM, Xiaodi Wu via swift-evolution 
>> <swift-evolution@swift.org> wrote:
>> 
>> I haven't used it myself, but is this the use case addressed by 
>> `withExtendedLifetime(_:_:)`?
> 
> Yeah, if you want to vend resources managed by an object to consumers outside 
> of that object like this, you need to use withExtendedLifetime to keep the 
> object alive for as long as you're using the resources. A cleaner way to 
> model this might be to put the class or protocol in control of handling the 
> I/O to the file handle, instead of vending the file handle itself, so that 
> the ownership semantics fall out more naturally:
> 
> protocol Storage {
>  func write(bytes: UnsafeRawPointer, count: Int)
> }
> 
> func save(string: String, to: Storage?) {
>    if let data = string.data(using: String.Encoding.utf8) {
>        data.withUnsafeBytes {
>            _ = to?.write(bytes: $0, count: data.count)
>        }
>    }
> }
> 
> -Joe
> 
>> On Wed, Sep 21, 2016 at 16:54 John Holdsworth via swift-evolution 
>> <swift-evolution@swift.org> wrote:
>> Hi,
>> 
>> For complex statements in C++ any temporary instances created in the course
>> of an expression have their lifetime extended to the completion of the 
>> current
>> statement after which they are all deallocated en masse. This makes certain
>> types of language usage possible and easier to reason with.
>> 
>> I’m bringing this up as I had a problem with some code crashing only when
>> compiled with release configuration and the problem could have been avoided
>> if Swift deferred deallocation to the end of a statement. While Swift’s ARC 
>> policy
>> is consistent in itself this seems to be a particular problem interfacing 
>> between
>> language/reference counting systems. My problem code was a Java-Swift Bridge.
>> 
>> A contrived example:
>> 
>> import Foundation
>> 
>> protocol Storage {
>>    var fp: UnsafeMutablePointer<FILE> { get }
>> }
>> 
>> class FileStorage: Storage {
>> 
>>    let fp: UnsafeMutablePointer<FILE>
>> 
>>    init?(path: String, mode: String = "w") {
>>        print("Opening")
>>        let fp = fopen(path, mode)
>>        if fp == nil {
>>            return nil
>>        }
>>        self.fp = fp!
>>    }
>> 
>>    deinit {
>>        print("Closing")
>>        fclose(fp)
>>    }
>> }
>> 
>> func save(string: String, to: Storage?) {
>>    if let data = string.data(using: String.Encoding.utf8) {
>>        print("Saving1")
>>        if let fp = to?.fp {
>>            print("Saving2")
>>            data.withUnsafeBytes {
>>                _ = fwrite($0, 1, data.count, fp)
>>            }
>>            print("Saving3")
>>        }
>>    }
>> }
>> 
>> save(string: "Hello World\n", to: FileStorage(path: "/tmp/a.txt"))
>> 
>> 
>> In debug configuration is prints:
>> Opening
>> Saving1
>> Saving2
>> Saving3
>> Closing
>> 
>> Whereas in release configuration it prints:
>> Opening
>> Saving1
>> Closing <!!!
>> Saving2
>> Saving3
>> 
>> The optimiser is vigorously deallocating objects when they are no longer 
>> referenced regardless
>> of whether an variable referencing it is still in scope (In fairness this 
>> particular problem only occurs
>> for Optional augments of Protocols) but this behaviour seems to be implicit 
>> in the current language
>> spec. The alternative is to retain arguments themselves as I believe they 
>> are in Objective-C ARC.
>> 
>> This would have been avoided if the temporary FileStorage instance has been 
>> considered to have
>> a lifetime up to the end of the statement calling function save() and hence 
>> the duration of the call.
>> This needed increase ARC overhead in any way. Just alter the timing of it to 
>> be more conservative.
>> 
>> John
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to