2016-08-08 18 views
8

Trong Swift, cấu trúc String cũng được coi là đối tượng lớp như khi sử dụng phương thức NSCoderencodeObject(_:forKey:). Tôi biết rằng String được kết nối trực tiếp với lớp mục tiêu-c, NSString, nhưng có cách nào để tạo một tùy chỉnh struct hoạt động tương tự không? Có lẽ cầu nó vào một lớp học tùy chỉnh? Tôi muốn có thể thực hiện một việc như sau:Xác định cấu trúc được xử lý như một lớp trong Swift

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") 
    } 
} 
+1

Hiện tại không có cách nào để thêm cầu nối ngầm vào ObjC vào các loại tùy chỉnh. Việc thực hiện hiện tại đòi hỏi một số phép thuật biên dịch. –

+0

@RobNapier +1 để làm rõ. Tôi vẫn chưa ở trình độ kỹ năng để đóng góp, nhưng có lẽ đây có thể là một chủ đề tiềm năng cho sự tiến hóa nhanh chóng? Tôi sẽ đánh giá rất cao bất cứ ai theo đuổi nó với một đề nghị, nếu họ thấy lợi ích. –

+1

https://github.com/apple/swift-evolution/blob/master/proposals/0058-objectivecbridgeable.md –

Trả lời

0

Tôi tìm thấy giải pháp làm việc, thanh lịch hoạt động với _ObjectiveCBridgeablestruct có thể được mã hóa bởi NSCoder; nhờ số điện thoại reference được cung cấp Martin R. Đây là mã thư viện tôi đã viết cho bất kỳ ai quan tâm. bây giờ tôi có thể làm một cái gì đó như thế này:

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) 
    } 

} 
2

Hiện tại, điều này không thể thực hiện được. SE-0058 sẽ giải quyết nó, nhưng được hoãn lại của Swift 3. Việc thực hiện cuối cùng SE-0058 sẽ được hy vọng để xử lý nhiều hơn chỉ là ObjC bắc cầu; ví dụ cho phép C++ hoặc .NET bridging cũng như trong một giải pháp chung chung hơn.

+0

Điều gì về điều này http://stackoverflow.com/questions/35893517/is-it-possible-to-replicate-swifts-automatic-numeric-value-bridging-to-foundatio? –

+1

'_ObjectiveCBridgeable' là riêng tư. Trong khi nó có thể làm việc, không có thử nghiệm rằng nó hoạt động với bất cứ điều gì, nhưng loại stdlib. Đối với mã sở thích, chắc chắn, nhưng tôi sẽ không gửi bất cứ thứ gì dựa trên các chi tiết triển khai không có giấy tờ. –

2

Cuối cùng, cầu nối giữa StringNSString khá đơn giản.

NSString chỉ có 2 biến mẫu (Con trỏ chuỗi nxcsptr và độ dài nxcslen). String sử dụng _StringCore, chỉ có 3 thuộc tính (_baseAddress, _countAndFlags_owner). Việc chuyển đổi qua lại được mã hóa cứng và được trình biên dịch gọi một cách rõ ràng. Không có hệ thống tự động nào được triển khai để tạo các lớp ngoài cấu trúc, hoặc ngược lại.

Bạn sẽ phải triển khai cặp struct/class (như với StringNSString) và triển khai bộ khởi tạo xây dựng cái kia.

Các vấn đề liên quan