SwiftUI में `some` कीवर्ड क्या है?




swift5.1 (4)

दूसरा उत्तर नए some कीवर्ड के तकनीकी पहलू को समझाने का अच्छा काम करता है लेकिन यह उत्तर आसानी से समझाने की कोशिश करेगा कि क्यों

मान लीजिए कि मेरे पास एक प्रोटोकॉल है पशु और मैं तुलना करना चाहता हूं कि क्या दो जानवर भाई बहन हैं:

protocol Animal {
    func isSibling(with animal: Self) -> Bool
}

इस तरह से यह तुलना करने के लिए ही समझ में आता है कि यदि दो जानवर एक ही प्रकार के जानवर हैं तो भाई बहन हैं

अब मुझे केवल संदर्भ के लिए एक जानवर का एक उदाहरण बनाने दें

class Dog: Animal {
    func isSibling(with animal: Dog) -> Bool {
        return true // doesn't really matter implementation of this
    }
}

some T बिना रास्ता

अब हम कहते हैं कि मेरे पास एक फ़ंक्शन है जो एक 'परिवार' से एक जानवर लौटाता है।

func animalFromAnimalFamily() -> Animal {
    return myDog // myDog is just some random variable of type `Dog`
}

नोट: यह फ़ंक्शन वास्तव में संकलन नहीं करेगा। ऐसा इसलिए है क्योंकि 'कुछ' सुविधा को जोड़ने से पहले आप प्रोटोकॉल प्रकार को वापस नहीं कर सकते हैं यदि प्रोटोकॉल 'स्व' या जेनरिक का उपयोग करता है । लेकिन हम कहते हैं कि आप कर सकते हैं ... इस प्रकार के नाटक myDog to abstract type Animal, आइए देखते हैं क्या होता है

अब मुद्दा यह है कि क्या मैं ऐसा करने की कोशिश करता हूं:

let animal1: Animal = animalFromAnimalFamily()
let animal2: Animal = animalFromAnimalFamily()

animal1.isSibling(animal2) // error

यह एक त्रुटि फेंक देगा

क्यूं कर? वैसे इसका कारण यह है कि जब आप animal1.isSibling(animal2) बुलाते हैं, तो स्विफ्ट को पता नहीं चलता है कि जानवर कुत्ते, बिल्ली या जो कुछ भी हैं। जहां तक ​​स्विफ्ट को पता है, animal1 और animal2 असंबंधित पशु प्रजातियां हो सकती हैं । चूंकि हम विभिन्न प्रकार के जानवरों की तुलना नहीं कर सकते (ऊपर देखें)। यह त्रुटि करेगा

some T इस समस्या का हल कैसे निकालते हैं

आइए पिछले समारोह को फिर से लिखें:

func animalFromAnimalFamily() -> some Animal {
    return myDog
}
let animal1 = animalFromAnimalFamily()
let animal2 = animalFromAnimalFamily()

animal1.isSibling(animal2)

animal1 और animal2 Animal नहीं हैं, लेकिन वे वर्ग हैं जो पशु को लागू करते हैं

यह आपको अब क्या करने देता है जब आप animal1.isSibling(animal2) , Swift को पता है कि animal1 और animal2 एक ही प्रकार के हैं।

तो जिस तरह से मैं इसके बारे में सोचना पसंद करता हूं:

some T स्विफ्ट को जानते हैं कि some T के किस कार्यान्वयन का उपयोग किया जा रहा है, लेकिन कक्षा का उपयोगकर्ता नहीं करता है।

(सेल्फ-प्रमोशन डिस्क्लेमर) मैंने एक ब्लॉग पोस्ट लिखी है जो इस नई सुविधा पर गहराई से (उदाहरण के रूप में यहां) थोड़ी अधिक जाती है

नए SwiftUI ट्यूटोरियल में निम्नलिखित कोड हैं:

struct ContentView: View {
    var body: some View {
        Text("Hello World")
    }
}

दूसरी पंक्ति में some शब्द, और उनकी साइट पर हाइलाइट किया गया है जैसे कि यह एक कीवर्ड था।

Swift 5.1 में some कीवर्ड के रूप में नहीं दिखाई देता है, और मैं नहीं देखता कि कुछ और शब्द वहाँ क्या कर सकता है, क्योंकि यह वह जगह है जहाँ आमतौर पर टाइप होता है। क्या स्विफ्ट का कोई नया, अघोषित संस्करण है? क्या यह एक प्रकार्य है जिसका उपयोग एक प्रकार से किया जा रहा है, जिसके बारे में मुझे नहीं पता था?

कीवर्ड some क्या करता है?


स्विफ्ट 5.1 ( SE-0244 ) के some कीवर्ड का इस्तेमाल प्रोटोकॉल के साथ रिटर्न टाइप के रूप में किया जाता है।

Xcode 11 रिलीज़ नोट इसे इस तरह प्रस्तुत करते हैं:

फ़ंक्शंस अब उनके कंक्रीट रिटर्न प्रकार को यह घोषित करके छुपा सकते हैं कि सटीक रिटर्न प्रकार को निर्दिष्ट करने के बजाय, यह किस प्रोटोकॉल के अनुरूप है:

func makeACollection() -> some Collection {
    return [1, 2, 3]
}

फ़ंक्शन को कॉल करने वाला कोड प्रोटोकॉल के इंटरफ़ेस का उपयोग कर सकता है, लेकिन अंतर्निहित प्रकार में दृश्यता नहीं है। ( SE-0244 -0244, 40538331)

ऊपर दिए गए उदाहरण में, आपको यह बताने की आवश्यकता नहीं है कि आप एक Array को वापस करने जा रहे हैं। यह आपको एक सामान्य प्रकार भी वापस करने की अनुमति देता है जो सिर्फ Collection अनुरूप है।

अब, some का उपयोग करते समय ध्यान देने योग्य ज्ञात समस्या है :

कुछ प्रोटोकॉल रिटर्न प्रकारों की घोषणा के लिए iOS 13 में स्विफ्ट 5.1 रनटाइम, macOS 10.15, वॉचओएस 6 या टीवीओएस 13 की आवश्यकता होती है, लेकिन स्विफ्ट कंपाइलर इसे लागू नहीं करता है। पिछले ऑपरेटिंग सिस्टम संस्करणों पर कुछ रिटर्न प्रकारों का उपयोग करने वाला ऐप रनिंग के समय क्रैश हो सकता है, या तो स्विफ्ट_getOpaqueTypeMetadata प्रतीकों के गुम होने के कारण, या एक स्ट्रिंग स्ट्रिंग को आरेखित करने में विफलता के कारण जिसमें "Qo_" होता है। (50731151)

वर्कअराउंड : केवल उन द्विपत्रों को तैनात करें जो कुछ वापसी प्रकारों का उपयोग करते हैं IOS 13, macOS 10.15, वॉचओएस 6, और टीवीओएस 13. उन्हें उन कोड से बचें जो पिछले ऑपरेटिंग सिस्टम संस्करणों पर चलना चाहिए।

तो इसका मतलब है कि आप iOS 12 और इससे पहले के some से बचने के लिए उपलब्धता का उपयोग करने वाले हैं:

@available(iOS 13.0, *)
func makeACollection() -> some Collection {
    ...
}

some View SE-0244 द्वारा पेश किए गए एक अपारदर्शी परिणाम प्रकार है और Xcode 11. के साथ स्विफ्ट 5.1 में उपलब्ध है। आप इसे "रिवर्स" जेनेरिक प्लेसहोल्डर के रूप में सोच सकते हैं।

एक सामान्य जेनेरिक प्लेसहोल्डर के विपरीत जो कॉलर द्वारा संतुष्ट है:

protocol P {}
struct S1 : P {}
struct S2 : P {}

func foo<T : P>(_ x: T) {}
foo(S1()) // Caller chooses T == S1.
foo(S2()) // Caller chooses T == S2.

एक अपारदर्शी परिणाम प्रकार एक अंतर्निहित जेनेरिक प्लेसहोल्डर है जो कार्यान्वयन से संतुष्ट है, इसलिए आप इस बारे में सोच सकते हैं:

func bar() -> some P {
  return S1() // Implementation chooses S1 for the opaque result.
}

जैसा दिख रहा है:

func bar() -> <Output : P> Output {
  return S1() // Implementation chooses Output == S1.
}

वास्तव में, इस सुविधा के साथ अंतिम लक्ष्य यह है कि इस तरह के अधिक स्पष्ट रूप में रिवर्स जेनरिक की अनुमति दी जाए, जो आपको बाधाओं को भी जोड़ने देगा, जैसे -> <T : Collection> T where T.Element == Int अधिक जानकारी के लिए इस पोस्ट को देखें

इससे दूर करने वाली मुख्य बात यह है कि some P लौटाने वाला एक फ़ंक्शन वह होता है जो किसी विशिष्ट एकल कंक्रीट प्रकार का मान लौटाता है जो P अनुरूप होता है। फ़ंक्शन के भीतर विभिन्न अनुरूप प्रकारों को वापस करने का प्रयास करने से एक संकलक त्रुटि उत्पन्न होती है:

// error: Function declares an opaque return type, but the return
// statements in its body do not have matching underlying types.
func bar(_ x: Int) -> some P {
  if x > 10 {
    return S1()
  } else {
    return S2()
  }
}

जैसा कि निहित जेनेरिक प्लेसहोल्डर कई प्रकारों से संतुष्ट नहीं हो सकता है।

यह P वापस करने वाले एक फ़ंक्शन के विपरीत है, जिसका उपयोग S1 और S2 दोनों का प्रतिनिधित्व करने के लिए किया जा सकता है, क्योंकि यह एक मनमाने ढंग से P2 अंकन का प्रतिनिधित्व करता है:

func baz(_ x: Int) -> P {
  if x > 10 {
    return S1()
  } else {
    return S2()
  }
}

ठीक है, तो क्या लाभ अपारदर्शी परिणाम प्रकार -> some P पर प्रोटोकॉल रिटर्न प्रकार है -> P ?

1. पीएटी के साथ अपारदर्शी परिणाम प्रकार का उपयोग किया जा सकता है

प्रोटोकॉल की एक प्रमुख वर्तमान सीमा यह है कि PATs (संबद्ध प्रकार के प्रोटोकॉल) को वास्तविक प्रकारों के रूप में उपयोग नहीं किया जा सकता है। यद्यपि यह एक प्रतिबंध है जो संभवतः भाषा के भविष्य के संस्करण में उठा लिया जाएगा, क्योंकि अपारदर्शी परिणाम प्रकार प्रभावी रूप से सिर्फ सामान्य प्लेसहोल्डर हैं, उन्हें आज PAT के साथ उपयोग किया जा सकता है।

इसका मतलब है कि आप इस तरह की चीजें कर सकते हैं:

func giveMeACollection() -> some Collection {
  return [1, 2, 3]
}

let collection = giveMeACollection()
print(collection.count) // 3

2. अपारदर्शी परिणाम प्रकारों की पहचान होती है

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

इसका मतलब है कि आप इस तरह की चीजें कर सकते हैं:

//   foo() -> <Output : Equatable> Output {
func foo() -> some Equatable { 
  return 5 // The opaque result type is inferred to be Int.
}

let x = foo()
let y = foo()
print(x == y) // Legal both x and y have the return type of foo.

यह कानूनी है क्योंकि कंपाइलर जानता है कि x और y दोनों का एक ही ठोस प्रकार है। यह == लिए एक महत्वपूर्ण आवश्यकता है, जहां दोनों प्रकार के पैरामीटर Self

protocol Equatable {
  static func == (lhs: Self, rhs: Self) -> Bool
}

इसका मतलब यह है कि यह दो मूल्यों की अपेक्षा करता है जो दोनों एक ही प्रकार के ठोस अनुरूप प्रकार हैं। भले ही Equatable एक प्रकार के रूप में उपयोग करने योग्य थे, आप एक दूसरे के साथ दो मनमाने ढंग से Equatable अनुरूप मूल्यों की तुलना करने में सक्षम नहीं होंगे, उदाहरण के लिए:

func foo(_ x: Int) -> Equatable { // Assume this is legal.
  if x > 10 {
    return 0
  } else {
    return "hello world"      
  }
}

let x = foo(20)
let y = foo(5)
print(x == y) // Illegal.

जैसा कि संकलक यह साबित नहीं कर सकता है कि दो मनमाने Equatable मानों में एक ही अंतर्निहित ठोस प्रकार है।

इसी तरह से, अगर हमने एक और अपारदर्शी प्रकार की वापसी समारोह पेश किया:

//   foo() -> <Output1 : Equatable> Output1 {
func foo() -> some Equatable { 
  return 5 // The opaque result type is inferred to be Int.
}

//   bar() -> <Output2 : Equatable> Output2 {
func bar() -> some Equatable { 
  return "" // The opaque result type is inferred to be String.
}

let x = foo()
let y = bar()
print(x == y) // Illegal, the return type of foo != return type of bar.

उदाहरण अवैध हो जाता है क्योंकि यद्यपि foo और bar दोनों some Equatable , उनके "रिवर्स" जेनेरिक प्लेसहोल्डर Output1 और Output2 विभिन्न प्रकारों से संतुष्ट हो सकते हैं।

3. अपारदर्शी परिणाम प्रकार सामान्य प्लेसहोल्डर्स के साथ रचना करते हैं

नियमित रूप से प्रोटोकॉल-टाइप किए गए मानों के विपरीत, अपारदर्शी परिणाम प्रकार नियमित जेनेरिक प्लेसहोल्डर्स के साथ अच्छी तरह से रचना करते हैं, उदाहरण के लिए:

protocol P {
  var i: Int { get }
}
struct S : P {
  var i: Int
}

func makeP() -> some P { // Opaque result type inferred to be S.
  return S(i: .random(in: 0 ..< 10))
}

func bar<T : P>(_ x: T, _ y: T) -> T {
  return x.i < y.i ? x : y
}

let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Legal, T is inferred to be the return type of makeP.

यह काम नहीं करता अगर makeP बस P वापस आ गया होता, क्योंकि दो P मूल्यों में अलग-अलग अंतर्निहित ठोस प्रकार हो सकते हैं, उदाहरण के लिए:

struct T : P {
  var i: Int
}

func makeP() -> P {
  if .random() { // 50:50 chance of picking each branch.
    return S(i: 0)
  } else {
    return T(i: 1)
  }
}

let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Illegal.

कंक्रीट प्रकार पर एक अपारदर्शी परिणाम प्रकार का उपयोग क्यों करें?

इस बिंदु पर आप अपने आप को सोच रहे होंगे, क्यों न केवल कोड लिखें:

func makeP() -> S {
  return S(i: 0)
}

ठीक है, एक अपारदर्शी परिणाम प्रकार का उपयोग आपको S द्वारा P द्वारा दिए गए केवल इंटरफ़ेस को उजागर करके टाइप S एक कार्यान्वयन विवरण बनाने की अनुमति देता है, जिससे आपको किसी भी कोड को तोड़ने के बिना कंक्रीट के प्रकार को बदलने का लचीलापन मिलता है जो फ़ंक्शन पर निर्भर करता है।

उदाहरण के लिए, आप प्रतिस्थापित कर सकते हैं:

func makeP() -> some P {
  return S(i: 0)
}

साथ में:

func makeP() -> some P { 
  return T(i: 1)
}

बिना किसी कोड को तोड़े जो makeP() कहता है makeP()

इस सुविधा के बारे में अधिक जानकारी के लिए भाषा गाइड के अपारदर्शी प्रकार अनुभाग और SE-0244 देखें।


"कुछ" का अर्थ है:

" यह नया स्विफ्ट 5.1 कीवर्ड कुछ का मतलब है कि गणना की गई संपत्ति कुछ भी वापस कर सकती है जब कुछ कम से कम व्यू प्रोटोकॉल के अनुरूप हो। जो टेक्स्ट के लिए सही हो। और स्विफ्ट 5.1 के साथ हमें अब रिटर्न कीवर्ड भी नहीं जोड़ना होगा।" किसी फ़ंक्शन की अंतिम पंक्ति या बंद को स्वचालित रूप से वापस कर दिया जाएगा। "





swift5.1