Skip to content

Commit aa3e6e1

Browse files
committed
Parity: NSCoding: NSOrderedSet
- NSOrderedSet uses a subset of the way NSSet encodes things; encode using the existing methods. - We’re using convenience initializers to mimic “/* not inherited */“ factory initializers from ObjC. Just like those initializers, they aren’t actually inherited. We need to replicate all the convenience initializers of NSOrderedSet in NSMutableOrderedSet for source compatibility, and we do so here.
1 parent a3ab9e1 commit aa3e6e1

File tree

8 files changed

+165
-55
lines changed

8 files changed

+165
-55
lines changed

Foundation/NSOrderedSet.swift

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,8 @@ open class NSOrderedSet: NSObject, NSCopying, NSMutableCopying, NSSecureCoding,
6969
}
7070

7171
public required convenience init?(coder aDecoder: NSCoder) {
72-
guard aDecoder.allowsKeyedCoding else {
73-
preconditionFailure("Unkeyed coding is unsupported.")
74-
}
75-
var idx = 0
76-
var objects : [AnyObject] = []
77-
while aDecoder.containsValue(forKey: ("NS.object.\(idx)")) {
78-
guard let object = aDecoder.decodeObject(forKey: "NS.object.\(idx)") else {
79-
return nil
80-
}
81-
objects.append(object as! NSObject)
82-
idx += 1
83-
}
84-
self.init(array: objects)
72+
// This uses the same storage setup as NSSet, but without allowing the use of the "NS.objects" key:
73+
self.init(array: NSSet._objects(from: aDecoder, allowDecodingNonindexedArrayKey: false))
8574
}
8675

