4

I am trying to parse some JSON structures into an in-memory map using swift 2.0 - and I keep running across this problem. The first time it happened, I thought it was just some weird thing I did, but it just happened again for the same reason so there is clearly something I don't get.

I have created two unit tests that demonstrate. The first (testFails) assigns a value to a dictionary, then builds up the sub value using a handle to the nested structure. The second (testWorks) always dereferences the full path from the top level dictionary and builds it up. The second version always works, but the first never does. The only things I can think of are either (1), the dictionary is copying the reference to the sub map (subMap), or (2) something funky with optionals is taking place.

Neither makes sense to me, and the debugger in XCode sucks so bad it doesn't ever show values for the structures. The debugger says 0 keys/pairs but when I print the count I get a value, so that is useless. E.G., put a breakpoint on the XCTAssertEqual line in testWorks, and the debugger will sometimes say the variable 'map' contains 0 key/value pairs, sometimes just show nothing - even though there clearly is one key/value pair in there.

On a side (newbie) note, I find myself resorting to print statements in this very 'modern' environment because the XCode debugger fails a lot - it shows me no data or something completely foreign. E.G., in Java I could check the reference address of the different variables and clear up when proxies/copies were being made, but I can't do that in XCode.

func testFails()
{
    var map = [String: [String: String]]();
    var subMap = [String: String]();
    map["map"] = subMap;
    subMap["1"] = "2";
    XCTAssertEqual(1,  map.count);
    XCTAssertEqual(1,  map["map"]!.count);  // << Fails
    XCTAssertEqual("2", map["map"]!["1"]);  // << Fails
}

func testWorks()
{
    var map = [String: [String: String]]();
    map["map"] = [String: String]();
    map["map"]!["1"] = "2";
    XCTAssertEqual(1,  map["map"]!.count);
    XCTAssertEqual("2", map["map"]!["1"]);
}
absmiths
  • 1,144
  • 1
  • 12
  • 21

1 Answers1

0

Swift Collectiontypes (Arrays, Dictionaries etc.) are not objects, they are structs.

So when map["map"] = subMap; map will take all info inside subMap and put it under key "map", note that the reference to subMap is not put under key "map".

So when you later add a value under key "1" you are not adding it to the value under map["map"], you are adding it to subMap only.

So then when you try access the "map" value it will contain what subMap contained when being set to "map" in your case empty.

Hopefully I didnt confuse you, terminology isnt my brightest side.

Arbitur
  • 38,684
  • 22
  • 91
  • 128
  • Okay - I might understand your point. So this declaration in NSDictionary: `public subscript (key: NSCopying) -> AnyObject? { get }` means that the subscript operator does a deep copy rather than an assignment? – absmiths Nov 03 '15 at 16:22
  • You're using Dictionary not NSDictionary, they are not the same thing. NSDictionary isan object and will work the way you would expect in testFails() but they dont support generics and will always return value as NSObject – Arbitur Nov 03 '15 at 16:29
  • I don't want to get too far afield from this question, but does this imply I don't need to initialize subtrees explicitly? E.G., does `map["map"]["key"] = "value"` automatically create the subtree for "map" if it does not exist? If so, an explicit assignment would create an unnecessary intermediate object. I tried removing the second line from testWorks and it still works, which would imply that my assumption is true. – absmiths Nov 03 '15 at 17:33
  • One clarification - your answer presupposes a fundamental difference between structs and objects that I don't know about. I thought structs were simply objects without methods? Are you saying that structs and objects treat references in different ways? – absmiths Nov 03 '15 at 17:36
  • I found the answer to this here: http://stackoverflow.com/questions/24232799/why-choose-struct-over-class. They are indeed very different - I had no idea! Thanks a ton. – absmiths Nov 03 '15 at 17:38
  • Structs doesnt exist in Java so yes it can be confusing for you ;) No unfortunatelly you cant do like that (`map["map"]["key"] = "value"`) you could do like this though: `map["map"] = ["key":"value"]` – Arbitur Nov 03 '15 at 17:46
  • My last encounter with struct was in C/C++ 20 years ago, and I don't think that is very similar either. I have read / watched a lot since this answer - things are looking much better now . . . – absmiths Nov 03 '15 at 19:03