swift class properties
Define struct que se trata como una clase en Swift (3)
En última instancia, el puente entre String
y NSString
es bastante simple.
NSString
solo tiene 2 variables de instancia (El puntero de cadena nxcsptr
y la longitud nxcslen
). String
utiliza _StringCore
, que solo tiene 3 propiedades ( _baseAddress
, _countAndFlags
y _owner
). La conversión de ida y vuelta está codificada de forma rígida y el compilador la llama explícitamente. No hay un sistema automático implementado para generar clases fuera de las estructuras, o viceversa.
Tendrás que implementar un par de struct
/ class
(como con String
y NSString
) e implementar los inicializadores que construyen uno desde el otro.
En Swift, una estructura String
también se trata como un objeto de clase, como cuando se utiliza el NSCoder
encodeObject(_:forKey:)
. Sé que String
está puenteado directamente con la clase NSString
-c, NSString
, pero ¿hay alguna manera de hacer una struct
personalizada que se comporte de manera similar? ¿Quizás lo vincule a una clase personalizada? Quiero ser capaz de hacer algo como esto:
struct SortedArray <Value: Comparable> {}
// Would I need to create a bridge between
// SortedArray and NSSortedArray? Can I even do that?
class NSSortedArray <Value: Comparable> : NSObject, NSCoding {
required init?(coder aDecoder: NSCoder) {}
func encodeWithCoder(aCoder: NSCoder) {}
}
class MyClass : NSObject, NSCoding {
private var objects: SortedArray<String> = SortedArray<String>()
required init?(coder aDecoder: NSCoder) {
guard let objects = aDecoder.decodeObjectForKey("objects") as? SortedArray<String> else { return nil }
self.objects = objects
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(objects, forKey: "objects")
}
}
Esto no es posible actualmente. SE-0058 lo abordará, pero se difiere de Swift 3. Se esperaría que una implementación final de SE-0058 maneje algo más que solo el puente ObjC; por ejemplo, permitir el puente C ++ o .NET también en una solución más genérica.
Encontré una solución elegante y funcional que funciona con una struct
_ObjectiveCBridgeable
que puede codificar NSCoder
; gracias a la referencia que proporcionó Martin R. Aquí está el código de la biblioteca que escribí para cualquier persona interesada. Ahora puedo hacer algo como esto:
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)
}
}