在Swift中,String
结构也被视为类对象,就像使用NSCoder
encodeObject(_:forKey:)
方法一样。我知道String
直接与objective-c类NSString
桥接,但有没有办法让自定义struct
行为相似?也许将它连接到一个自定义类?我希望能够做到这样的事情:
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")
}
}
答案 0 :(得分:2)
目前无法实现。 SE-0058将解决它,但是会从Swift 3中推迟。期望SE-0058的最终实现不仅仅是处理ObjC桥接;例如,在更通用的解决方案中允许C ++或.NET桥接。
答案 1 :(得分:2)
最终,String
和NSString
之间的桥接非常简单。
NSString
只有2个实例变量(字符串指针nxcsptr
,长度nxcslen
)。 String
使用_StringCore
,其中只有3个属性(_baseAddress
,_countAndFlags
和_owner
)。来回转换是硬编码的,并由编译器显式调用。没有自动系统实现用于从结构中生成类,反之亦然。
您必须实施struct
/ class
对(与String
和NSString
一样),并实现从另一个构建一个的初始化程序。
答案 2 :(得分:0)
我找到了一个优雅的解决方案,适用于==
_ObjectiveCBridgeable
,可以由struct
编码;感谢reference提供的Martin R。这是我为感兴趣的人写的库代码。我现在可以这样做:
NSCoder
<强> SortedArray.swift 强>
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")
}
<强> NSSortedArray.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)
}
}
}
<强> NSCoder.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)
}
}
}