Define struct that is treated like a class in Swift

后端 未结 3 822
-上瘾入骨i
-上瘾入骨i 2021-01-12 05:30

In Swift a String structure is also treated as a class object like when using the NSCoder encodeObject(_:forKey:) method. I do know th

相关标签:
3条回答
  • 2021-01-12 06:08

    This isn't currently possible. SE-0058 will address it, but is deferred out of Swift 3. A final implementation of SE-0058 would be hoped to handle more than just ObjC bridging; for example allowing C++ or .NET bridging as well in a more generic solution.

    0 讨论(0)
  • 2021-01-12 06:12

    Ultimately, the bridging between String and NSString is quite simple.

    NSString only has 2 instance variables (The string pointer nxcsptr, and the length nxcslen). String uses _StringCore, which only has 3 properties (_baseAddress, _countAndFlags, and _owner). The conversion back and forth is hard coded, and called explicitly by the compiler. There's no automatic system implemented for generating classes out of structs, or vice versa.

    You'll have to implement a struct/class pair (like with String and NSString), and implement initializers that construct one from the other.

    0 讨论(0)
  • 2021-01-12 06:19

    I found a working, elegant solution that works with an _ObjectiveCBridgeable struct that can be encoded by NSCoder; thanks to the reference that Martin R provided. Here is the library code I wrote for anyone interested. I can now do something like this:

    func init?(coder aDecoder: NSCoder) {
        guard let numbers = aDecoder.decodeObjectForKey("Numbers") as? SortedArray<Int> else { return nil }
        print(numbers) // Outputs "[1,3,5]"
    }
    
    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(SortedArray<Int>([1,5,3]), forKey: "Numbers")
    }
    

    SortedArray.swift

    //
    //  SortedArray.swift
    //  NoodleKit
    //
    //  Created by Thom Morgan on 8/15/16.
    //  Copyright © 2016 CocoaPods. All rights reserved.
    //
    
    // MARK: - ** Import Modules **
    
    import Foundation
    
    // MARK: - ** SortOrder Enumeration **
    
    /// Ascending or descending sort order enumerations
    public enum SortOrder : Int {
        case Ascending, Descending
    }
    
    // MARK: - ** SortedArray Structure **
    
    /// An array data structure that automatically places elements in order as
    /// they added to the collection.
    public struct SortedArray <Value: Comparable> : CollectionType, _ObjectiveCBridgeable, CustomStringConvertible {
    
        // MARK: - _ObjectiveCBridgeable
    
        /// Required typealias from the `_ObjectiveCBridgeable` private protocol
        public typealias _ObjectiveCType = NSSortedArray<Value>
    
        // MARK: - CollectionType
    
        public typealias Index = Int
        public typealias Generator = IndexingGenerator<SortedArray<Value>>
    
        public var startIndex: Index { return 0 }
        public var endIndex: Index { return values.count }
        public var range: Range<Index> { return 0 ..< values.count }
        public var count: Int { return values.count }
    
        // MARK: - CustomStringConvertible
    
        public var description: String { return "\(values)" }
    
        // MARK: - SortedArray
    
        /// The order in which to sort array elements.
        public var sortOrder: SortOrder {
            willSet { if sortOrder != newValue { values = values.reverse() } }
        }
    
        /// The elements of this array.
        public private (set) var values = [Value]()
    
        /// Whether or not to allow duplicate elements to be added to this array.
        public var uniqueElements: Bool = true
    
        // MARK: - ** Constructor Methods **
    
        // MARK: - SortedArray
    
        /// Verbose constructor in which the sort order can be established at
        /// initialization.
        /// - parameter sortOrder: The order in which to sort array elements.
        /// - parameter values: The initial elements to populate this array. 
        /// - note: The initial values parameter need not be sorted, as it will
        /// automatically be sorted upon initialization.
        /// - returns: An array structure instance with sorted values.
        public init(sortOrder: SortOrder = .Ascending, values: [Value] = [Value]()) {
            self.sortOrder = sortOrder
            self.values = values.sort({ (a: Value, b: Value) -> Bool in
                return sortOrder == .Ascending ? (a < b) : (b < a)
            })
        }
    
        /// Convenience constructor that sets the inital array elements.
        /// - parameter values: The initial elements to populate this array.
        /// - returns: An array structure instance with sorted values in 
        /// ascending order.
        public init(_ values: [Value]) {
            sortOrder = .Ascending
            self.values = values.sort({ (a: Value, b: Value) -> Bool in
                return a < b
            })
        }
    
        /// Duplicating constructor.
        /// - parameter sortedArray: Another array to initialize from.
        /// - returns: An array structure instance with sorted values
        /// identical to `sortedArray`.
        public init(_ sortedArray: SortedArray<Value>) {
            sortOrder = sortedArray.sortOrder
            values = sortedArray.values
        }
    
        /// Bridging constructor from an `NSSortedArray` class instance.
        /// - parameter sortedArray: Another array to initialize from.
        /// - returns: An array structure instance with sorted values
        /// identical to `sortedArray`.
        public init(_ sortedArray: NSSortedArray<Value>) {
            sortOrder = sortedArray.sortOrder
            values = sortedArray.values
        }
    
        // MARK: - ** Public Methods **
    
        // MARK: - _ObjectiveCBridgeable
    
        /// Required static method from the `_ObjectiveCBridgeable` private 
        /// protocol.
        /// - returns: `true`, indicating that this structure is bridgeable to
        /// an Objective-C class, namely `NSSortedArray`.
        public static func _isBridgedToObjectiveC() -> Bool {
            return true
        }
    
        /// Required static method from the `_ObjectiveCBridgeable` private
        /// protocol.
        /// - returns: `NSSortedArray<Value>.self`
        public static func _getObjectiveCType() -> Any.Type {
            return _ObjectiveCType.self
        }
    
        /// Required static method from the `_ObjectiveCBridgeable` private
        /// protocol.
        /// - parameter source: An `NSSortedArray<Value>` instance to force bridge
        /// to `SortedArray<Value>`.
        /// - parameter result: The `SortedArray<Value>` instance created from
        /// the forced bridging.
        public static func _forceBridgeFromObjectiveC(source: _ObjectiveCType, inout result: SortedArray<Value>?) {
            result = SortedArray<Value>(source)
        }
    
        /// Required static method from the `_ObjectiveCBridgeable` private
        /// protocol.
        /// - parameter source: An `NSSortedArray<Value>` instance to conditionally
        /// bridge to `SortedArray<Value>`.
        /// - parameter result: The `SortedArray<Value>` instance created from
        /// the conditional bridging.
        public static func _conditionallyBridgeFromObjectiveC(source: _ObjectiveCType, inout result: SortedArray<Value>?) -> Bool {
            _forceBridgeFromObjectiveC(source, result: &result)
            return true
        }
    
        /// Required method from the `_ObjectiveCBridgeable` private protocol
        /// - returns: An `NSStortedArray<Value>` instance identical to `self`.
        public func _bridgeToObjectiveC() -> _ObjectiveCType {
            return NSSortedArray<Value>(self)
        }
    
        // MARK: - CollectionType
    
        public subscript (index: Index) -> Value {
            get { return values[index] }
            set { values[index] = newValue }
        }
    
        public func generate() -> Generator {
            return Generator(SortedArray(values: values))
        }
    
        /// Insert `newElement` at index `i`.
        ///
        /// - requires: `i <= count`.
        ///
        /// - complexity: O(`self.count`).
        public mutating func insert(value: Value, atIndex index: Index) {
            values.insert(value, atIndex: index)
        }
    
        /// Remove and return the element at index `i`.
        ///
        /// Invalidates all indices with respect to `self`.
        ///
        /// - complexity: O(`self.count`).
        public mutating func removeAtIndex(index: Index) -> Value {
            return values.removeAtIndex(index)
        }
    
        /// Remove all elements.
        ///
        /// - postcondition: `capacity == 0` iff `keepCapacity` is `false`.
        ///
        /// - complexity: O(`self.count`).    
        public mutating func removeAll() {
            values.removeAll()
        }
    
        // MARK: - SortedArray
    
        /// Returns the first index where `value` appears in `self` or `nil` if
        /// `value` is not found.
        ///
        /// - note: This is a significantly less costly implementation of the
        /// default system method `indexOf(element: Element)`.
        ///
        /// - complexity: O(`log(self.count)`)
        ///
        /// - parameter value: The value to search for
        /// - parameter range: The range to search within. If `nil` the entire
        /// range of elements are searched.
        /// - returns: The first index where `value` appears in `self` or `nil` if
        /// `value` is not found.
        @warn_unused_result
        public func indexOf(value: Value, searchRange range: Range<Index>? = nil) -> Index? {
    
            if values.count == 0 { return nil }
    
            let range = range ?? 0 ..< values.count
            let index = (range.startIndex + range.length / 2)
            let val = values[index]
    
            if range.length == 1 {
                return val == value ? index : nil
            } else if (val > value && sortOrder == .Ascending) || (val < value && sortOrder == .Descending) {
                return indexOf(value, searchRange: range.startIndex ..< index)
            }
    
            return indexOf(value, searchRange: index ..< range.endIndex)
    
        }
    
        /// Returns the first index where `value` would be placed in sorted order
        /// in `self`.
        ///
        /// - complexity: O(`log(self.count)`)
        ///
        /// - parameter value: The value to search for.
        /// - parameter range: The range to search within. If `nil` the entire
        /// range of elements are searched.
        /// - returns: Returns the first index where `value` would be placed
        /// in sorted order.
        @warn_unused_result
        public func ordinalIndexForValue(value: Value, searchRange range: Range<Index>? = nil) -> Index {
    
            if values.count == 0 { return 0 }
    
            let range = range ?? 0 ..< values.count
            let index = (range.startIndex + range.length / 2)
            let val = values[index]
    
            if range.length == 1 {
                return (val > value && sortOrder == .Ascending) || (val < value && sortOrder == .Descending) ? index : index + 1
            } else if (val > value && sortOrder == .Ascending) || (val < value && sortOrder == .Descending) {
                return ordinalIndexForValue(value, searchRange: range.startIndex ..< index)
            }
    
            return ordinalIndexForValue(value, searchRange: index ..< range.endIndex)
    
        }
    
        /// Adds a value to `self` in sorted order.
        /// - parameter value: The value to add.
        /// - returns: The index where `value` was inserted, or `nil` if
        /// `uniqueElements` is set to `true` and `value` already exists in
        /// `self.
        ///
        /// - complexity: O(`log(self.count)`)
        public mutating func add(value: Value) -> Index? {
            var index = 0
            if values.count == 0 { values.append(value) }
            else {
                if uniqueElements && indexOf(value) != nil { return nil }
                index = ordinalIndexForValue(value)
                values.insert(value, atIndex: index)
            }
            return index
        }
    
        /// Removes all instances of `value` from `self`
        /// - parameter value: The `value` to remove from `self`.
        ///
        /// - complexity: O(`log(self.count) * n`) where `n` is the number of
        /// times `value` occurs in `self`.
        public mutating func remove(value: Value){
            var index = indexOf(value)
            while index != nil {
                values.removeAtIndex(index!)
                index = indexOf(value)
            }
        }
    
    }
    

    NSSortedArray.swift

    //
    //  NSSortedArray.swift
    //  NoodleKit
    //
    //  Created by Thom Morgan on 6/29/16.
    //  Copyright © 2016 NoodleOfDeath. All rights reserved.
    //
    
    // MARK: - ** Import Modules **
    
    import Foundation
    
    private struct CodingKeys {
        static let SortOrder = "SortOrder"
        static let Values = "Values"
    }
    
    // MARK: - ** NSSortedArray Class **
    
    /// An array class that automatically places elements in order as
    /// they added to the collection.
    public class NSSortedArray <Value: Comparable> : NSObject, CollectionType, NSCoding {
    
        // MARK: - CollectionType
    
        public typealias Index = Int
        public typealias Generator = IndexingGenerator<NSSortedArray<Value>>
    
        public var startIndex: Index { return 0 }
        public var endIndex: Index { return values.count }
        public var range: Range<Index> { return 0 ..< values.count }
        public var count: Int { return values.count }
    
        // MARK: - CustomStringConvertible
    
        public override var description: String { return "\(values)" }
    
        // MARK: - NSSortedArray
    
        /// The order in which to sort array elements.
        public var sortOrder: SortOrder {
            willSet { if sortOrder != newValue { values = values.reverse() } }
        }
    
        /// The elements of this array.
        public private (set) var values = [Value]()
    
        /// Whether or not to allow duplicate elements to be added to this array.
        public var uniqueElements: Bool = true
    
        // MARK: - ** Constructor Methods **
    
        // MARK: - NSSortedArray
    
        /// Verbose constructor in which the sort order can be established at
        /// initialization.
        /// - parameter sortOrder: The order in which to sort array elements.
        /// - parameter values: The initial elements to populate this array.
        /// - note: The initial values parameter need not be sorted, as it will
        /// automatically be sorted upon initialization.
        /// - returns: An array structure instance with sorted values.
        public init(sortOrder: SortOrder = .Ascending, values: [Value] = [Value]()) {
            self.sortOrder = sortOrder
            self.values = values.sort({ (a: Value, b: Value) -> Bool in
                return sortOrder == .Ascending ? (a < b) : (b < a)
            })
        }
    
        /// Convenience constructor that sets the inital array elements.
        /// - parameter values: The initial elements to populate this array.
        /// - returns: An array structure instance with sorted values in
        /// ascending order.
        public init(_ values: [Value]) {
            sortOrder = .Ascending
            self.values = values.sort({ (a: Value, b: Value) -> Bool in
                return a < b
            })
        }
    
        /// Duplicating constructor.
        /// - parameter sortedArray: Another array to initialize from.
        /// - returns: An array structure instance with sorted values
        /// identical to `sortedArray`.
        public init(_ sortedArray: NSSortedArray<Value>) {
            sortOrder = sortedArray.sortOrder
            values = sortedArray.values
        }
    
        /// Bridging constructor from a `SortedArray` structure instance.
        /// - parameter sortedArray: Another array to initialize from.
        /// - returns: An array class instance with sorted values
        /// identical to `sortedArray`.
        public init(_ sortedArray: SortedArray<Value>) {
            sortOrder = sortedArray.sortOrder
            values = sortedArray.values
        }
    
        // MARK: - NSCoding
    
        public convenience required init?(coder aDecoder: NSCoder) {
            guard let sortOrder = SortOrder(rawValue: aDecoder.decodeIntegerForKey(CodingKeys.SortOrder)) else { return nil }
            guard let values = aDecoder.decodeObjectForKey(CodingKeys.Values) as? [Value] else { return nil }
            self.init(sortOrder: sortOrder, values: values)
        }
    
        public func encodeWithCoder(aCoder: NSCoder) {
            aCoder.encodeInteger(sortOrder.rawValue, forKey: CodingKeys.SortOrder)
            aCoder.encodeObject(values, forKey: CodingKeys.Values)
        }
    
        // MARK: - CollectionType
    
        public subscript (index: Index) -> Value {
            get { return values[index] }
            set { values[index] = newValue }
        }
    
        public func generate() -> Generator {
            return Generator(NSSortedArray(values: values))
        }
    
        /// Insert `newElement` at index `i`.
        ///
        /// - requires: `i <= count`.
        ///
        /// - complexity: O(`self.count`).
        public func insert(value: Value, atIndex index: Index) {
            values.insert(value, atIndex: index)
        }
    
        /// Remove and return the element at index `i`.
        ///
        /// Invalidates all indices with respect to `self`.
        ///
        /// - complexity: O(`self.count`).
        public func removeAtIndex(index: Index) -> Value {
            return values.removeAtIndex(index)
        }
    
        /// Remove all elements.
        ///
        /// - postcondition: `capacity == 0` iff `keepCapacity` is `false`.
        ///
        /// - complexity: O(`self.count`).
        public func removeAll(keepCapacity keepCapacity: Bool = false) {
            values.removeAll(keepCapacity: keepCapacity)
        }
    
        // MARK: - NSSortedArray
    
        /// Returns the first index where `value` appears in `self` or `nil` if
        /// `value` is not found.
        ///
        /// - note: This is a significantly less costly implementation of the
        /// default system method `indexOf(element: Element)`.
        ///
        /// - complexity: O(`log(self.count)`)
        ///
        /// - parameter value: The value to search for.
        /// - parameter range: The range to search within. If `nil` the entire
        /// range of elements are searched.
        /// - returns: The first index where `value` appears in `self` or `nil` if
        /// `value` is not found.
        @warn_unused_result
        public func indexOf(value: Value, searchRange range: Range<Index>? = nil) -> Index? {
    
            if values.count == 0 { return nil }
    
            let range = range ?? 0 ..< values.count
            let index = (range.startIndex + range.length / 2)
            let val = values[index]
    
            if range.length == 1 {
                return val == value ? index : nil
            } else if (val > value && sortOrder == .Ascending) || (val < value && sortOrder == .Descending) {
                return indexOf(value, searchRange: range.startIndex ..< index)
            }
    
            return indexOf(value, searchRange: index ..< range.endIndex)
    
        }
    
        /// Returns the first index where `value` would be placed in sorted order
        /// in `self`.
        ///
        /// - complexity: O(`log(self.count)`)
        ///
        /// - parameter value: The value to search for.
        /// - parameter range: The range to search within. If `nil` the entire
        /// range of elements are searched.
        /// - returns: The first index where `value` would be placed in sorted
        /// order in `self`.
        @warn_unused_result
        public func ordinalIndexForValue(value: Value, searchRange range: Range<Index>? = nil) -> Index {
    
            if values.count == 0 { return 0 }
    
            let range = range ?? 0 ..< values.count
            let index = (range.startIndex + range.length / 2)
            let val = values[index]
    
            if range.length == 1 {
                return (val > value && sortOrder == .Ascending) || (val < value && sortOrder == .Descending) ? index : index + 1
            } else if (val > value && sortOrder == .Ascending) || (val < value && sortOrder == .Descending) {
                return ordinalIndexForValue(value, searchRange: range.startIndex ..< index)
            }
    
            return ordinalIndexForValue(value, searchRange: index ..< range.endIndex)
    
        }
    
        /// Adds a value to `self` in sorted order.
        /// - parameter value: The value to add.
        /// - returns: The index where `value` was inserted, or `nil` if
        /// `uniqueElements` is set to `true` and `value` already exists in
        /// `self.
        ///
        /// - complexity: O(`log(self.count)`)
        public func add(value: Value) -> Index? {
            var index = 0
            if values.count == 0 { values.append(value) }
            else {
                if uniqueElements && indexOf(value) != nil { return nil }
                index = ordinalIndexForValue(value)
                values.insert(value, atIndex: index)
            }
            return index
        }
    
        /// Removes all instances of `value` from `self`
        /// - parameter value: The `value` to remove from `self`.
        ///
        /// - complexity: O(`log(self.count) * n`) where `n` is the number of
        /// times `value` occurs in `self`.
        public func remove(value: Value){
            var index = indexOf(value)
            while index != nil {
                values.removeAtIndex(index!)
                index = indexOf(value)
            }
        }
    
    }
    

    NSCoder.swift

    //
    //  NSCoder.swift
    //  NoodleKit
    //
    //  Created by Thom Morgan on 8/15/16.
    //  Copyright © 2016 CocoaPods. All rights reserved.
    //
    
    // MARK: - ** Import Modules **
    
    import Foundation
    
    // MARK: - ** NSCoder - _ObjectiveCBridgeable Encoding Compatibility **
    
    extension NSCoder {
    
        /// Encodes an `_ObjectiveCBridgeable` data structure.
        /// - important: The objective-c class being bridged to must conform to
        /// `NSCoding`.
        /// - parameter object: The object to encode.
        public func encodeObject<T: _ObjectiveCBridgeable>(object: T?) {
            encodeObject(object?._bridgeToObjectiveC())
        }
    
        /// Encodes an `_ObjectiveCBridgeable` data structure as a root object.
        /// - important: The objective-c class being bridged to must conform to
        /// `NSCoding`.
        /// - parameter object: The object to encode.
        public func encodeRootObject<T: _ObjectiveCBridgeable>(object: T) {
            encodeRootObject(object._bridgeToObjectiveC())
        }
    
        /// Encodes an `_ObjectiveCBridgeable` conditional data structure.
        /// - important: The objective-c class being bridged to must conform to
        /// `NSCoding`.
        /// - parameter object: The object to encode.
        public func encodeConditionalObject<T: _ObjectiveCBridgeable>(object: T?) {
            encodeConditionalObject(object?._bridgeToObjectiveC())
        }
    
        /// Encodes an `_ObjectiveCBridgeable` data structure and maps it to a 
        /// specific `key`.
        /// - important: The objective-c class being bridged to must conform to
        /// `NSCoding`.
        /// - parameter object: The object to encode.
        /// - parameter key: The key to associate with this object.
        public func encodeObject<T: _ObjectiveCBridgeable>(object: T?, forKey key: String) {
            encodeObject(object?._bridgeToObjectiveC(), forKey: key)
        }
    
        /// Encodes an `_ObjectiveCBridgeable` conditional data structure and maps
        /// it to a specific `key`.
        /// - important: The objective-c class being bridged to must conform to
        /// `NSCoding`.
        /// - parameter object: The object to encode.
        /// - parameter key: The key to associate with this object.
        public func encodeConditionalObject<T: _ObjectiveCBridgeable>(object: T?, forKey key: String) {
            encodeConditionalObject(object?._bridgeToObjectiveC(), forKey: key)
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题