`as!` NSMutableDictionary, casts to <uninitialized>, `as?` works correctly

Hey everyone,

I'm playing with using Apple's legacy types with Swift and discovered an interesting conundrum:

  • At some point in my code I'm using API which provides me with CFArray. I use it as follows:
let attachments: NSArray! =
    CMSampleBufferGetSampleAttachmentsArray(
        sampleBuffer!,
        createIfNecessary: true
    )
  • Now, I happen to know that first element of this CFArray is a CFMutableDictionary. I modify it as follows:
let dictionary = unsafeBitCast(CFArrayGetValueAtIndex(attachments, 0),
    to: CFMutableDictionary.self)
let key = Unmanaged.passUnretained(
    kCMSampleAttachmentKey_DisplayImmediately).toOpaque()
let value = Unmanaged.passUnretained(kCFBooleanTrue).toOpaque()

CFDictionarySetValue(dictionary, key, value)
  • For debugging purposes, I created the following variables after the modification:
var isItNsMutableDictionary = attachments[0] is NSMutableDictionary
var maybeNsMutableDictionary = attachments[0] as? NSMutableDictionary
var nsMutableDictionary = maybeNsMutableDictionary!
var nsMutableDictionaryForcedDowncast =
    attachments[0] as! NSMutableDictionary
  • I attached debugger to look at variables. Here is what it said:
(lldb) po isItNsMutableDictionary
true

(lldb) po maybeNsMutableDictionary
▿ Optional<NSMutableDictionary>
  ▿ some : 1 element
    ▿ 0 : 2 elements
      - key : DisplayImmediately
      - value : 1

(lldb) po nsMutableDictionary
▿ 1 element
  ▿ 0 : 2 elements
    - key : DisplayImmediately
    - value : 1

(lldb) po nsMutableDictionaryForcedDowncast
 <uninitialized>

What's going on there?

  • Why is nsMutableDictionaryForcedDowncast different then nsMutableDictionary?
    • I expected (a as? NSMutableDictionary)! to be the same as a as! NSMutableDictionary
  • What could be the reason as? works correctly when as! don't? Which one should I use?

The <uninitialized> makes it seem like you are stopped at a breakpoint on the fourth line (or you have optimizations turned on). If that's not the case, it could be a bug in LLDB and not the compiler/runtime.

What happens if you print the values instead of inspecting them in the debugger?

Could you file a bug report on bugs.swift.org with a self-contained example and reproduction steps?

  • I expected (a as? NSMutableDictionary)! to be the same as a as! NSMutableDictionary

This is correct, they should be the same according to the dynamic casting documentation:

The following invariants relate the three casting operators:

  • [...]
  • [...]
  • Forced cast: x as! T is equivalent to (x as? T)!
1 Like

Printing the value actually crashes the program with bad access exception.

I'll try to report this when I have time, good to know it can indeed be a Swift bug.