समानता के लिए आप कार्यों और परीक्षणों का परीक्षण कैसे करते हैं?


88

पुस्तक कहती है कि "फ़ंक्शन और क्लोज़र संदर्भ प्रकार हैं"। तो, अगर संदर्भ समान हैं तो आपको कैसे पता चलेगा? == और === काम नहीं करते।

func a() { }
let å = a
let b = å === å // Could not find an overload for === that accepts the supplied arguments

5
जहाँ तक मैं बता सकता हूँ, आप भी MyClass.self
मेटाक्लेसेस

पहचान के लिए दो बंदों की तुलना करना आवश्यक नहीं होना चाहिए। क्या आप एक उदाहरण दे सकते हैं कि आप यह कहां करेंगे? वैकल्पिक समाधान हो सकता है।
बिल

1
मल्टिकास्ट क्लोजर, एक ला सी #। वे स्विफ्ट में जरूरी बदसूरत हैं, क्योंकि आप टी (टी, यू) "ऑपरेटर" को अधिभार नहीं दे सकते हैं, लेकिन हम अभी भी उन्हें खुद बना सकते हैं। संदर्भ द्वारा एक मंगलाचरण सूची से क्लोजर निकालने में सक्षम होने के बिना, हालांकि, हमें अपना स्वयं का आवरण वर्ग बनाने की आवश्यकता है। यह एक खींचें है, और आवश्यक नहीं होना चाहिए।
जेसी

2
महान प्रश्न, लेकिन पूरी तरह से अलग बात: åसंदर्भ पर एक विशेषांक का आपका उपयोग aवास्तव में दिलचस्प है। क्या कोई सम्मेलन है जो आप यहां खोज रहे हैं? (मुझे नहीं पता कि मैं वास्तव में इसे पसंद करता हूं या नहीं; लेकिन ऐसा लगता है कि यह बहुत शक्तिशाली हो सकता है, विशेष रूप से शुद्ध कार्यात्मक प्रोग्रामिंग में।)
रोब नेपियर

2
@ क्या मैं एक ऐरे में क्लोजर स्टोर कर रहा हूं और उन्हें खोजने और निकालने के लिए indexOf ({$ 0 == क्लोजर) का उपयोग नहीं कर सकता। अब मुझे ऑप्टिमाइज़ेशन के कारण अपने कोड को रिस्ट्रक्चर करना है, जो मुझे खराब भाषा डिज़ाइन लगता है।
जैक मोरिस

जवाबों:


72

क्रिस लॅटनर ने डेवलपर मंचों पर लिखा:

यह एक ऐसी विशेषता है जिसे हम जानबूझकर समर्थन नहीं करना चाहते हैं। अनुकूलन के आधार पर विफल होने या बदलने के लिए कई प्रकार की चीजें हैं जो कार्यों की सूचक समानता (स्विफ्ट प्रकार सिस्टम अर्थ में, जिसमें कई प्रकार के क्लोजर शामिल हैं) का कारण होगा। यदि "===" फ़ंक्शन पर परिभाषित किया गया था, तो संकलक को समान विधि निकायों, शेयर थ्रक्स को मर्ज करने और क्लोज़र में कुछ कैप्चर ऑप्टिमाइज़ेशन करने की अनुमति नहीं दी जाएगी। इसके अलावा, कुछ जेनरिक संदर्भों में इस प्रकार की समानता बेहद आश्चर्यजनक होगी, जहां आप एक प्रकार के फ़ंक्शन के प्रकारों की अपेक्षा किसी फ़ंक्शन के वास्तविक हस्ताक्षर को समायोजित करने वाले रीबस्ट्रेक्शन थ्रक्स प्राप्त कर सकते हैं।

https://devforums.apple.com/message/1035180#1035180

इसका मतलब यह है कि आपको समानता के लिए क्लोजर की तुलना करने की कोशिश भी नहीं करनी चाहिए क्योंकि अनुकूलन परिणाम को प्रभावित कर सकते हैं।


18
यह मेरे लिए थोड़ा सा है, जो विनाशकारी था क्योंकि मैं एक ऐरे में क्लोजर स्टोर कर रहा था और अब उन्हें indexOf ({$ 0 == क्लोजर}) से हटा नहीं सकता, इसलिए मुझे रिफ्लेक्टर करना होगा। IMHO ऑप्टिमाइज़ेशन को भाषा डिज़ाइन को प्रभावित नहीं करना चाहिए। इसलिए मैट के उत्तर में अब पदावनत किए गए @objc_block की तरह त्वरित सुधार के बिना, मैं तर्क दूंगा कि स्विफ्ट इस समय ठीक से स्टोर नहीं कर सकता है और न ही पुनर्प्राप्त कर सकता है। इसलिए मुझे नहीं लगता कि कॉलबैक हैवी कोड में स्विफ्ट के उपयोग की वकालत करना उचित है। वेब डेवलपमेंट में किस तरह का सामना करना पड़ा। कौन-सी वजह थी जिसकी वजह से हम पहली बार स्विफ्ट में आए ...
Zack Morris

4
@ZackMorris स्टोर को किसी प्रकार के पहचानकर्ता के साथ बंद कर देता है ताकि आप इसे बाद में हटा सकें। यदि आप संदर्भ प्रकारों का उपयोग कर रहे हैं, तो आप केवल ऑब्जेक्ट के लिए एक संदर्भ स्टोर कर सकते हैं अन्यथा आप अपने स्वयं के पहचानकर्ता सिस्टम के साथ आ सकते हैं। आप एक प्रकार भी डिज़ाइन कर सकते हैं जिसमें एक क्लोजर होता है और एक अद्वितीय पहचानकर्ता होता है जिसे आप सादे बंद होने के बजाय उपयोग कर सकते हैं।
ड्रग'

5
@drewag हाँ, वर्कअराउंड हैं, लेकिन ज़ैक सही है। यह वास्तव में लंगड़ा है। मैं समझता हूं कि अनुकूलन करने की इच्छा है, लेकिन अगर कोड में कहीं है कि डेवलपर को कुछ क्लोजर की तुलना करने की आवश्यकता है, तो बस कंपाइलर को उन विशेष खंडों का अनुकूलन नहीं करना है। या कंपाइलर के कुछ प्रकार के अतिरिक्त कार्य करते हैं जो इसे समानता के हस्ताक्षर बनाने में सक्षम बनाता है जो फ्रैकिंग अनुकूलन के साथ नहीं टूटता है। यह Apple है जिसके बारे में हम यहां बात कर रहे हैं ... यदि वे एक Xeon को iMac में फिट कर सकते हैं तो वे निश्चित रूप से क्लोजर को तुलनीय बना सकते हैं। मुझे एक विराम दें!
कॉममाटॉस्ट

10

मैंने बहुत खोजा। ऐसा लगता है कि फ़ंक्शन पॉइंटर तुलना का कोई तरीका नहीं है। मुझे जो सबसे अच्छा समाधान मिला है वह है फंक्शन को एनकैप्सुलेट करना या हैशेबल ऑब्जेक्ट में बंद करना। पसंद:

var handler:Handler = Handler(callback: { (message:String) in
            //handler body
}))

2
यह, अब तक का सबसे अच्छा तरीका है। इसे बंद करने और बंद करने के लिए बंद करने के लिए बेकार है, लेकिन यह nondeterministic, असमर्थित नाजुकता से बेहतर है।

8

सबसे सरल तरीका ब्लॉक प्रकार के रूप में नामित किया गया है @objc_block, और अब आप इसे किसी भी ओबजेक्ट के लिए डाल सकते हैं जो इसके साथ तुलनीय है ===। उदाहरण:

    typealias Ftype = @objc_block (s:String) -> ()

    let f : Ftype = {
        ss in
        println(ss)
    }
    let ff : Ftype = {
        sss in
        println(sss)
    }
    let obj1 = unsafeBitCast(f, AnyObject.self)
    let obj2 = unsafeBitCast(ff, AnyObject.self)
    let obj3 = unsafeBitCast(f, AnyObject.self)

    println(obj1 === obj2) // false
    println(obj1 === obj3) // true

अरे, मैं कोशिश कर रहा हूँ कि अगर असुरक्षितबेस्टस्ट (श्रोता, AnyObject.self) === असुरक्षितबेस्ट (च, एनीओबजेक्ट.सेल्फ) लेकिन घातक त्रुटि प्राप्त करें: विभिन्न आकारों के प्रकारों के बीच असुरक्षित नहीं कर सकते। विचार एक ईवेंट आधारित प्रणाली बनाने का है लेकिन रिमूवइनवेंटिस्टनर विधि फ़ंक्शन पॉइंटर्स की जांच करने में सक्षम होना चाहिए।
फ्रीजिंग_

2
स्विफ्ट 2.x पर @objc_block के बजाय @conference (ब्लॉक) का उपयोग करें। बहुत बढ़िया जवाब!
गैब्रियल.मैसाना

6

मैं भी जवाब के लिए देख रहा हूँ। और मैं इसे पिछले पर पाया है।

आपको जो आवश्यक है वह वास्तविक फ़ंक्शन पॉइंटर है और इसका संदर्भ फ़ंक्शन ऑब्जेक्ट में छिपा हुआ है।

func peekFunc<A,R>(f:A->R)->(fp:Int, ctx:Int) {
    typealias IntInt = (Int, Int)
    let (hi, lo) = unsafeBitCast(f, IntInt.self)
    let offset = sizeof(Int) == 8 ? 16 : 12
    let ptr  = UnsafePointer<Int>(lo+offset)
    return (ptr.memory, ptr.successor().memory)
}
@infix func === <A,R>(lhs:A->R,rhs:A->R)->Bool {
    let (tl, tr) = (peekFunc(lhs), peekFunc(rhs))
    return tl.0 == tr.0 && tl.1 == tr.1
}

और यहाँ डेमो है:

// simple functions
func genericId<T>(t:T)->T { return t }
func incr(i:Int)->Int { return i + 1 }
var f:Int->Int = genericId
var g = f;      println("(f === g) == \(f === g)")
f = genericId;  println("(f === g) == \(f === g)")
f = g;          println("(f === g) == \(f === g)")
// closures
func mkcounter()->()->Int {
    var count = 0;
    return { count++ }
}
var c0 = mkcounter()
var c1 = mkcounter()
var c2 = c0
println("peekFunc(c0) == \(peekFunc(c0))")
println("peekFunc(c1) == \(peekFunc(c1))")
println("peekFunc(c2) == \(peekFunc(c2))")
println("(c0() == c1()) == \(c0() == c1())") // true : both are called once
println("(c0() == c2()) == \(c0() == c2())") // false: because c0() means c2()
println("(c0 === c1) == \(c0 === c1)")
println("(c0 === c2) == \(c0 === c2)")

क्यों और कैसे काम करता है, यह जानने के लिए नीचे दिए गए URL देखें:

जैसा कि आप देखते हैं कि यह केवल पहचान की जांच करने में सक्षम है (दूसरा परीक्षण पैदावार false)। लेकिन यह काफी अच्छा होना चाहिए।


5
यह विधि संकलक अनुकूलन के साथ विश्वसनीय नहीं होगी devforums.apple.com/message/1035180#1035180
drewag

8
यह एक अपरिभाषित कार्यान्वयन विवरण पर आधारित हैक है। तब इसका उपयोग करने का अर्थ है कि आपका कार्यक्रम एक अपरिभाषित परिणाम देगा।
नौ

8
ध्यान दें कि यह अनिर्दिष्ट सामान और अघोषित कार्यान्वयन विवरणों पर निर्भर करता है, जो भविष्य में आपके ऐप को बदल सकते हैं। उत्पादन कोड में उपयोग करने के लिए अनुशंसित नहीं है।
क्रिस्टिक

यह "तिपतिया घास" है, लेकिन पूरी तरह से अस्थिर है। मुझे नहीं पता कि यह इनाम क्यों दिया गया। जानबूझकर कार्य करने के लिए भाषा में समानता नहीं है, संकलक को मुक्त करने के सटीक उद्देश्य के लिए फ़ंक्शन अनुकूलन को तोड़ने के लिए स्वतंत्र रूप से अनुकूलन क्षमता प्राप्त करने के लिए।
अलेक्जेंडर - मोनिका

... और यह वही दृष्टिकोण है जिसके बारे में क्रिस लैटनर (शीर्ष उत्तर देखें) के खिलाफ वकालत करते हैं।
पिपासा

4

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

typealias SwfBlock = () -> ()
typealias ObjBlock = @convention(block) () -> ()

func testSwfBlock(a: SwfBlock, _ b: SwfBlock) -> String {
    let objA = unsafeBitCast(a as ObjBlock, AnyObject.self)
    let objB = unsafeBitCast(b as ObjBlock, AnyObject.self)
    return "a is ObjBlock: \(a is ObjBlock), b is ObjBlock: \(b is ObjBlock), objA === objB: \(objA === objB)"
}

func testObjBlock(a: ObjBlock, _ b: ObjBlock) -> String {
    let objA = unsafeBitCast(a, AnyObject.self)
    let objB = unsafeBitCast(b, AnyObject.self)
    return "a is ObjBlock: \(a is ObjBlock), b is ObjBlock: \(b is ObjBlock), objA === objB: \(objA === objB)"
}

func testAnyBlock(a: Any?, _ b: Any?) -> String {
    if !(a is ObjBlock) || !(b is ObjBlock) {
        return "a nor b are ObjBlock, they are not equal"
    }
    let objA = unsafeBitCast(a as! ObjBlock, AnyObject.self)
    let objB = unsafeBitCast(b as! ObjBlock, AnyObject.self)
    return "a is ObjBlock: \(a is ObjBlock), b is ObjBlock: \(b is ObjBlock), objA === objB: \(objA === objB)"
}

class Foo
{
    lazy var swfBlock: ObjBlock = self.swf
    func swf() { print("swf") }
    @objc func obj() { print("obj") }
}

let swfBlock: SwfBlock = { print("swf") }
let objBlock: ObjBlock = { print("obj") }
let foo: Foo = Foo()

print(testSwfBlock(swfBlock, swfBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testSwfBlock(objBlock, objBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false

print(testObjBlock(swfBlock, swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testObjBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true

print(testAnyBlock(swfBlock, swfBlock)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true

print(testObjBlock(foo.swf, foo.swf)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testSwfBlock(foo.obj, foo.obj)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testAnyBlock(foo.swf, foo.swf)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(foo.swfBlock, foo.swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true

दिलचस्प हिस्सा यह है कि स्वतंत्र रूप से स्विफब्लॉक को ओबजब्लॉक में कैसे डाला जाता है, फिर भी वास्तव में दो डाले गए स्फ़ब्लॉक ब्लॉक हमेशा अलग मूल्य होंगे, जबकि ओब्ब्लब्लॉक नहीं होगा। जब हम ओब्जब्लॉक को स्वफ़ब्लॉक में डालते हैं, तो उनके साथ भी यही होता है, वे दो अलग-अलग मूल्य बन जाते हैं। इसलिए, संदर्भ को संरक्षित करने के लिए, इस तरह की कास्टिंग से बचा जाना चाहिए।

मैं अभी भी इस पूरे विषय को समझ रहा हूं, लेकिन एक चीज जो मैंने छोड़ी है वह है @convention(block)कक्षा / संरचना विधियों पर उपयोग करने की क्षमता , इसलिए मैंने एक फीचर अनुरोध दायर किया जिसमें मतदान की आवश्यकता है या यह समझाने की आवश्यकता है कि यह एक बुरा विचार क्यों है। मुझे यह भी समझ में आता है कि यह दृष्टिकोण सभी के साथ खराब हो सकता है, यदि हां, तो क्या कोई समझा सकता है कि क्यों?


1
मुझे नहीं लगता कि आप क्रिस लैटनर के तर्क को समझते हैं कि ऐसा क्यों नहीं किया गया (और ऐसा नहीं होना चाहिए) का समर्थन किया गया। "मुझे यह भी समझ में आ रहा है कि यह दृष्टिकोण सभी के साथ बुरा हो सकता है, यदि हां, तो क्या कोई समझा सकता है कि क्यों?" क्योंकि एक अनुकूलित बिल्ड में, कंपाइलर कोड को कई तरीकों से जोड़ने के लिए स्वतंत्र है, जो फ़ंक्शन समानता के विचार को तोड़ते हैं। एक मूल उदाहरण के लिए, यदि एक फ़ंक्शन का शरीर उसी तरह से शुरू करता है जैसे कोई अन्य फ़ंक्शन करता है, तो कंपाइलर को मशीन कोड में दो को ओवरलैप करने की संभावना है, केवल अलग-अलग निकास बिंदुओं को रखते हुए। यह दोहराव कम करता है
अलेक्जेंडर - मोनिका

1
मूल रूप से, क्लोजर अनाम कक्षाओं की वस्तुओं को शुरू करने के तरीके हैं (जैसे जावा में, लेकिन यह अधिक स्पष्ट है)। ये क्लोजर ऑब्जेक्ट्स आबंटित किए गए हैं, और क्लोजर द्वारा कैप्चर किए गए डेटा को स्टोर करते हैं, जो क्लोजर के फ़ंक्शन के लिए निहित पैरामीटर की तरह कार्य करते हैं। क्लोजर ऑब्जेक्ट एक फ़ंक्शन के लिए एक संदर्भ रखता है जो स्पष्ट (कवक args के माध्यम से) और अंतर्निहित (कैप्चर क्लोजर संदर्भ के माध्यम से) args पर संचालित होता है। हालांकि फ़ंक्शन बॉडी को एक एकल बिंदु के रूप में साझा किया जा सकता है, क्लोजर ऑब्जेक्ट का पॉइंटर नहीं हो सकता है, क्योंकि संलग्न मानों के सेट के प्रति एक क्लोजर ऑब्जेक्ट है।
अलेक्जेंडर - मोनिका

1
इसलिए जब आपके पास होता है Struct S { func f(_: Int) -> Bool }, तो आपके पास वास्तव में प्रकार का एक फ़ंक्शन S.fहोता है जो टाइप होता है (S) -> (Int) -> Bool। यह फ़ंक्शन साझा किया जा सकता है। यह पूरी तरह से इसके स्पष्ट मापदंडों द्वारा परिचालित है। जब आप इसे एक उदाहरण विधि के रूप में उपयोग करते हैं (या तो selfकिसी ऑब्जेक्ट, उदाहरण के लिए S().f, या इसे स्पष्ट रूप से बाइंड करके, जैसे कि पैरामीटर को कॉल करके पैरामीटर को बाध्य करते हैं S.f(S())), तो आप एक नया क्लोजर ऑब्जेक्ट बनाते हैं। यह ऑब्जेक्ट एक पॉइंटर को स्टोर करता है S.f(जिसे साझा किया जा सकता है) , but also to your instance (स्व , the एस () `)।
अलेक्जेंडर - मोनिका

1
यह क्लोजर ऑब्जेक्ट अद्वितीय उदाहरण के अनुसार होना चाहिए S। यदि क्लोजर पॉइंटर समानता संभव थी, तो आपको यह जानकर आश्चर्य होगा कि s1.fयह वैसा ही पॉइंटर नहीं है s2.f(क्योंकि एक क्लोजर ऑब्जेक्ट है जो संदर्भ देता है s1और fदूसरा एक क्लोजर ऑब्जेक्ट है जो संदर्भ देता है s2और f)।
अलेक्जेंडर - मोनिका

यह शानदार है, धन्यवाद! हाँ, अब तक मेरे पास क्या चल रहा है, इसकी एक तस्वीर थी और यह सब कुछ एक परिप्रेक्ष्य में रखता है! 👍
इयान Bytchek

4

यहाँ एक संभव समाधान है (वैचारिक रूप से 'टंकाय' उत्तर के समान)। बिंदु एक वर्ग को परिभाषित करने के लिए है जो कुछ कार्यक्षमता को लपेटता है (जैसे कमांड):

स्विफ्ट:

typealias Callback = (Any...)->Void
class Command {
    init(_ fn: @escaping Callback) {
        self.fn_ = fn
    }

    var exec : (_ args: Any...)->Void {
        get {
            return fn_
        }
    }
    var fn_ :Callback
}

let cmd1 = Command { _ in print("hello")}
let cmd2 = cmd1
let cmd3 = Command { (_ args: Any...) in
    print(args.count)
}

cmd1.exec()
cmd2.exec()
cmd3.exec(1, 2, "str")

cmd1 === cmd2 // true
cmd1 === cmd3 // false

जावा:

interface Command {
    void exec(Object... args);
}
Command cmd1 = new Command() {
    public void exec(Object... args) [
       // do something
    }
}
Command cmd2 = cmd1;
Command cmd3 = new Command() {
   public void exec(Object... args) {
      // do something else
   }
}

cmd1 == cmd2 // true
cmd1 == cmd3 // false

यह बहुत अच्छा होगा यदि आप इसे सामान्य बना दें।
अलेक्जेंडर - मोनिका

2

खैर 2 दिन हो गए हैं और किसी ने हल नहीं निकाला है, इसलिए मैं अपनी टिप्पणी को एक उत्तर में बदलूंगा:

जहां तक ​​मैं बता सकता हूं, आप कार्यों की समानता या पहचान (उदाहरण के लिए) और मेटाक्लेसेस (जैसे, MyClass.self) की जांच नहीं कर सकते :

लेकिन - और यह सिर्फ एक विचार है - मैं मदद नहीं कर सकता लेकिन ध्यान दें कि whereजेनरिक में क्लॉज प्रकार की समानता की जांच करने में सक्षम प्रतीत होता है। तो शायद आप इसका लाभ उठा सकते हैं, कम से कम पहचान की जाँच के लिए?


2

सामान्य समाधान नहीं है, लेकिन अगर कोई श्रोता पैटर्न को लागू करने की कोशिश कर रहा है, तो मैंने पंजीकरण के दौरान फ़ंक्शन की "आईडी" वापस कर दी है, इसलिए मैं इसे बाद में अपंजीकृत करने के लिए उपयोग कर सकता हूं (जो मूल प्रश्न का एक प्रकार है) "श्रोताओं" के मामले के रूप में आमतौर पर अपंजीकृत समानता के कार्यों की जांच करने के लिए नीचे आता है, जो कि कम से कम अन्य उत्तरों के अनुसार "तुच्छ" नहीं है।

तो कुछ इस तरह:

class OfflineManager {
    var networkChangedListeners = [String:((Bool) -> Void)]()

    func registerOnNetworkAvailabilityChangedListener(_ listener: @escaping ((Bool) -> Void)) -> String{
        let listenerId = UUID().uuidString;
        networkChangedListeners[listenerId] = listener;
        return listenerId;
    }
    func unregisterOnNetworkAvailabilityChangedListener(_ listenerId: String){
        networkChangedListeners.removeValue(forKey: listenerId);
    }
}

अब आपको बस key"रजिस्टर" फ़ंक्शन द्वारा लौटे को स्टोर करना होगा और अपंजीकृत होने पर इसे पास करना होगा।


0

मेरा समाधान NSObject तक फैली कक्षा में कार्यों को लपेटना था

class Function<Type>: NSObject {
    let value: (Type) -> Void

    init(_ function: @escaping (Type) -> Void) {
        value = function
    }
}

जब आप ऐसा करते हैं, तो उनकी तुलना कैसे करें? मान लीजिए कि आप उनमें से किसी एक को अपने रैपर से हटाना चाहते हैं, तो आप ऐसा कैसे करते हैं? धन्यवाद।
रिकार्डो

0

मुझे पता है कि मैं छह साल देर से इस सवाल का जवाब दे रहा हूं, लेकिन मुझे लगता है कि यह सवाल के पीछे की प्रेरणा को देखने लायक है। प्रश्नकर्ता ने टिप्पणी की:

संदर्भ द्वारा एक मंगलाचरण सूची से क्लोजर निकालने में सक्षम होने के बिना, हालांकि, हमें अपना स्वयं का आवरण वर्ग बनाने की आवश्यकता है। यह एक खींचें है, और आवश्यक नहीं होना चाहिए।

इसलिए मुझे लगता है कि प्रश्नकर्ता इस तरह से कॉलबैक सूची बनाए रखना चाहता है:

class CallbackList {
    private var callbacks: [() -> ()] = []

    func call() {
        callbacks.forEach { $0() }
    }

    func addCallback(_ callback: @escaping () -> ()) {
        callbacks.append(callback)
    }

    func removeCallback(_ callback: @escaping () -> ()) {
        callbacks.removeAll(where: { $0 == callback })
    }
}

लेकिन हम removeCallbackउस तरह से नहीं लिख सकते हैं , क्योंकि ==कार्यों के लिए काम नहीं करता है। (न ही करता है ===।)

यहां आपकी कॉलबैक सूची प्रबंधित करने का एक अलग तरीका है। addCallbackकॉलबैक हटाने के लिए पंजीकरण ऑब्जेक्ट को वापस लौटाएँ , और पंजीकरण ऑब्जेक्ट का उपयोग करें। यहां 2020 में, हम कम्बाइन के AnyCancellableपंजीकरण के रूप में उपयोग कर सकते हैं ।

संशोधित एपीआई इस तरह दिखता है:

class CallbackList {
    private var callbacks: [NSObject: () -> ()] = [:]

    func call() {
        callbacks.values.forEach { $0() }
    }

    func addCallback(_ callback: @escaping () -> ()) -> AnyCancellable {
        let key = NSObject()
        callbacks[key] = callback
        return .init { self.callbacks.removeValue(forKey: key) }
    }
}

अब, जब आप कॉलबैक जोड़ते हैं, तो आपको removeCallbackबाद में पास करने के लिए इसे इधर-उधर रखने की आवश्यकता नहीं होती है । कोई removeCallbackविधि नहीं है। इसके बजाय, आप कॉलबैक को निकालने के लिए AnyCancellableइसकी cancelविधि को सहेजते हैं और कॉल करते हैं। इससे भी बेहतर, यदि आप AnyCancellableएक इंस्टेंस प्रॉपर्टी में स्टोर करते हैं, तो यह इंस्टेंस नष्ट होने पर अपने आप रद्द हो जाएगा।


इसका सबसे आम कारण है कि हमें प्रकाशकों के लिए कई ग्राहकों को प्रबंधित करना है। इन सभी के बिना हल मिलाएं। क्या C # अनुमति देता है, और स्विफ्ट यह पता लगाने के लिए नहीं है कि क्या दो क्लोजर एक ही नामित फ़ंक्शन को संदर्भित करते हैं। यह भी उपयोगी है, लेकिन बहुत कम बार।
जेसी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.