| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See http://swift.org/LICENSE.txt for license information |
| // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| |
| import CoreFoundation |
| |
| // Archives created using the class method archivedRootDataWithObject used this key for the root object in the hierarchy of encoded objects. The NSKeyedUnarchiver class method unarchiveObjectWithData: will look for this root key as well. You can also use it as the key for the root object in your own archives. |
| public let NSKeyedArchiveRootObjectKey: String = "root" |
| |
| internal let NSKeyedArchiveNullObjectReference = _NSKeyedArchiverUID(value: 0) |
| internal let NSKeyedArchiveNullObjectReferenceName: String = "$null" |
| internal let NSKeyedArchivePlistVersion = 100000 |
| internal let NSKeyedArchiverSystemVersion : UInt32 = 2000 |
| |
| internal func escapeArchiverKey(_ key: String) -> String { |
| if key.hasPrefix("$") { |
| return "$" + key |
| } else { |
| return key |
| } |
| } |
| |
| internal let NSPropertyListClasses : [AnyClass] = [ |
| NSArray.self, |
| NSDictionary.self, |
| NSString.self, |
| NSData.self, |
| NSDate.self, |
| NSNumber.self |
| ] |
| |
| open class NSKeyedArchiver : NSCoder { |
| struct ArchiverFlags : OptionSet { |
| let rawValue : UInt |
| |
| init(rawValue : UInt) { |
| self.rawValue = rawValue |
| } |
| |
| static let none = ArchiverFlags(rawValue: 0) |
| static let finishedEncoding = ArchiverFlags(rawValue : 1) |
| static let requiresSecureCoding = ArchiverFlags(rawValue: 2) |
| } |
| |
| private class EncodingContext { |
| // the object container that is being encoded |
| var dict = Dictionary<String, Any>() |
| // the index used for non-keyed objects (encodeObject: vs encodeObject:forKey:) |
| var genericKey : UInt = 0 |
| } |
| |
| private static var _classNameMap = Dictionary<String, String>() |
| private static var _classNameMapLock = NSLock() |
| |
| private var _stream : AnyObject |
| private var _flags = ArchiverFlags(rawValue: 0) |
| private var _containers : Array<EncodingContext> = [EncodingContext()] |
| private var _objects : Array<Any> = [NSKeyedArchiveNullObjectReferenceName] |
| private var _objRefMap : Dictionary<AnyHashable, UInt32> = [:] |
| private var _replacementMap : Dictionary<AnyHashable, Any> = [:] |
| private var _classNameMap : Dictionary<String, String> = [:] |
| private var _classes : Dictionary<String, _NSKeyedArchiverUID> = [:] |
| private var _cache : Array<_NSKeyedArchiverUID> = [] |
| |
| open weak var delegate: NSKeyedArchiverDelegate? |
| open var outputFormat = PropertyListSerialization.PropertyListFormat.binary { |
| willSet { |
| if outputFormat != PropertyListSerialization.PropertyListFormat.xml && |
| outputFormat != PropertyListSerialization.PropertyListFormat.binary { |
| NSUnimplemented() |
| } |
| } |
| } |
| |
| open class func archivedData(withRootObject rootObject: Any) -> Data { |
| let data = NSMutableData() |
| let keyedArchiver = NSKeyedArchiver(forWritingWith: data) |
| |
| keyedArchiver.encode(rootObject, forKey: NSKeyedArchiveRootObjectKey) |
| keyedArchiver.finishEncoding() |
| |
| return data._swiftObject |
| } |
| |
| open class func archiveRootObject(_ rootObject: Any, toFile path: String) -> Bool { |
| var fd : Int32 = -1 |
| var auxFilePath : String |
| var finishedEncoding : Bool = false |
| |
| do { |
| (fd, auxFilePath) = try _NSCreateTemporaryFile(path) |
| } catch _ { |
| return false |
| } |
| |
| defer { |
| do { |
| if finishedEncoding { |
| try _NSCleanupTemporaryFile(auxFilePath, path) |
| } else { |
| try FileManager.default.removeItem(atPath: auxFilePath) |
| } |
| } catch _ { |
| } |
| } |
| |
| let writeStream = _CFWriteStreamCreateFromFileDescriptor(kCFAllocatorSystemDefault, fd)! |
| |
| if !CFWriteStreamOpen(writeStream) { |
| return false |
| } |
| |
| let keyedArchiver = NSKeyedArchiver(output: writeStream) |
| |
| keyedArchiver.encode(rootObject, forKey: NSKeyedArchiveRootObjectKey) |
| keyedArchiver.finishEncoding() |
| finishedEncoding = keyedArchiver._flags.contains(ArchiverFlags.finishedEncoding) |
| |
| CFWriteStreamClose(writeStream) |
| |
| return finishedEncoding |
| } |
| |
| public override init() { |
| NSUnimplemented() |
| } |
| |
| private init(output: AnyObject) { |
| self._stream = output |
| super.init() |
| } |
| |
| public convenience init(forWritingWith data: NSMutableData) { |
| self.init(output: data) |
| } |
| |
| private func _writeXMLData(_ plist : NSDictionary) -> Bool { |
| var success = false |
| |
| if let data = self._stream as? NSMutableData { |
| let xml : CFData? |
| |
| xml = _CFPropertyListCreateXMLDataWithExtras(kCFAllocatorSystemDefault, plist) |
| if let unwrappedXml = xml { |
| data.append(unwrappedXml._swiftObject) |
| success = true |
| } |
| } else { |
| success = CFPropertyListWrite(plist, self._stream as! CFWriteStream, |
| kCFPropertyListXMLFormat_v1_0, 0, nil) > 0 |
| } |
| |
| return success |
| } |
| |
| private func _writeBinaryData(_ plist : NSDictionary) -> Bool { |
| return __CFBinaryPlistWriteToStream(plist, self._stream) > 0 |
| } |
| |
| |
| /// If encoding has not yet finished, then invoking this property will call finishEncoding and return the data. If you initialized the keyed archiver with a specific mutable data instance, then it will be returned from this property after finishEncoding is called. |
| open var encodedData: Data { |
| NSUnimplemented() |
| } |
| |
| open func finishEncoding() { |
| if _flags.contains(ArchiverFlags.finishedEncoding) { |
| return |
| } |
| |
| var plist = Dictionary<String, Any>() |
| var success : Bool |
| |
| plist["$archiver"] = NSStringFromClass(type(of: self)) |
| plist["$version"] = NSKeyedArchivePlistVersion |
| plist["$objects"] = self._objects |
| plist["$top"] = self._containers[0].dict |
| |
| |
| |
| if let unwrappedDelegate = self.delegate { |
| unwrappedDelegate.archiverWillFinish(self) |
| } |
| |
| let nsPlist = plist._bridgeToObjectiveC() |
| |
| if self.outputFormat == PropertyListSerialization.PropertyListFormat.xml { |
| success = _writeXMLData(nsPlist) |
| } else { |
| success = _writeBinaryData(nsPlist) |
| } |
| |
| if let unwrappedDelegate = self.delegate { |
| unwrappedDelegate.archiverDidFinish(self) |
| } |
| |
| if success { |
| let _ = self._flags.insert(ArchiverFlags.finishedEncoding) |
| } |
| } |
| |
| open class func setClassName(_ codedName: String?, for cls: AnyClass) { |
| let clsName = String(describing: type(of: cls)) |
| _classNameMapLock.synchronized { |
| _classNameMap[clsName] = codedName |
| } |
| } |
| |
| open func setClassName(_ codedName: String?, for cls: AnyClass) { |
| let clsName = String(describing: type(of: cls)) |
| _classNameMap[clsName] = codedName |
| } |
| |
| open override var systemVersion: UInt32 { |
| return NSKeyedArchiverSystemVersion |
| } |
| |
| open override var allowsKeyedCoding: Bool { |
| return true |
| } |
| |
| private func _validateStillEncoding() -> Bool { |
| if self._flags.contains(ArchiverFlags.finishedEncoding) { |
| fatalError("Encoder already finished") |
| } |
| |
| return true |
| } |
| |
| private class func _supportsSecureCoding(_ objv : Any?) -> Bool { |
| var supportsSecureCoding : Bool = false |
| |
| if let secureCodable = objv as? NSSecureCoding { |
| supportsSecureCoding = type(of: secureCodable).supportsSecureCoding |
| } |
| |
| return supportsSecureCoding |
| } |
| |
| private func _validateObjectSupportsSecureCoding(_ objv : Any?) { |
| if let objv = objv, self.requiresSecureCoding && |
| !NSKeyedArchiver._supportsSecureCoding(objv) { |
| fatalError("Secure coding required when encoding \(objv)") |
| } |
| } |
| |
| private func _createObjectRefCached(_ uid : UInt32) -> _NSKeyedArchiverUID { |
| if uid == 0 { |
| return NSKeyedArchiveNullObjectReference |
| } else if Int(uid) <= self._cache.count { |
| return self._cache[Int(uid) - 1] |
| } else { |
| let objectRef = _NSKeyedArchiverUID(value: uid) |
| self._cache.insert(objectRef, at: Int(uid) - 1) |
| return objectRef |
| } |
| } |
| |
| /** |
| Return a new object identifier, freshly allocated if need be. A placeholder null |
| object is associated with the reference. |
| */ |
| private func _referenceObject(_ objv: Any?, conditional: Bool = false) -> _NSKeyedArchiverUID? { |
| var uid : UInt32? |
| |
| if objv == nil { |
| return NSKeyedArchiveNullObjectReference |
| } |
| |
| let value = _SwiftValue.store(objv)! |
| |
| uid = self._objRefMap[value] |
| if uid == nil { |
| if conditional { |
| return nil // object has not been unconditionally encoded |
| } |
| |
| uid = UInt32(self._objects.count) |
| |
| self._objRefMap[value] = uid |
| self._objects.insert(NSKeyedArchiveNullObjectReferenceName, at: Int(uid!)) |
| } |
| |
| return _createObjectRefCached(uid!) |
| } |
| |
| /** |
| Returns true if the object has already been encoded. |
| */ |
| private func _haveVisited(_ objv: Any?) -> Bool { |
| if objv == nil { |
| return true // always have a null reference |
| } else { |
| return self._objRefMap[_SwiftValue.store(objv)] != nil |
| } |
| } |
| |
| /** |
| Get or create an object reference, and associate the object. |
| */ |
| private func _addObject(_ objv: Any?) -> _NSKeyedArchiverUID? { |
| let haveVisited = _haveVisited(objv) |
| let objectRef = _referenceObject(objv) |
| |
| if !haveVisited { |
| _setObject(objv!, forReference: objectRef!) |
| } |
| |
| return objectRef |
| } |
| |
| private func _pushEncodingContext(_ encodingContext: EncodingContext) { |
| self._containers.append(encodingContext) |
| } |
| |
| private func _popEncodingContext() { |
| self._containers.removeLast() |
| } |
| |
| private var _currentEncodingContext : EncodingContext { |
| return self._containers.last! |
| } |
| |
| /** |
| Associate an encoded object or reference with a key in the current encoding context |
| */ |
| private func _setObjectInCurrentEncodingContext(_ object : Any?, forKey key: String? = nil, escape: Bool = true) { |
| let encodingContext = self._containers.last! |
| var encodingKey : String |
| |
| if key != nil { |
| if escape { |
| encodingKey = escapeArchiverKey(key!) |
| } else { |
| encodingKey = key! |
| } |
| } else { |
| encodingKey = _nextGenericKey() |
| } |
| |
| if encodingContext.dict[encodingKey] != nil { |
| NSLog("*** NSKeyedArchiver warning: replacing existing value for key '\(encodingKey)'; probable duplication of encoding keys in class hierarchy") |
| } |
| |
| encodingContext.dict[encodingKey] = object |
| } |
| |
| /** |
| The generic key is used for objects that are encoded without a key. It is a per-encoding |
| context monotonically increasing integer prefixed with "$". |
| */ |
| private func _nextGenericKey() -> String { |
| let key = "$" + String(_currentEncodingContext.genericKey) |
| _currentEncodingContext.genericKey += 1 |
| return key |
| } |
| |
| /** |
| Update replacement object mapping |
| */ |
| private func replaceObject(_ object: Any, withObject replacement: Any?) { |
| if let unwrappedDelegate = self.delegate { |
| unwrappedDelegate.archiver(self, willReplace: object, with: replacement) |
| } |
| |
| self._replacementMap[_SwiftValue.store(object)] = replacement |
| } |
| |
| /** |
| Returns true if the type cannot be encoded directly (i.e. is a container type) |
| */ |
| private func _isContainer(_ objv: Any?) -> Bool { |
| // Note that we check for class equality rather than membership, because |
| // their mutable subclasses are as object references |
| guard let obj = objv else { return false } |
| if obj is String { return false } |
| guard let nsObject = obj as? NSObject else { return true } |
| return !(type(of: nsObject) === NSString.self || type(of: nsObject) === NSNumber.self || type(of: nsObject) === NSData.self) |
| } |
| |
| /** |
| Associates an object with an existing reference |
| */ |
| private func _setObject(_ objv: Any, forReference reference : _NSKeyedArchiverUID) { |
| let index = Int(reference.value) |
| self._objects[index] = objv |
| } |
| |
| /** |
| Returns a dictionary describing class metadata for a class |
| */ |
| private func _classDictionary(_ clsv: AnyClass) -> Dictionary<String, Any> { |
| func _classNameForClass(_ clsv: AnyClass) -> String? { |
| var className : String? |
| |
| className = classNameForClass(clsv) |
| if className == nil { |
| className = NSKeyedArchiver.classNameForClass(clsv) |
| } |
| |
| return className |
| } |
| |
| var classDict : [String:Any] = [:] |
| let className = NSStringFromClass(clsv) |
| let mappedClassName = _classNameForClass(clsv) |
| |
| if mappedClassName != nil && mappedClassName != className { |
| // If we have a mapped class name, OS X only encodes the mapped name |
| classDict["$classname"] = mappedClassName |
| } else { |
| var classChain : [String] = [] |
| var classIter : AnyClass? = clsv |
| |
| classDict["$classname"] = className |
| |
| repeat { |
| classChain.append(NSStringFromClass(classIter!)) |
| classIter = _getSuperclass(classIter!) |
| } while classIter != nil |
| |
| classDict["$classes"] = classChain |
| |
| if let ns = clsv as? NSObject.Type { |
| let classHints = ns.classFallbacksForKeyedArchiver() |
| if classHints.count > 0 { |
| classDict["$classhints"] = classHints |
| } |
| } |
| } |
| |
| return classDict |
| } |
| |
| /** |
| Return an object reference for a class |
| |
| Because _classDictionary() returns a dictionary by value, and every |
| time we bridge to NSDictionary we get a new object (the hash code is |
| different), we maintain a private mapping between class name and |
| object reference to avoid redundantly encoding class metadata |
| */ |
| private func _classReference(_ clsv: AnyClass) -> _NSKeyedArchiverUID? { |
| let className = NSStringFromClass(clsv) |
| var classRef = self._classes[className] // keyed by actual class name |
| |
| if classRef == nil { |
| let classDict = _classDictionary(clsv) |
| classRef = _addObject(classDict._bridgeToObjectiveC()) |
| |
| if let unwrappedClassRef = classRef { |
| self._classes[className] = unwrappedClassRef |
| } |
| } |
| |
| return classRef |
| } |
| |
| /** |
| Return the object replacing another object (if any) |
| */ |
| private func _replacementObject(_ object: Any?) -> Any? { |
| var objectToEncode : Any? = nil // object to encode after substitution |
| |
| // nil cannot be mapped |
| if object == nil { |
| return nil |
| } |
| |
| // check replacement cache |
| objectToEncode = self._replacementMap[object as! AnyHashable] |
| if objectToEncode != nil { |
| return objectToEncode |
| } |
| |
| // object replaced by NSObject.replacementObjectForKeyedArchiver |
| // if it is replaced with nil, it cannot be further replaced |
| if let ns = objectToEncode as? NSObject { |
| objectToEncode = ns.replacementObjectForKeyedArchiver(self) |
| if objectToEncode == nil { |
| replaceObject(object!, withObject: nil) |
| return nil |
| } |
| } |
| |
| if objectToEncode == nil { |
| objectToEncode = object |
| } |
| |
| // object replaced by delegate. If the delegate returns nil, nil is encoded |
| if let unwrappedDelegate = self.delegate { |
| objectToEncode = unwrappedDelegate.archiver(self, willEncode: objectToEncode!) |
| replaceObject(object!, withObject: objectToEncode) |
| } |
| |
| return objectToEncode |
| } |
| |
| /** |
| Internal function to encode an object. Returns the object reference. |
| */ |
| private func _encodeObject(_ objv: Any?, conditional: Bool = false) -> NSObject? { |
| var object : Any? = nil // object to encode after substitution |
| var objectRef : _NSKeyedArchiverUID? // encoded object reference |
| let haveVisited : Bool |
| |
| let _ = _validateStillEncoding() |
| |
| haveVisited = _haveVisited(objv) |
| object = _replacementObject(objv) |
| |
| // bridge value types |
| if let bridgedObject = object as? _ObjectBridgeable { |
| object = bridgedObject._bridgeToAnyObject() |
| } |
| |
| objectRef = _referenceObject(object, conditional: conditional) |
| guard let unwrappedObjectRef = objectRef else { |
| // we can return nil if the object is being conditionally encoded |
| return nil |
| } |
| |
| _validateObjectSupportsSecureCoding(object) |
| |
| if !haveVisited { |
| var encodedObject : Any |
| |
| if _isContainer(object) { |
| guard let codable = object as? NSCoding else { |
| fatalError("Object \(object) does not conform to NSCoding") |
| } |
| |
| let innerEncodingContext = EncodingContext() |
| _pushEncodingContext(innerEncodingContext) |
| codable.encode(with: self) |
| |
| let ns = object as? NSObject |
| let cls : AnyClass = ns?.classForKeyedArchiver ?? type(of: object!) as! AnyClass |
| |
| _setObjectInCurrentEncodingContext(_classReference(cls), forKey: "$class", escape: false) |
| _popEncodingContext() |
| encodedObject = innerEncodingContext.dict |
| } else { |
| encodedObject = object! |
| } |
| |
| _setObject(encodedObject, forReference: unwrappedObjectRef) |
| } |
| |
| if let unwrappedDelegate = self.delegate { |
| unwrappedDelegate.archiver(self, didEncode: object) |
| } |
| |
| return unwrappedObjectRef |
| } |
| |
| /** |
| Encode an object and associate it with a key in the current encoding context. |
| */ |
| private func _encodeObject(_ objv: Any?, forKey key: String?, conditional: Bool = false) { |
| if let objectRef = _encodeObject(objv, conditional: conditional) { |
| _setObjectInCurrentEncodingContext(objectRef, forKey: key, escape: key != nil) |
| } |
| } |
| |
| open override func encode(_ object: Any?) { |
| _encodeObject(object, forKey: nil) |
| } |
| |
| open override func encodeConditionalObject(_ object: Any?) { |
| _encodeObject(object, forKey: nil, conditional: true) |
| } |
| |
| open override func encode(_ objv: Any?, forKey key: String) { |
| _encodeObject(objv, forKey: key, conditional: false) |
| } |
| |
| open override func encodeConditionalObject(_ objv: Any?, forKey key: String) { |
| _encodeObject(objv, forKey: key, conditional: true) |
| } |
| |
| open override func encodePropertyList(_ aPropertyList: Any) { |
| if !NSPropertyListClasses.contains(where: { $0 == type(of: aPropertyList) }) { |
| fatalError("Cannot encode non-property list type \(type(of: aPropertyList)) as property list") |
| } |
| encode(aPropertyList) |
| } |
| |
| open func encodePropertyList(_ aPropertyList: Any, forKey key: String) { |
| if !NSPropertyListClasses.contains(where: { $0 == type(of: aPropertyList) }) { |
| fatalError("Cannot encode non-property list type \(type(of: aPropertyList)) as property list") |
| } |
| encode(aPropertyList, forKey: key) |
| } |
| |
| open func _encodePropertyList(_ aPropertyList: Any, forKey key: String? = nil) { |
| let _ = _validateStillEncoding() |
| _setObjectInCurrentEncodingContext(aPropertyList, forKey: key) |
| } |
| |
| internal func _encodeValue<T: NSObject>(_ objv: T, forKey key: String? = nil) where T: NSCoding { |
| _encodePropertyList(objv, forKey: key) |
| } |
| |
| private func _encodeValueOfObjCType(_ type: _NSSimpleObjCType, at addr: UnsafeRawPointer) { |
| switch type { |
| case .ID: |
| let objectp = addr.assumingMemoryBound(to: Any.self) |
| encode(objectp.pointee) |
| case .Class: |
| let classp = addr.assumingMemoryBound(to: AnyClass.self) |
| encode(NSStringFromClass(classp.pointee)._bridgeToObjectiveC()) |
| case .Char: |
| let charp = addr.assumingMemoryBound(to: CChar.self) |
| _encodeValue(NSNumber(value: charp.pointee)) |
| case .UChar: |
| let ucharp = addr.assumingMemoryBound(to: UInt8.self) |
| _encodeValue(NSNumber(value: ucharp.pointee)) |
| case .Int, .Long: |
| let intp = addr.assumingMemoryBound(to: Int32.self) |
| _encodeValue(NSNumber(value: intp.pointee)) |
| case .UInt, .ULong: |
| let uintp = addr.assumingMemoryBound(to: UInt32.self) |
| _encodeValue(NSNumber(value: uintp.pointee)) |
| case .LongLong: |
| let longlongp = addr.assumingMemoryBound(to: Int64.self) |
| _encodeValue(NSNumber(value: longlongp.pointee)) |
| case .ULongLong: |
| let ulonglongp = addr.assumingMemoryBound(to: UInt64.self) |
| _encodeValue(NSNumber(value: ulonglongp.pointee)) |
| case .Float: |
| let floatp = addr.assumingMemoryBound(to: Float.self) |
| _encodeValue(NSNumber(value: floatp.pointee)) |
| case .Double: |
| let doublep = addr.assumingMemoryBound(to: Double.self) |
| _encodeValue(NSNumber(value: doublep.pointee)) |
| case .Bool: |
| let boolp = addr.assumingMemoryBound(to: Bool.self) |
| _encodeValue(NSNumber(value: boolp.pointee)) |
| case .CharPtr: |
| let charpp = addr.assumingMemoryBound(to: UnsafePointer<Int8>.self) |
| encode(NSString(utf8String: charpp.pointee)) |
| default: |
| fatalError("NSKeyedArchiver.encodeValueOfObjCType: unknown type encoding ('\(type.rawValue)')") |
| } |
| } |
| |
| open override func encodeValue(ofObjCType typep: UnsafePointer<Int8>, at addr: UnsafeRawPointer) { |
| guard let type = _NSSimpleObjCType(UInt8(typep.pointee)) else { |
| let spec = String(typep.pointee) |
| fatalError("NSKeyedArchiver.encodeValueOfObjCType: unsupported type encoding spec '\(spec)'") |
| } |
| |
| if type == .StructBegin { |
| fatalError("NSKeyedArchiver.encodeValueOfObjCType: this archiver cannot encode structs") |
| } else if type == .ArrayBegin { |
| let scanner = Scanner(string: String(cString: typep)) |
| |
| scanner.scanLocation = 1 // advance past ObJCType |
| |
| var count : Int = 0 |
| guard scanner.scanInteger(&count) && count > 0 else { |
| fatalError("NSKeyedArchiver.encodeValueOfObjCType: array count is missing or zero") |
| } |
| |
| guard let elementType = _NSSimpleObjCType(scanner.scanUpToString(String(_NSSimpleObjCType.ArrayEnd))) else { |
| fatalError("NSKeyedArchiver.encodeValueOfObjCType: array type is missing") |
| } |
| |
| encode(_NSKeyedCoderOldStyleArray(objCType: elementType, count: count, at: addr)) |
| } else { |
| return _encodeValueOfObjCType(type, at: addr) |
| } |
| } |
| |
| open override func encode(_ boolv: Bool, forKey key: String) { |
| _encodeValue(NSNumber(value: boolv), forKey: key) |
| } |
| |
| |
| open override func encode(_ intv: Int32, forKey key: String) { |
| _encodeValue(NSNumber(value: intv), forKey: key) |
| } |
| |
| open override func encode(_ intv: Int64, forKey key: String) { |
| _encodeValue(NSNumber(value: intv), forKey: key) |
| } |
| |
| open override func encode(_ realv: Float, forKey key: String) { |
| _encodeValue(NSNumber(value: realv), forKey: key) |
| } |
| |
| open override func encode(_ realv: Double, forKey key: String) { |
| _encodeValue(NSNumber(value: realv), forKey: key) |
| } |
| |
| open override func encode(_ intv: Int, forKey key: String) { |
| _encodeValue(NSNumber(value: intv), forKey: key) |
| } |
| |
| open override func encode(_ data: Data) { |
| // this encodes as a reference to an NSData object rather than encoding inline |
| encode(data._nsObject) |
| } |
| |
| open override func encodeBytes(_ bytesp: UnsafePointer<UInt8>?, length lenv: Int, forKey key: String) { |
| // this encodes the data inline |
| let data = NSData(bytes: bytesp, length: lenv) |
| _encodeValue(data, forKey: key) |
| } |
| |
| /** |
| Helper API for NSArray and NSDictionary that encodes an array of objects, |
| creating references as it goes |
| */ |
| internal func _encodeArrayOfObjects(_ objects : NSArray, forKey key : String) { |
| var objectRefs = [NSObject]() |
| |
| objectRefs.reserveCapacity(objects.count) |
| |
| for object in objects { |
| let objectRef = _encodeObject(_SwiftValue.store(object))! |
| |
| objectRefs.append(objectRef) |
| } |
| |
| _encodeValue(objectRefs._bridgeToObjectiveC(), forKey: key) |
| } |
| |
| /** |
| Enables secure coding support on this keyed archiver. You do not need to enable |
| secure coding on the archiver to enable secure coding on the unarchiver. Enabling |
| secure coding on the archiver is a way for you to be sure that all classes that |
| are encoded conform with NSSecureCoding (it will throw an exception if a class |
| which does not NSSecureCoding is archived). Note that the getter is on the superclass, |
| NSCoder. See NSCoder for more information about secure coding. |
| */ |
| open override var requiresSecureCoding: Bool { |
| get { |
| return _flags.contains(ArchiverFlags.requiresSecureCoding) |
| } |
| set { |
| if newValue { |
| let _ = _flags.insert(ArchiverFlags.requiresSecureCoding) |
| } else { |
| _flags.remove(ArchiverFlags.requiresSecureCoding) |
| } |
| } |
| } |
| |
| // During encoding, the coder first checks with the coder's |
| // own table, then if there was no mapping there, the class's. |
| open class func classNameForClass(_ cls: AnyClass) -> String? { |
| let clsName = String(reflecting: cls) |
| var mappedClass : String? |
| |
| _classNameMapLock.synchronized { |
| mappedClass = _classNameMap[clsName] |
| } |
| |
| return mappedClass |
| } |
| |
| open func classNameForClass(_ cls: AnyClass) -> String? { |
| let clsName = String(reflecting: cls) |
| return _classNameMap[clsName] |
| } |
| } |
| |
| extension NSKeyedArchiverDelegate { |
| func archiver(_ archiver: NSKeyedArchiver, willEncode object: Any) -> Any? { |
| // Returning the same object is the same as doing nothing |
| return object |
| } |
| |
| func archiver(_ archiver: NSKeyedArchiver, didEncode object: Any?) { } |
| |
| func archiver(_ archiver: NSKeyedArchiver, willReplace object: Any?, with newObject: Any?) { } |
| |
| func archiverWillFinish(_ archiver: NSKeyedArchiver) { } |
| |
| func archiverDidFinish(_ archiver: NSKeyedArchiver) { } |
| |
| } |
| |
| public protocol NSKeyedArchiverDelegate : class { |
| |
| // Informs the delegate that the object is about to be encoded. The delegate |
| // either returns this object or can return a different object to be encoded |
| // instead. The delegate can also fiddle with the coder state. If the delegate |
| // returns nil, nil is encoded. This method is called after the original object |
| // may have replaced itself with replacementObjectForKeyedArchiver:. |
| // This method is not called for an object once a replacement mapping has been |
| // setup for that object (either explicitly, or because the object has previously |
| // been encoded). This is also not called when nil is about to be encoded. |
| // This method is called whether or not the object is being encoded conditionally. |
| func archiver(_ archiver: NSKeyedArchiver, willEncode object: Any) -> Any? |
| |
| // Informs the delegate that the given object has been encoded. The delegate |
| // might restore some state it had fiddled previously, or use this to keep |
| // track of the objects which are encoded. The object may be nil. Not called |
| // for conditional objects until they are really encoded (if ever). |
| func archiver(_ archiver: NSKeyedArchiver, didEncode object: Any?) |
| |
| // Informs the delegate that the newObject is being substituted for the |
| // object. This is also called when the delegate itself is doing/has done |
| // the substitution. The delegate may use this method if it is keeping track |
| // of the encoded or decoded objects. |
| func archiver(_ archiver: NSKeyedArchiver, willReplace object: Any?, withObject newObject: Any?) |
| |
| // Notifies the delegate that encoding is about to finish. |
| func archiverWillFinish(_ archiver: NSKeyedArchiver) |
| |
| // Notifies the delegate that encoding has finished. |
| func archiverDidFinish(_ archiver: NSKeyedArchiver) |
| } |