在Swift中实现NSCoding协议有什么问题

时间:2014-06-25 19:21:57

标签: objective-c swift

我认为我要小心谨慎,并通过转换一个类来尝试在现有的Obj-C项目上使用Swift。还有一个小而简单的。哦,亲爱的。

将原始obj-c音译为Swift应该是直截了当的,所以看起来如此。不幸的是,虽然编码器到持久存储似乎有效,但它在init编码器的第一行出现EXC_BREAKPOINT错误而崩溃。

IF(并且大写是有意的)NSCoding / Swift提供与NSCoding / ObjC相同的持久性内容,然后我的所有obj-c版本应该能够读取Swift编码的内容,反之亦然。事实证明并非如此 - 当我试图从Swift版本中读取持久性存储时,我完美运行的obj-c版本崩溃了。当然,如果NSCoding正确实现,它应该生成一个可读的东西?否则,应该有单独的NSCodingSwift和NSCodingObjC协议吗?

总而言之,我可以在obj-c中读/写。我不能写/ obj-c和read / swift我可以写/ swift read / obj-c而且我无法在swift中读/写。

以下是两个版本:

let keyBeaconItemNameKey = "name"
let keyBeaconItemUUIDKey = "uuid"
let keyBeaconItemMajorValueKey = "major"
let keyBeaconItemMinorValueKey = "minor"

import UIKit
import CoreLocation

class SMBeaconItem : NSObject, NSCoding
{
    var name : String!
    var uuid : NSUUID!
    var major : NSNumber!
    var minor : NSNumber!

    init(newName : String, newUUID : NSUUID, newMajor : NSNumber, newMinor : NSNumber )
    {
        name = newName
        uuid = newUUID
        major = newMajor
        minor = newMinor
    }

    init( coder decoder : NSCoder!)
    {
        name = decoder.decodeObjectForKey(keyBeaconItemNameKey) as String
        uuid = decoder.decodeObjectForKey(keyBeaconItemUUIDKey) as NSUUID
        major = decoder.decodeObjectForKey(keyBeaconItemMajorValueKey) as NSNumber
        minor = decoder.decodeObjectForKey(keyBeaconItemMinorValueKey) as NSNumber
    }

    func encodeWithCoder( encoder: NSCoder!)
    {
        encoder.encodeObject(name, forKey:keyBeaconItemNameKey)
        encoder.encodeObject(uuid, forKey:keyBeaconItemUUIDKey)
        encoder.encodeObject(major, forKey:keyBeaconItemMajorValueKey)
        encoder.encodeObject(minor, forKey:keyBeaconItemMinorValueKey)
       }
    }

工作原件:

@implementation SMBeaconItem

- (instancetype)initWithName:(NSString *)name uuid:(NSUUID *)uuid major:(CLBeaconMajorValue)major minor:(CLBeaconMinorValue)minor
{
    self = [super init];
    if (!self)
    {
        return nil;
    }

    _name = name;
    _uuid = uuid;
    _majorValue = major;
    _minorValue = minor;

    return self;
}

#pragma mark - Persistence

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];
    if (!self)
    {
        return nil;
    }

    _name = [aDecoder decodeObjectForKey:keyBeaconItemNameKey];
    _uuid = [aDecoder decodeObjectForKey:keyBeaconItemUUIDKey];
    _majorValue = [[aDecoder decodeObjectForKey:keyBeaconItemMajorValueKey] unsignedIntegerValue];
    _minorValue = [[aDecoder decodeObjectForKey:keyBeaconItemMinorValueKey] unsignedIntegerValue];

    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:self.name forKey:keyBeaconItemNameKey];
    [aCoder encodeObject:self.uuid forKey:keyBeaconItemUUIDKey];
    [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:self.majorValue] forKey:keyBeaconItemMajorValueKey];
    [aCoder encodeObject:[NSNumber numberWithUnsignedInteger:self.minorValue] forKey:keyBeaconItemMinorValueKey];
}

@end

感谢您提供任何帮助。

2 个答案:

答案 0 :(得分:0)

唯一让我感到高兴的是,您使用String代替NSString作为name类型。在Apple推出的测试版中,String(奇怪的是)不是NSString的一对一替代品。即,缺少某些方法,需要调用.bridgeToObjectiveC()来获取NSString版本。使用该类型可能会符合NSCoder期望的内容,尽管这种差异不应该如此。

我没有真正测试过这个说法,因为我不在我的开发机器上。但那是我的直觉。尝试一下,看看会发生什么!如果没有任何变化,请尝试切换您设置变量的顺序,看看它是否与name字段或init函数的第一行有关。

答案 1 :(得分:0)

下面的代码工作迅速,可以在UserDefaults中保存检索NSCoding值

import UIKit
import Foundation

class ViewController: UIViewController {

    var employees: Employees?
    let static_key = "nscdeing_data_saved"

