Re: Swift crash in dispatch_apply
Re: Swift crash in dispatch_apply
- Subject: Re: Swift crash in dispatch_apply
- From: "Gerriet M. Denkmann" <email@hidden>
- Date: Wed, 09 Jul 2014 00:03:16 +0700
On 8 Jul 2014, at 23:12, Clark S. Cox III <email@hidden> wrote:
>>
>> On Jul 7, 2014, at 22:10, Gerriet M. Denkmann <email@hidden> wrote:
>>
>>
>> On 8 Jul 2014, at 10:57, Clark S. Cox III <email@hidden> wrote:
>>
>>>>
>>>> On Jul 7, 2014, at 20:36, Gerriet M. Denkmann <email@hidden> wrote:
>>>>
>>>>
>>>> On 8 Jul 2014, at 10:03, Roland King <email@hidden> wrote:
>>>>
>>>>>>> Why are you making self unowned?
>>>>>>
>>>>>> I have no idea.
>>>>>> The Swift book (in the chapter "“Resolving Strong Reference Cycles for Closures”) seems to say that this is the correct way to "resolve a strong reference cycle between a closure and a class instance”.
>>>>>
>>>>> Not quite - it says unowned if the block and self have the same lifetime, else weak.
>>>>
>>>> The book says: “If the captured reference will never become nil, it should always be captured as an unowned reference, rather than a weak reference.”
>>>>
>>>> And I can think of no way how "self" = instance of my Crash class could ever become nil while its function makeCrash is running.
>>>
>>> Believe me, it can. Nothing after that point has a strong reference to self (because the only remaining reference to self is inside of the closure, but you made that particular reference “unowned”), the compiler is free to release it after the closure is created, but before dispatch_apply is called.
>>
>> But the caller of makeCrash has a strong reference to self via
>> let dummy = Crash()
>> does this not keep the instance of Crash alive?
>
> No, because the compiler can see that dummy is never used again after the call to makeCrash.
So I changed the call to:
println("will try makeCrash")
let dummy = Crash()
dummy.makeCrash()
println("makeCrash did NOT crash status: " + dummy.status )
But it still crashes.
>
>
>>>
>>>> Anyway: even without "[unowned self]" I always see a println() inside of deinit{} (if it is not crashing before).
>>>> Which might indicate that there are no evil retain-cycles taking place.
>>>
>>> There are indeed no retain cycles.
>>>
>>> The only thing referencing self is the closure
>>> The only thing referencing the closure is the code inside of dispatch_apply
>>> Once dispatch_apply is done, the closure is released and deallocated
>>> Once the closure is deallocated, the reference it holds on self is released
>>>
>>>> Could anybody show me some example which really needs [unowned self] or [weak self] to break a retain-cycle?
>>>
>>> If, for instance, you stored the closure in a property on self, that would create a retain cycle that would need breaking.
>>
>> To sum up:
>> There are no retain cycles in my example, so one does NOT need [unowned self].
>> And using [unowned self] is dangerous, because "self" might go away while dispatch_apply is still running.
>> Correct?
>
> Correct. “unowned” is essentially telling the compiler “don’t count this reference, assume that I know what I’m doing and I have something else that will keep the referenced object alive”. However, in this case, nothing else is keeping the object alive, and the compiler trustingly attempts to access the now destroyed object and that leads to the crash.
>
>> What I not understand: dispatch_apply() returns after all threads have finished (Correct?).
>
> Correct.
>
>> To execute code after dispatch_apply() has finished, self must still exist.
>
> If, in makeCrash, you used self after the dispatch_apply, then the compiler would see that it still has a reference and self would still exist.
So I changed this to
dispatch_apply( nbrThreads, queue, ...
println("makeCrash did do dispatch_apply without crashing")
But it still crashes.
To sum up:
class Crash : NSObject // must be subclass of NSObject to get a crash
{
var globalSize = 0
let status = "Still alive"
let queue = dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 )
func makeCrash()
{
globalSize = 97 // anything
let nbrThreads : UInt = 1 // any positive number will do; output might be garbled for > 1
let localSize = globalSize
println("makeCrash will do dispatch_apply( \(nbrThreads)) with [unowned self]")
dispatch_apply( nbrThreads, queue,
{ [unowned self] (idx: size_t) in // no crash without [unowned self] or with [weak self]
println( "thread[\(idx)] local \(localSize)") // ok
// next line will crash --- Mac OS X 10.9.4 Xcode Version 6.0 (6A254o) beta 3
println( "thread[\(idx)] global \(self.globalSize)") // will crash
}
)
println("makeCrash did do dispatch_apply without crashing")
}
}
println("will try makeCrash")
let dummy = Crash()
dummy.makeCrash()
println("makeCrash did NOT crash status: " + dummy.status )
Result:
will try makeCrash
makeCrash will do dispatch_apply( 1) with [unowned self]
(lldb) bt
* thread #1: tid = 0x293263, 0x00000001001bae63 libswift_stdlib_core.dylib`_swift_abortRetainUnowned + 19, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
* frame #0: 0x00000001001bae63 libswift_stdlib_core.dylib`_swift_abortRetainUnowned + 19
frame #1: 0x00000001001c5c6f libswift_stdlib_core.dylib`swift_unknownRetainUnowned + 351
frame #2: 0x0000000100002104 Prime CL`Prime_CL.Crash.makeCrash (self=0x000012344c40efc0)() -> () + 772 at HaSwiftAtom.swift:82
frame #3: 0x00000001000027a2 Prime CL`@objc Prime_CL.Crash.makeCrash (Prime_CL.Crash)() -> () + 34 at HaSwiftAtom.swift:0
frame #4: 0x0000000100003404 Prime CL`Prime_CL.HaSwiftAtom.makePrimesTo (lastPrime=Swift.UInt at 0x00007fff5fbfeab0, self=0x000012344c3fcfe0)
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Xcode-users mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden