> On Sep 21, 2016, at 4:01 PM, John Holdsworth via swift-evolution > <swift-evolution@swift.org> wrote: > > 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.
You are thinking about this the inverse way. That the first case works is an artifact of the optimizer failing to do a good enough job. Future improved ARC optimization can cause both to fail. > > John > > >> On 21 Sep 2016, at 23:34, Joe Groff <jgr...@apple.com >> <mailto:jgr...@apple.com>> wrote: >> >> >>> On Sep 21, 2016, at 3:14 PM, Xiaodi Wu via swift-evolution >>> <swift-evolution@swift.org <mailto: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 <mailto: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 <mailto: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
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution