संपत्ति के रैपरों को स्विफ्ट करें

यदि आपने SwiftUI का उपयोग किया है, तो संभवतः आपने @ObservedObject, @EnvironmentObject, @FetchRequest और जैसे कीवर्ड पर ध्यान दिया है। प्रॉपर्टी रैपर्स (इसके बाद "संपत्ति रैपर" के रूप में जाना जाता है) स्विफ्ट 5.1 की एक नई विशेषता है। यह लेख आपको यह समझने में मदद करेगा कि @ से सभी निर्माण कहां से आए, स्विफ्टयूआई और अपनी परियोजनाओं में उनका उपयोग कैसे करें।



इसके द्वारा अनुवादित: एवगेनी ज़वोज़ानस्की, फ़नकॉर्प के डेवलपर।


नोट: जब तक अनुवाद तैयार किया गया था, तब तक मूल लेख के स्रोत कोड का हिस्सा भाषा में परिवर्तन के कारण अपनी प्रासंगिकता खो चुका था, इसलिए कुछ कोड उदाहरणों को जानबूझकर प्रतिस्थापित किया गया था।


SwiftUI घोषणा से कुछ महीने पहले, मार्च 2019 में सबसे पहले Swift मंचों पर संपत्ति रैपर शुरू किए गए थे। अपने मूल प्रस्ताव में, स्विफ्ट कोर टीम के एक सदस्य डगलस ग्रेगोर ने इस निर्माण (तब संपत्ति प्रतिनिधि कहा जाता है) को "उदाहरण के लिए, lazy जैसे भाषा निर्माण द्वारा प्रदान की गई कार्यक्षमता का उपयोगकर्ता-सुलभ सामान्यीकरण" के रूप में वर्णित किया।


यदि किसी संपत्ति को lazy कीवर्ड के साथ घोषित किया जाता है, तो इसका मतलब है कि इसे पहली बार एक्सेस करने पर इसे इनिशियलाइज़ किया जाएगा। उदाहरण के लिए, आस्थगित संपत्ति आरंभीकरण एक निजी संपत्ति का उपयोग करके लागू किया जा सकता है, एक गणना की गई संपत्ति के माध्यम से पहुँचा जा सकता है। लेकिन lazy कीवर्ड का उपयोग करने से यह बहुत आसान हो जाता है।


 struct Structure {    //      lazy    lazy var deferred = …    //            private var _deferred: Type?    var deferred: Type {        get {            if let value = _deferred { return value }            let initialValue = …            _deferred = initialValue            return initialValue        }        set {            _deferred = newValue        }    } } 

SE-0258: संपत्ति आवरण पूरी तरह से संपत्ति रैपर के डिजाइन और कार्यान्वयन की व्याख्या करता है। इसलिए, आधिकारिक प्रलेखन में विवरण को बेहतर बनाने की कोशिश करने के बजाय, कुछ उदाहरणों पर विचार करें, जिन्हें संपत्ति के रैपर का उपयोग करके लागू किया जा सकता है:


  • संपत्ति मूल्यों का प्रतिबंध;
  • गुण बदलने पर मूल्यों का रूपांतरण;
  • समानता के बदलते अर्थ और गुणों की तुलना करना;
  • संपत्ति का उपयोग लॉगिंग।

संपत्ति मान को सीमित करें


SE-0258: प्रॉपर्टी रैपर कई व्यावहारिक उदाहरण प्रदान करता है, जिसमें @Clamping @Copying , @Atomic , @ThreadSpecific , @Box , @UserDefault @Box , @UserDefault@Clamping रैपर पर विचार करें, जो आपको किसी संपत्ति के अधिकतम या न्यूनतम मूल्य को सीमित करने की अनुमति देता है।


 @propertyWrapper struct Clamping<Value: Comparable> {    var value: Value    let range: ClosedRange<Value>    init(initialValue value: Value, _ range: ClosedRange<Value>) {        precondition(range.contains(value))        self.value = value        self.range = range    }    var wrappedValue: Value {        get { value }        set { value = min(max(range.lowerBound, newValue), range.upperBound) }    } } 

@Clamping का उपयोग किया जा सकता है, उदाहरण के लिए, किसी समाधान की अम्लता का अनुकरण करने के लिए, जिसका मान 0 से 14 तक का मान ले सकता है।


 struct Solution {    @Clamping(0...14) var pH: Double = 7.0 } let carbonicAcid = Solution(pH: 4.68) 

पीएच मान (0...14) से परे पीएच मान सेट करने का प्रयास करने से संपत्ति न्यूनतम या अधिकतम अंतराल के निकटतम मूल्य ले जाएगी।


 let superDuperAcid = Solution(pH: -1) superDuperAcid.pH // 0 

संपत्ति रैपर का उपयोग अन्य संपत्ति रैपर को लागू करने के लिए किया जा सकता है। उदाहरण के लिए, @UnitInterval आवरण संपत्ति के मान को अंतराल (0...1) @Clamping(0...1) उपयोग @Clamping(0...1) :


 @propertyWrapper struct UnitInterval<Value: FloatingPoint> {    @Clamping(0...1)    var wrappedValue: Value = .zero    init(initialValue value: Value) {        self.wrappedValue = value    } } 

इसी तरह के विचार


  • @Positive / @NonNegative यह दर्शाता है कि मान एक धनात्मक या ऋणात्मक संख्या हो सकता है।
  • @NonZero इंगित करता है कि संपत्ति का मूल्य 0 नहीं हो सकता है।
  • @Validated या @Whitelisted / @Blacklisted कुछ मानों के लिए एक संपत्ति के मूल्य को प्रतिबंधित करता है।

बदलते हुए गुण जब बदलते हैं


पाठ क्षेत्र मानों को मान्य करना एप्लिकेशन डेवलपर्स के लिए एक निरंतर सिरदर्द है। इस पर नज़र रखने के लिए बहुत सारी चीज़ें हैं: प्लैटिट्यूड से जैसे कि किसी पाठ क्षेत्र के माध्यम से कोड दर्ज करने के लिए दुर्भावनापूर्ण प्रयासों के लिए एन्कोडिंग। रिक्त स्थान को हटाने के लिए एक संपत्ति आवरण का उपयोग करने पर विचार करें जो एक उपयोगकर्ता ने एक पंक्ति की शुरुआत और अंत में दर्ज किया है।


 import Foundation let url = URL(string: " https://habrahabr.ru") // nil let date = ISO8601DateFormatter().date(from: " 2019-06-24") // nil let words = " Hello, world!".components(separatedBy: .whitespaces) words.count // 3 

Foundation trimmingCharacters(in:) प्रदान करता है trimmingCharacters(in:) पद्धति में, जिसके साथ आप किसी पंक्ति के आरंभ और अंत में रिक्त स्थान निकाल सकते हैं। जब भी आपको इनपुट की शुद्धता की गारंटी देने की आवश्यकता हो, आप इस विधि को कॉल कर सकते हैं, लेकिन यह बहुत सुविधाजनक नहीं है। इसके लिए आप प्रॉपर्टी रैपर का इस्तेमाल कर सकते हैं।


 import Foundation @propertyWrapper struct Trimmed {    private(set) var value: String = ""    var wrappedValue: String {        get { return value }        set { value = newValue.trimmingCharacters(in: .whitespacesAndNewlines) }    }    init(initialValue: String) {        self.wrappedValue = initialValue    } } 

 struct Post {    @Trimmed var title: String    @Trimmed var body: String } let quine = Post(title: " Swift Property Wrappers ", body: "…") quine.title // "Swift Property Wrappers" —        quine.title = "   @propertyWrapper " // "@propertyWrapper" 

इसी तरह के विचार


  • @Transformed स्ट्रिंग में ICU रूपांतरण लागू करता है।
  • @Rounded / @Truncated राउंड या स्ट्रिंग मान को छोटा करता है।

समानता और संपत्ति की तुलना के शब्दार्थों को बदलें


स्विफ्ट में, दो तार समान होते हैं यदि वे विहित रूप से समतुल्य हैं , अर्थात्। एक ही अक्षर होते हैं। लेकिन मान लें कि हम चाहते हैं कि स्ट्रिंग गुण समान हों, न कि मामला संवेदनशील, जिसमें वे शामिल हों।


@CaseInsensitive या SubString गुणों के लिए एक आवरण को लागू करता है।


 import Foundation @propertyWrapper struct CaseInsensitive<Value: StringProtocol> {    var wrappedValue: Value } extension CaseInsensitive: Comparable {    private func compare(_ other: CaseInsensitive) -> ComparisonResult {        wrappedValue.caseInsensitiveCompare(other.wrappedValue)    }    static func == (lhs: CaseInsensitive, rhs: CaseInsensitive) -> Bool {        lhs.compare(rhs) == .orderedSame    }    static func < (lhs: CaseInsensitive, rhs: CaseInsensitive) -> Bool {        lhs.compare(rhs) == .orderedAscending    }    static func > (lhs: CaseInsensitive, rhs: CaseInsensitive) -> Bool {        lhs.compare(rhs) == .orderedDescending    } } 

 let hello: String = "hello" let HELLO: String = "HELLO" hello == HELLO // false CaseInsensitive(wrappedValue: hello) == CaseInsensitive(wrappedValue: HELLO) // true 

इसी तरह के विचार


  • @Approximate डबल या फ्लोट के गुणों की एक मोटी तुलना के लिए @Approximate
  • @Ranked उन संपत्तियों के लिए जिनके मूल्य क्रम में हैं (उदाहरण के लिए, ताश खेलने की रैंक)।

प्रॉपर्टी एक्सेस लॉगिंग


@Versioned आपको निर्धारित मानों को इंटरसेप्ट करने और याद रखने की अनुमति देगा कि वे कब सेट किए गए थे।


 import Foundation @propertyWrapper struct Versioned<Value> {    private var value: Value    private(set) var timestampedValues: [(Date, Value)] = []    var wrappedValue: Value {        get { value }        set {            defer { timestampedValues.append((Date(), value)) }            value = newValue        }    }    init(initialValue value: Value) {        self.wrappedValue = value    } } 

ExpenseReport वर्ग ExpenseReport व्यय रिपोर्ट के प्रसंस्करण राज्यों के टाइमस्टैम्प को बचाने की अनुमति देता है।


 class ExpenseReport {    enum State { case submitted, received, approved, denied }    @Versioned var state: State = .submitted } 

लेकिन उपरोक्त उदाहरण संपत्ति रैपर के वर्तमान कार्यान्वयन में एक गंभीर सीमा को प्रदर्शित करता है, जो स्विफ्ट प्रतिबंध से आता है: गुण अपवाद नहीं फेंक सकते। यदि हम मान को बदलने से रोकने के लिए @Versioned पर रोक fatalError() के लिए।


 class ExpenseReport {    @Versioned var state: State = .submitted {        willSet {            if newValue == .approved,                $state.timestampedValues.map { $0.1 }.contains(.denied)            {                fatalError("")            }        }    } } var tripExpenses = ExpenseReport() tripExpenses.state = .denied tripExpenses.state = .approved // Fatal error: «»   . 

इसी तरह के विचार


  • @Audited संपत्ति का उपयोग @Audited करने के लिए तैयार है।
  • @UserDefault में डेटा को पढ़ने और सहेजने के लिए तंत्र को @UserDefault करने के लिए UserDefaults

प्रतिबंध


गुण अपवाद नहीं फेंक सकते


जैसा कि पहले ही उल्लेख किया गया है, संपत्ति रैपर अमान्य मानों को संसाधित करने के लिए केवल कुछ तरीकों का उपयोग कर सकते हैं:


  • उन्हें अनदेखा करें;
  • fatalError () का उपयोग करके एप्लिकेशन को समाप्त करें।

लिपटे हुए गुण `टाइपेलियस` विशेषता के साथ चिह्नित नहीं किए जा सकते


ऊपर @UnitInterval उदाहरण, जिनकी संपत्ति अंतराल (0...1) द्वारा सीमित है, के रूप में घोषित नहीं किया जा सकता है


 typealias UnitInterval = Clamping(0...1) 

कई संपत्ति रैपर की एक संरचना के उपयोग पर प्रतिबंध


संपत्ति रैपर की रचना करना एक सराहनीय कार्य नहीं है: घोषणा के आदेश से व्यवहार प्रभावित होगा। एक उदाहरण पर विचार करें जिसमें स्लग संपत्ति, जो एक ब्लॉग पोस्ट का यूआरएल है, सामान्यीकृत है। इस स्थिति में, सामान्यीकरण का परिणाम इस आधार पर अलग-अलग होगा कि रिक्त स्थान को हटाने के पहले या बाद में रिक्त स्थान को बदल दिया जाता है। इसलिए, फिलहाल, कई संपत्ति रैपरों की एक संरचना समर्थित नहीं है।


 @propertyWrapper struct Dasherized {    private(set) var value: String = ""    var wrappedValue: String {        get { value }        set { value = newValue.replacingOccurrences(of: " ", with: "-") }    }    init(initialValue: String) {        self.wrappedValue = initialValue    } } struct Post {    …    @Dasherized @Trimmed var slug: String // error: multiple property wrappers are not supported } 

हालाँकि, नेस्टेड प्रॉपर्टी रैपर का उपयोग करके इस सीमा को दरकिनार किया जा सकता है।


 @propertyWrapper struct TrimmedAndDasherized {    @Dasherized    private(set) var value: String = ""    var wrappedValue: String {        get { value }        set { value = newValue.trimmingCharacters(in: .whitespacesAndNewlines) }    }    init(initialValue: String) {        self.wrappedValue = initialValue    } } struct Post {    …    @TrimmedAndDasherized var slug: String } 

अन्य संपत्ति आवरण प्रतिबंध


  • प्रोटोकॉल के अंदर इस्तेमाल नहीं किया जा सकता।
  • एक रैपर संपत्ति उदाहरण enum में घोषित नहीं किया जा सकता है।
  • एक वर्ग के अंदर घोषित एक लिपटे हुए संपत्ति को किसी अन्य संपत्ति द्वारा ओवरराइड नहीं किया जा सकता है।
  • एक लिपटे संपत्ति lazy , @NSCopying , @NSManaged , weak या @NSCopying नहीं हो सकता।
  • एक लपेटी गई संपत्ति इसकी परिभाषा के भीतर केवल एक होनी चाहिए (यानी, @Lazy var (x, y) = /* ... */ )।
  • एक लिपटे संपत्ति में getter और setter परिभाषित नहीं हो सकता है।
  • wrappedValue संपत्ति के प्रकार और wrappedValue में wrappedValue चर init(wrappedValue:) पास संपत्ति के आवरण प्रकार के समान पहुंच स्तर होना चाहिए।
  • projectedValue प्रकार की संपत्ति का रैपर प्रकार के समान पहुंच स्तर होना चाहिए।
  • init() में संपत्ति आवरण प्रकार के समान पहुंच स्तर होना चाहिए।

आइए संक्षेप में बताते हैं। स्विफ्ट में संपत्ति रैपर पुस्तकालय लेखकों को उच्च-स्तरीय व्यवहार तक पहुंच प्रदान करते हैं जो पहले भाषा के कार्यों के लिए आरक्षित थे। पठनीयता में सुधार और कोड जटिलता को कम करने की उनकी क्षमता बहुत बड़ी है, और हमने केवल इस उपकरण की क्षमताओं की सतही जांच की।


क्या आप अपनी परियोजनाओं में संपत्ति के रैपर का उपयोग करते हैं? टिप्पणियों में लिखें!

Source: https://habr.com/ru/post/hi485008/


All Articles