8776
open var count: Int {
@@ -339,7 +328,7 @@ open class NSOrderedSet: NSObject, NSCopying, NSMutableCopying, NSSecureCoding,
339328
public func description(withLocale locale: Locale?, indent level: Int) -> String {
340329
return _orderedStorage.description(withLocale: locale, indent: level)
341330
}
342-
331+
343332
public convenience init(object: Any) {
344333
self.init(array: [object])
345334
}
@@ -370,7 +359,7 @@ open class NSOrderedSet: NSObject, NSCopying, NSMutableCopying, NSSecureCoding,
370359
public convenience init(array set: [Any], copyItems flag: Bool) {
371360
self.init(array: set, range: NSRange(location: 0, length: set.count), copyItems: flag)
372361
}
373-
362+
374363
public convenience init(array set: [Any], range: NSRange, copyItems flag: Bool) {
375364
var objects = set
376365

@@ -470,7 +459,10 @@ open class NSMutableOrderedSet: NSOrderedSet {
470459
_orderedStorage = _mutableOrderedStorage
471460
}
472461

473-
public required init?(coder aDecoder: NSCoder) { NSUnimplemented() }
462+
public required convenience init?(coder aDecoder: NSCoder) {
463+
// See NSOrderedSet.init?(coder:)
464+
self.init(array: NSSet._objects(from: aDecoder, allowDecodingNonindexedArrayKey: false))
465+
}
474466

475467
open override func copy(with zone: NSZone? = nil) -> Any {
476468
if type(of: self) === NSMutableOrderedSet.self {
@@ -672,6 +664,69 @@ open class NSMutableOrderedSet: NSOrderedSet {
672664
open func sort(using sortDescriptors: [NSSortDescriptor]) {
673665
_mutableOrderedStorage.sort(using: sortDescriptors)
674666
}
667+
668+
// MARK: Convenience initializers that are automatically inherited in ObjC, but not in Swift:
669+
670+
public convenience init() {
671+
self.init(objects: [], count: 0)
672+
}
673+
674+
public convenience init(object: Any) {
675+
self.init(array: [object])
676+
}
677+
678+
public convenience init(orderedSet set: NSOrderedSet) {
679+
self.init(orderedSet: set, copyItems: false)
680+
}
681+
682+
public convenience init(orderedSet set: NSOrderedSet, copyItems flag: Bool) {
683+
self.init(orderedSet: set, range: NSRange(location: 0, length: set.count), copyItems: flag)
684+
}
685+
686+
public convenience init(orderedSet set: NSOrderedSet, range: NSRange, copyItems flag: Bool) {
687+
// TODO: Use the array method here when available.
688+
self.init(array: Array(set), range: range, copyItems: flag)
689+
}
690+
691+
public convenience init(array: [Any]) {
692+
let buffer = UnsafeMutablePointer<AnyObject>.allocate(capacity: array.count)
693+
for (idx, element) in array.enumerated() {
694+
buffer.advanced(by: idx).initialize(to: __SwiftValue.store(element))
695+
}
696+
self.init(objects: buffer, count: array.count)
697+
buffer.deinitialize(count: array.count)
698+
buffer.deallocate()
699+
}
700+
701+
public convenience init(array set: [Any], copyItems flag: Bool) {
702+
self.init(array: set, range: NSRange(location: 0, length: set.count), copyItems: flag)
703+
}
704+
705+
public convenience init(array set: [Any], range: NSRange, copyItems flag: Bool) {
706+
var objects = set
707+
708+
if let range = Range(range), range.count != set.count || flag {
709+
objects = [Any]()
710+
for index in range.indices {
711+
let object = set[index]
712+
objects.append(flag ? (object as! NSObject).copy() : object)
713+
}
714+
}
715+
716+
self.init(array: objects)
717+
}
718+
719+
public convenience init(set: Set<AnyHashable>) {
720+
self.init(set: set, copyItems: false)
721+
}
722+
723+
public convenience init(set: Set<AnyHashable>, copyItems flag: Bool) {
724+
self.init(array: Array(set), copyItems: flag)
725+
}
726+
727+
public convenience init(objects elements: Any...) {
728+
self.init(array: elements)
729+
}
675730
}
676731

677732

Foundation/NSSet.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,19 @@ open class NSSet : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCodi
8686
self.init(array: [object])
8787
}
8888

89-
internal class func _objects(from aDecoder: NSCoder) -> [NSObject] {
89+
internal class func _objects(from aDecoder: NSCoder, allowDecodingNonindexedArrayKey: Bool = true) -> [NSObject] {
9090
guard aDecoder.allowsKeyedCoding else {
9191
preconditionFailure("Unkeyed coding is unsupported.")
9292
}
93-
if type(of: aDecoder) == NSKeyedUnarchiver.self || aDecoder.containsValue(forKey: "NS.objects") {
93+
if (allowDecodingNonindexedArrayKey && type(of: aDecoder) == NSKeyedUnarchiver.self) || aDecoder.containsValue(forKey: "NS.objects") {
9494
let objects = aDecoder._decodeArrayOfObjectsForKey("NS.objects")
9595
return objects as! [NSObject]
9696
} else {
9797
var objects: [NSObject] = []
9898
var count = 0
99-
while let object = aDecoder.decodeObject(forKey: "NS.object.\(count)") {
99+
var key: String { return "NS.object.\(count)" }
100+
while aDecoder.containsValue(forKey: key) {
101+
let object = aDecoder.decodeObject(forKey: key)
100102
objects.append(object as! NSObject)
101103
count += 1
102104
}

TestFoundation/FixtureValues.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,26 @@ enum Fixtures {
243243
return NSCharacterSet.alphanumerics as NSCharacterSet
244244
}
245245

246+
// ===== NSOrderedSet, NSMutableOrderedSet =====
247+
248+
static let orderedSetOfNumbers = TypedFixture<NSOrderedSet>("NSOrderedSet-Numbers") {
249+
let numbers = [1, 2, 3, 4, 5].map { NSNumber(value: $0) }
250+
return NSOrderedSet(array: numbers)
251+
}
252+
253+
static let orderedSetEmpty = TypedFixture<NSOrderedSet>("NSOrderedSet-Empty") {
254+
return NSOrderedSet()
255+
}
256+
257+
static let mutableOrderedSetOfNumbers = TypedFixture<NSMutableOrderedSet>("NSMutableOrderedSet-Numbers") {
258+
let numbers = [1, 2, 3, 4, 5].map { NSNumber(value: $0) }
259+
return NSMutableOrderedSet(array: numbers)
260+
}
261+
262+
static let mutableOrderedSetEmpty = TypedFixture<NSMutableOrderedSet>("NSMutableOrderedSet-Empty") {
263+
return NSMutableOrderedSet()
264+
}
265+
246266
// ===== Fixture list =====
247267

248268
static let _listOfAllFixtures: [AnyFixture] = [
@@ -273,6 +293,10 @@ enum Fixtures {
273293
AnyFixture(Fixtures.characterSetString),
274294
AnyFixture(Fixtures.characterSetBitmap),
275295
AnyFixture(Fixtures.characterSetBuiltin),
296+
AnyFixture(Fixtures.orderedSetOfNumbers),
297+
AnyFixture(Fixtures.orderedSetEmpty),
298+
AnyFixture(Fixtures.mutableOrderedSetOfNumbers),
299+
AnyFixture(Fixtures.mutableOrderedSetEmpty),
276300
]
277301

278302
// This ensures that we do not have fixtures with duplicate identifiers:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

TestFoundation/TestNSOrderedSet.swift

Lines changed: 65 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,42 +9,6 @@
99

1010
class TestNSOrderedSet : XCTestCase {
1111

12-
static var allTests: [(String, (TestNSOrderedSet) -> () throws -> Void)] {
13-
return [
14-
("test_BasicConstruction", test_BasicConstruction),
15-
("test_Enumeration", test_Enumeration),
16-
("test_Uniqueness", test_Uniqueness),
17-
("test_reversedEnumeration", test_reversedEnumeration),
18-
("test_reversedOrderedSet", test_reversedOrderedSet),
19-
("test_reversedEmpty", test_reversedEmpty),
20-
("test_ObjectAtIndex", test_ObjectAtIndex),
21-
("test_ObjectsAtIndexes", test_ObjectsAtIndexes),
22-
("test_FirstAndLastObjects", test_FirstAndLastObjects),
23-
("test_AddObject", test_AddObject),
24-
("test_AddObjects", test_AddObjects),
25-
("test_RemoveAllObjects", test_RemoveAllObjects),
26-
("test_RemoveObject", test_RemoveObject),
27-
("test_RemoveObjectAtIndex", test_RemoveObjectAtIndex),
28-
("test_IsEqualToOrderedSet", test_IsEqualToOrderedSet),
29-
("test_Subsets", test_Subsets),
30-
("test_ReplaceObject", test_ReplaceObject),
31-
("test_ExchangeObjects", test_ExchangeObjects),
32-
("test_MoveObjects", test_MoveObjects),
33-
("test_InsertObjects", test_InsertObjects),
34-
("test_Insert", test_Insert),
35-
("test_SetObjectAtIndex", test_SetObjectAtIndex),
36-
("test_RemoveObjectsInRange", test_RemoveObjectsInRange),
37-
("test_ReplaceObjectsAtIndexes", test_ReplaceObjectsAtIndexes),
38-
("test_Intersection", test_Intersection),
39-
("test_Subtraction", test_Subtraction),
40-
("test_Union", test_Union),
41-
("test_Initializers", test_Initializers),
42-
("test_Sorting", test_Sorting),
43-
("test_reversedEnumerationMutable", test_reversedEnumerationMutable),
44-
("test_reversedOrderedSetMutable", test_reversedOrderedSetMutable),
45-
]
46-
}
47-
4812
func test_BasicConstruction() {
4913
let set = NSOrderedSet()
5014
let set2 = NSOrderedSet(array: ["foo", "bar"])
@@ -513,4 +477,69 @@ class TestNSOrderedSet : XCTestCase {
513477
XCTAssertEqual(work.lastObject as? String, krow.firstObject as? String)
514478
}
515479

480+
let fixtures = [
481+
Fixtures.orderedSetEmpty,
482+
Fixtures.orderedSetOfNumbers
483+
]
484+
485+
let mutableFixtures = [
486+
Fixtures.mutableOrderedSetEmpty,
487+
Fixtures.mutableOrderedSetOfNumbers
488+
]
489+
490+
func test_codingRoundtrip() throws {
491+
for fixture in fixtures {
492+
try fixture.assertValueRoundtripsInCoder()
493+
}
494+
for fixture in mutableFixtures {
495+
try fixture.assertValueRoundtripsInCoder()
496+
}
497+
}
498+
499+
func test_loadedValuesMatch() throws {
500+
for fixture in fixtures {
501+
try fixture.assertLoadedValuesMatch()
502+
}
503+
for fixture in mutableFixtures {
504+
try fixture.assertLoadedValuesMatch()
505+
}
506+
}
507+
508+
static var allTests: [(String, (TestNSOrderedSet) -> () throws -> Void)] {
509+
return [
510+
("test_BasicConstruction", test_BasicConstruction),
511+
("test_Enumeration", test_Enumeration),
512+
("test_Uniqueness", test_Uniqueness),
513+
("test_reversedEnumeration", test_reversedEnumeration),
514+
("test_reversedOrderedSet", test_reversedOrderedSet),
515+
("test_reversedEmpty", test_reversedEmpty),
516+
("test_ObjectAtIndex", test_ObjectAtIndex),
517+
("test_ObjectsAtIndexes", test_ObjectsAtIndexes),
518+
("test_FirstAndLastObjects", test_FirstAndLastObjects),
519+
("test_AddObject", test_AddObject),
520+
("test_AddObjects", test_AddObjects),
521+
("test_RemoveAllObjects", test_RemoveAllObjects),
522+
("test_RemoveObject", test_RemoveObject),
523+
("test_RemoveObjectAtIndex", test_RemoveObjectAtIndex),
524+
("test_IsEqualToOrderedSet", test_IsEqualToOrderedSet),
525+
("test_Subsets", test_Subsets),
526+
("test_ReplaceObject", test_ReplaceObject),
527+
("test_ExchangeObjects", test_ExchangeObjects),
528+
("test_MoveObjects", test_MoveObjects),
529+
("test_InsertObjects", test_InsertObjects),
530+
("test_Insert", test_Insert),
531+
("test_SetObjectAtIndex", test_SetObjectAtIndex),
532+
("test_RemoveObjectsInRange", test_RemoveObjectsInRange),
533+
("test_ReplaceObjectsAtIndexes", test_ReplaceObjectsAtIndexes),
534+
("test_Intersection", test_Intersection),
535+
("test_Subtraction", test_Subtraction),
536+
("test_Union", test_Union),
537+
("test_Initializers", test_Initializers),
538+
("test_Sorting", test_Sorting),
539+
("test_reversedEnumerationMutable", test_reversedEnumerationMutable),
540+
("test_reversedOrderedSetMutable", test_reversedOrderedSetMutable),
541+
("test_codingRoundtrip", test_codingRoundtrip),
542+
("test_loadedValuesMatch", test_loadedValuesMatch),
543+
]
544+
}
516545
}

0 commit comments

Comments
 (0)