    override func viewDidLoad() {
        super.viewDidLoad()
        var request = URLRequest(url: URL(string: "http://dummy.restapiexample.com/api/v1/employees")!, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 60)
        request.httpMethod = "GET"
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            if let status = (response as? HTTPURLResponse)?.statusCode, status == 200, let data = data{
                do {
                    guard let dic = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String:Any] else { return }
                    self.employees = Employees.init(fromDictionary: dic)
                    let archiveData = try NSKeyedArchiver.archivedData(withRootObject: self.employees as Any, requiringSecureCoding: true)
                    UserDefaults.standard.set(archiveData, forKey: self.static_key)
                } catch let error {
                    fatalError(error.localizedDescription)
                }
            }
        }.resume()
    }
    @IBAction func printAction(_ sender: Any) {
        if let data = UserDefaults.standard.data(forKey: static_key){
            do {
                let value = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data)
                print(value as Any)
            } catch let error {
                fatalError(error.localizedDescription)
            }
        }
    }
}

class Employees : NSObject, NSCoding, NSSecureCoding{
    static var supportsSecureCoding: Bool{
        return true
    }

    var data : [Datum]!
    var status : String!
    /**
     * Instantiate the instance using the passed dictionary values to set the properties values
     */
    init(fromDictionary dictionary: [String:Any]){
        status = dictionary["status"] as? String
        data = [Datum]()
        if let dataArray = dictionary["data"] as? [[String:Any]]{
            for dic in dataArray{
                let value = Datum(fromDictionary: dic)
                data.append(value)
            }
        }
    }

    /**
     * Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
     */
    func toDictionary() -> [String:Any]{
        var dictionary = [String:Any]()
        if status != nil{
            dictionary["status"] = status
        }
        if data != nil{
            var dictionaryElements = [[String:Any]]()
            for dataElement in data {
                dictionaryElements.append(dataElement.toDictionary())
            }
            dictionary["data"] = dictionaryElements
        }
        return dictionary
    }

    /**
     * NSCoding required initializer.
     * Fills the data from the passed decoder
     */
    @objc required init(coder aDecoder: NSCoder){
        data = aDecoder.decodeObject(forKey: "data") as? [Datum]
        status = aDecoder.decodeObject(forKey: "status") as? String
    }

    /**
     * NSCoding required method.
     * Encodes mode properties into the decoder
     */
    @objc func encode(with aCoder: NSCoder){
        if data != nil{
            aCoder.encode(data, forKey: "data")
        }
        if status != nil{
            aCoder.encode(status, forKey: "status")
        }
    }
}
class Datum : NSObject, NSCoding, NSSecureCoding{
    static var supportsSecureCoding: Bool{
        return true
    }

    var employeeAge : String!
    var employeeName : String!
    var employeeSalary : String!
    var id : String!
    var profileImage : String!

    /**
     * Instantiate the instance using the passed dictionary values to set the properties values
     */
    init(fromDictionary dictionary: [String:Any]){
        employeeAge = dictionary["employee_age"] as? String
        employeeName = dictionary["employee_name"] as? String
        employeeSalary = dictionary["employee_salary"] as? String
        id = dictionary["id"] as? String
        profileImage = dictionary["profile_image"] as? String
    }

    /**
     * Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
     */
    func toDictionary() -> [String:Any]{
        var dictionary = [String:Any]()
        if employeeAge != nil{
            dictionary["employee_age"] = employeeAge
        }
        if employeeName != nil{
            dictionary["employee_name"] = employeeName
        }
        if employeeSalary != nil{
            dictionary["employee_salary"] = employeeSalary
        }
        if id != nil{
            dictionary["id"] = id
        }
        if profileImage != nil{
            dictionary["profile_image"] = profileImage
        }
        return dictionary
    }

    /**
     * NSCoding required initializer.
     * Fills the data from the passed decoder
     */
    @objc required init(coder aDecoder: NSCoder){
        employeeAge = aDecoder.decodeObject(forKey: "employee_age") as? String
        employeeName = aDecoder.decodeObject(forKey: "employee_name") as? String
        employeeSalary = aDecoder.decodeObject(forKey: "employee_salary") as? String
        id = aDecoder.decodeObject(forKey: "id") as? String
        profileImage = aDecoder.decodeObject(forKey: "profile_image") as? String
    }

    /**
     * NSCoding required method.
     * Encodes mode properties into the decoder
     */
    @objc func encode(with aCoder: NSCoder){
        if employeeAge != nil{
            aCoder.encode(employeeAge, forKey: "employee_age")
        }
        if employeeName != nil{
            aCoder.encode(employeeName, forKey: "employee_name")
        }
        if employeeSalary != nil{
            aCoder.encode(employeeSalary, forKey: "employee_salary")
        }
        if id != nil{
            aCoder.encode(id, forKey: "id")
        }
        if profileImage != nil{
            aCoder.encode(profileImage, forKey: "profile_image")
        }
    }
}