नोट: कोड 5 स्विफ्ट के लिए अद्यतन किया गया है (Xcode 10.2) के । (स्विफ्ट 3 और स्विफ्ट 4.2 वर्जन को एडिट हिस्ट्री में पाया जा सकता है।) इसके अलावा संभवतः अनलगनेटेड डेटा को अब सही तरीके से हैंडल किया जा सकता है।
कैसे बनाएं Data
एक मूल्य से
Swift 4.2 के अनुसार, डेटा को केवल एक मान से बनाया जा सकता है
let value = 42.13
let data = withUnsafeBytes(of: value) { Data($0) }
print(data as NSData) // <713d0ad7 a3104540>
स्पष्टीकरण:
withUnsafeBytes(of: value)
मूल्य के कच्चे बाइट्स को कवर करने वाले बफर पॉइंटर के साथ क्लोजर को आमंत्रित करता है।
- एक कच्चा बफ़र पॉइंटर बाइट्स का एक क्रम है, इसलिए
Data($0)
इसका उपयोग डेटा बनाने के लिए किया जा सकता है।
कैसे से एक मान प्राप्त करने के लिए Data
स्विफ्ट 5 के रूप में, बाइट्स के लिए "अनपेड" के साथ बंद करने withUnsafeBytes(_:)
का Data
आह्वान किया UnsafeMutableRawBufferPointer
। load(fromByteOffset:as:)
विधि स्मृति से मूल्य में लिखा है:
let data = Data([0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40])
let value = data.withUnsafeBytes {
$0.load(as: Double.self)
}
print(value) // 42.13
इस दृष्टिकोण के साथ एक समस्या है: यह आवश्यक है कि स्मृति प्रकार के लिए संरेखित संपत्ति है (यहां: 8-बाइट पते के लिए संरेखित)। लेकिन इसकी गारंटी नहीं है, उदाहरण के लिए यदि डेटा दूसरे के एक स्लाइस के रूप में प्राप्त किया गया थाData
मूल्य के ।
इसलिए यह मूल्य के लिए बाइट्स की प्रतिलिपि बनाना अधिक सुरक्षित है :
let data = Data([0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40])
var value = 0.0
let bytesCopied = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
assert(bytesCopied == MemoryLayout.size(ofValue: value))
print(value) // 42.13
स्पष्टीकरण:
का वापसी मूल्य copyBytes()
कॉपी किए गए बाइट्स की संख्या है। यह गंतव्य बफ़र के आकार के बराबर है, या कम है यदि डेटा में पर्याप्त बाइट्स नहीं हैं।
सामान्य समाधान # 1
उपरोक्त रूपांतरण अब आसानी से सामान्य तरीकों के रूप में लागू किए जा सकते हैं struct Data
:
extension Data {
init<T>(from value: T) {
self = Swift.withUnsafeBytes(of: value) { Data($0) }
}
func to<T>(type: T.Type) -> T? where T: ExpressibleByIntegerLiteral {
var value: T = 0
guard count >= MemoryLayout.size(ofValue: value) else { return nil }
_ = Swift.withUnsafeMutableBytes(of: &value, { copyBytes(to: $0)} )
return value
}
}
बाधा T: ExpressibleByIntegerLiteral
यहां इसलिए जोड़ी जाती है ताकि हम आसानी से "शून्य" के मूल्य को शुरू कर सकें - यह वास्तव में प्रतिबंध नहीं है क्योंकि इस पद्धति का उपयोग "ट्रिलिंग" (पूर्णांक और फ्लोटिंग पॉइंट) प्रकारों के साथ किया जा सकता है, वैसे भी नीचे देखें।
उदाहरण:
let value = 42.13 // implicit Double
let data = Data(from: value)
print(data as NSData) // <713d0ad7 a3104540>
if let roundtrip = data.to(type: Double.self) {
print(roundtrip) // 42.13
} else {
print("not enough data")
}
इसी प्रकार, आप सरणियों को Data
वापस और पीछे बदल सकते हैं:
extension Data {
init<T>(fromArray values: [T]) {
self = values.withUnsafeBytes { Data($0) }
}
func toArray<T>(type: T.Type) -> [T] where T: ExpressibleByIntegerLiteral {
var array = Array<T>(repeating: 0, count: self.count/MemoryLayout<T>.stride)
_ = array.withUnsafeMutableBytes { copyBytes(to: $0) }
return array
}
}
उदाहरण:
let value: [Int16] = [1, Int16.max, Int16.min]
let data = Data(fromArray: value)
print(data as NSData) // <0100ff7f 0080>
let roundtrip = data.toArray(type: Int16.self)
print(roundtrip) // [1, 32767, -32768]
सामान्य समाधान # 2
उपरोक्त दृष्टिकोण में एक नुकसान है: यह वास्तव में केवल "तुच्छ" प्रकार के साथ काम करता है जैसे पूर्णांक और फ्लोटिंग बिंदु प्रकार। "कॉम्प्लेक्स" प्रकार Array
औरString
अंतर्निहित मेमोरी के लिए (छिपे हुए) संकेत हैं और केवल संरचना की प्रतिलिपि बनाकर इसे पास नहीं किया जा सकता है। यह उन संदर्भ प्रकारों के साथ भी काम नहीं करेगा जो वास्तविक वस्तु भंडारण के लिए संकेत हैं।
तो उस समस्या को हल कर सकते हैं, एक कर सकते हैं
एक प्रोटोकॉल को परिभाषित करें जो परिवर्तित करने के लिए तरीकों को परिभाषित करता है Data
और पीछे:
protocol DataConvertible {
init?(data: Data)
var data: Data { get }
}
प्रोटोकॉल एक्सटेंशन में डिफ़ॉल्ट विधियों के रूप में रूपांतरणों को लागू करें:
extension DataConvertible where Self: ExpressibleByIntegerLiteral{
init?(data: Data) {
var value: Self = 0
guard data.count == MemoryLayout.size(ofValue: value) else { return nil }
_ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
self = value
}
var data: Data {
return withUnsafeBytes(of: self) { Data($0) }
}
}
मैंने यहाँ एक फाल्ट इनिशियलाइज़र चुना है जो जाँचता है कि बाइट्स की संख्या टाइप के आकार से मेल खाती है।
और अंत में सभी प्रकारों के लिए अनुरूपता की घोषणा करें, जिन्हें सुरक्षित रूप से Data
और पीछे परिवर्तित किया जा सके :
extension Int : DataConvertible { }
extension Float : DataConvertible { }
extension Double : DataConvertible { }
// add more types here ...
यह रूपांतरण को और अधिक सुरुचिपूर्ण बनाता है:
let value = 42.13
let data = value.data
print(data as NSData) // <713d0ad7 a3104540>
if let roundtrip = Double(data: data) {
print(roundtrip) // 42.13
}
दूसरे दृष्टिकोण का लाभ यह है कि आप अनजाने में रूपांतरण नहीं कर सकते हैं। नुकसान यह है कि आपको सभी "सुरक्षित" प्रकारों को स्पष्ट रूप से सूचीबद्ध करना होगा।
आप अन्य प्रकारों के लिए भी प्रोटोकॉल लागू कर सकते हैं, जिनके लिए गैर-तुच्छ रूपांतरण की आवश्यकता होती है, जैसे:
extension String: DataConvertible {
init?(data: Data) {
self.init(data: data, encoding: .utf8)
}
var data: Data {
// Note: a conversion to UTF-8 cannot fail.
return Data(self.utf8)
}
}
या जो कुछ भी आवश्यक है उसे करने के लिए रूपांतरण विधियों को अपने स्वयं के प्रकारों में लागू करें ताकि मूल्य को क्रमबद्ध और निष्क्रिय कर सकें।
बाइट क्रम
उपरोक्त विधियों में कोई बाइट ऑर्डर रूपांतरण नहीं किया जाता है, डेटा हमेशा होस्ट बाइट क्रम में होता है। एक प्लेटफ़ॉर्म स्वतंत्र प्रतिनिधित्व के लिए (जैसे "बड़ा एंडियन" उर्फ "नेटवर्क" बाइट ऑर्डर), इसी पूर्णांक गुण सम्मान का उपयोग करें। initializers। उदाहरण के लिए:
let value = 1000
let data = value.bigEndian.data
print(data as NSData) // <00000000 000003e8>
if let roundtrip = Int(data: data) {
print(Int(bigEndian: roundtrip)) // 1000
}
बेशक यह रूपांतरण सामान्य रूप से सामान्य रूपांतरण विधि में भी किया जा सकता है।