खोज…


वाक्य - विन्यास

  • var closureVar: (<पैरामीटर>) -> (<returnType>) // एक चर या संपत्ति प्रकार के रूप में
  • टाइपेलियास क्लोजटाइप = (<पैरामीटर>) -> (<रिटर्नटाइप>)
  • {<<कैप्चरलिस्ट>] (<पैरामीटर>) <थ्रो-नेस> -> <रिटर्नटाइप> इन स्टेटमेंट>} // पूरा क्लोजर सिंटैक्स

टिप्पणियों

स्विफ्ट क्लोजर के बारे में अधिक जानकारी के लिए, Apple के दस्तावेज़ देखें।

मूल बातें बंद

क्लोजर (जिसे ब्लॉक या लैम्ब्डा के रूप में भी जाना जाता है) कोड के टुकड़े हैं जिन्हें आपके प्रोग्राम के भीतर संग्रहीत और पास किया जा सकता है।

let sayHi = { print("Hello") }
// The type of sayHi is "() -> ()", aka "() -> Void"

sayHi()  // prints "Hello"

अन्य कार्यों की तरह, क्लोजर तर्क स्वीकार कर सकते हैं और परिणाम लौटा सकते हैं या त्रुटियां फेंक सकते हैं :

let addInts = { (x: Int, y: Int) -> Int in
    return x + y
}
// The type of addInts is "(Int, Int) -> Int"

let result = addInts(1, 2)  // result is 3

let divideInts = { (x: Int, y: Int) throws -> Int in
    if y == 0 {
        throw MyErrors.DivisionByZero
    }
    return x / y
}
// The type of divideInts is "(Int, Int) throws -> Int"

क्लोज़र अपने दायरे से मूल्यों को पकड़ सकते हैं :

// This function returns another function which returns an integer
func makeProducer(x: Int) -> (() -> Int) {
    let closure = { x }  // x is captured by the closure
    return closure
}

// These two function calls use the exact same code,
// but each closure has captured different values.
let three = makeProducer(3)
let four = makeProducer(4)
three()  // returns 3
four()  // returns 4

क्लोजर सीधे कार्यों में पारित किया जा सकता है:

let squares = (1...10).map({ $0 * $0 })  // returns [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
let squares = (1...10).map { $0 * $0 }

NSURLSession.sharedSession().dataTaskWithURL(myURL,
    completionHandler: { (data: NSData?, response: NSURLResponse?, error: NSError?) in
        if let data = data {
            print("Request succeeded, data: \(data)")
        } else {
            print("Request failed: \(error)")
        }
    }).resume()

सिंटेक्स भिन्नता

मूल बंद सिंटैक्स है

{ [ कब्जा सूची ] ( पैरामीटर ) थ्रो-नेस -> शरीर in वापसी प्रकार }

इनमें से कई हिस्सों को छोड़ा जा सकता है, इसलिए सरल क्लोजर लिखने के कई समकक्ष तरीके हैं:

let addOne = { [] (x: Int) -> Int in return x + 1 }
let addOne = { [] (x: Int) -> Int in x + 1 }
let addOne = { (x: Int) -> Int in x + 1 }
let addOne = { x -> Int in x + 1 }
let addOne = { x in x + 1 }
let addOne = { $0 + 1 }

let addOneOrThrow = { [] (x: Int) throws -> Int in return x + 1 }
let addOneOrThrow = { [] (x: Int) throws -> Int in x + 1 }
let addOneOrThrow = { (x: Int) throws -> Int in x + 1 }
let addOneOrThrow = { x throws -> Int in x + 1 }
let addOneOrThrow = { x throws in x + 1 }
  • खाली होने पर कैप्चर सूची को छोड़ा जा सकता है।
  • यदि उनके प्रकार का अनुमान लगाया जा सकता है, तो पैरामीटर्स को एनोटेशन की आवश्यकता नहीं है।
  • यदि यह पता लगाया जा सकता है कि रिटर्न प्रकार को निर्दिष्ट करने की आवश्यकता नहीं है।
  • पैरामीटर का नाम नहीं होना चाहिए; इसके बजाय उन्हें $0 , $1 , $2 , आदि के साथ संदर्भित किया जा सकता है।
  • यदि क्लोजर में एक एकल अभिव्यक्ति होती है, जिसका मान लौटाया जाना है, तो return कीवर्ड छोड़ा जा सकता है।
  • यदि बंद करने के लिए एक त्रुटि को फेंकने का अनुमान लगाया जाता है, तो एक संदर्भ में लिखा जाता है जो फेंकने के बंद होने की उम्मीद करता है, या एक त्रुटि को फेंक नहीं throws , throws छोड़ा जा सकता है।
// The closure's type is unknown, so we have to specify the type of x and y.
// The output type is inferred to be Int, because the + operator for Ints returns Int.
let addInts = { (x: Int, y: Int) in x + y }

// The closure's type is specified, so we can omit the parameters' type annotations.
let addInts: (Int, Int) -> Int = { x, y in x + y }
let addInts: (Int, Int) -> Int = { $0 + $1 }

कार्यों में पासिंग बंद

पैरामीटर के रूप में कार्य बंद (या अन्य कार्यों) को स्वीकार कर सकते हैं:

func foo(value: Double, block: () -> Void) { ... }
func foo(value: Double, block: Int -> Int) { ... }
func foo(value: Double, block: (Int, Int) -> String) { ... }

ट्रेलिंग क्लोजर सिंटैक्स

यदि किसी फ़ंक्शन का अंतिम पैरामीटर क्लोजर है, तो क्लोजर ब्रेसेस { / } फंक्शन इनवोकेशन के बाद लिखा जा सकता है:

foo(3.5, block: { print("Hello") })

foo(3.5) { print("Hello") }

dispatch_async(dispatch_get_main_queue(), {
    print("Hello from the main queue")
})

dispatch_async(dispatch_get_main_queue()) {
    print("Hello from the main queue")
}

यदि किसी फ़ंक्शन का एकमात्र तर्क एक क्लोजर है, तो आप कोष्ठक युग्मक () को ट्रेसिंग क्लोजर सिंटैक्स के साथ कॉल करते समय छोड़ सकते हैं:

func bar(block: () -> Void) { ... }

bar() { print("Hello") }

bar { print("Hello") }

@noescape पैरामीटर

फ़ंक्शन कॉल रिटर्न से पहले @noescape चिह्नित क्लोजर मापदंडों को निष्पादित करने की गारंटी है, इसलिए self. का उपयोग करना self. बंद शरीर के अंदर की आवश्यकता नहीं है:

func executeNow(@noescape block: () -> Void) {
    // Since `block` is @noescape, it's illegal to store it to an external variable.
    // We can only call it right here.
    block()
}

func executeLater(block: () -> Void) {
    dispatch_async(dispatch_get_main_queue()) {
        // Some time in the future...
        block()
    }
}
class MyClass {
    var x = 0
    func showExamples() {
        // error: reference to property 'x' in closure requires explicit 'self.' to make capture semantics explicit
        executeLater { x = 1 }

        executeLater { self.x = 2 }  // ok, the closure explicitly captures self

        // Here "self." is not required, because executeNow() takes a @noescape block.
        executeNow { x = 3 }

        // Again, self. is not required, because map() uses @noescape.
        [1, 2, 3].map { $0 + x }
    }
}

स्विफ्ट 3 नोट:

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


throws और rethrows

अन्य कार्यों की तरह, क्लोज़र में त्रुटियां हो सकती हैं :

func executeNowOrIgnoreError(block: () throws -> Void) {
    do {
        try block()
    } catch {
        print("error: \(error)")
    }
}

फ़ंक्शन, निश्चित रूप से, इसके कॉलर के साथ त्रुटि पास कर सकता है:

func executeNowOrThrow(block: () throws -> Void) throws {
    try block()
}

हालाँकि, यदि ब्लॉक फेंकता नहीं है , तब भी कॉल करने वाला फ़ेंकने के कार्य से अड़ा रहता है:

// It's annoying that this requires "try", because "print()" can't throw!
try executeNowOrThrow { print("Just printing, no errors here!") }

समाधान को rethrows से rethrows , जो यह rethrows कि फ़ंक्शन केवल तभी फेंक सकता है जब उसका क्लोजर पैरामीटर फेंकता है :

func executeNowOrRethrow(block: () throws -> Void) rethrows {
    try block()
}

// "try" is not required here, because the block can't throw an error.
executeNowOrRethrow { print("No errors are thrown from this closure") }

// This block can throw an error, so "try" is required.
try executeNowOrRethrow { throw MyError.Example }

कई मानक पुस्तकालय कार्यों का उपयोग rethrows सहित map() , filter() , और indexOf()

कैप्चर, मजबूत / कमजोर संदर्भ और चक्र को बनाए रखना

class MyClass {
    func sayHi() { print("Hello") }
    deinit { print("Goodbye") }
}

जब एक बंद एक संदर्भ प्रकार (एक वर्ग उदाहरण) को पकड़ता है, तो यह डिफ़ॉल्ट रूप से एक मजबूत संदर्भ रखता है:

let closure: () -> Void
do {
    let obj = MyClass()
    // Captures a strong reference to `obj`: the object will be kept alive
    // as long as the closure itself is alive.
    closure = { obj.sayHi() }
    closure()  // The object is still alive; prints "Hello"
} // obj goes out of scope
closure()  // The object is still alive; prints "Hello"

क्लोजर की कैप्चर सूची का उपयोग एक कमजोर या अज्ञात संदर्भ निर्दिष्ट करने के लिए किया जा सकता है:

let closure: () -> Void
do {
    let obj = MyClass()
    // Captures a weak reference to `obj`: the closure will not keep the object alive;
    // the object becomes optional inside the closure.
    closure = { [weak obj] in obj?.sayHi() }
    closure()  // The object is still alive; prints "Hello"
} // obj goes out of scope and is deallocated; prints "Goodbye"
closure()  // `obj` is nil from inside the closure; this does not print anything.
let closure: () -> Void
do {
    let obj = MyClass()
    // Captures an unowned reference to `obj`: the closure will not keep the object alive;
    // the object is always assumed to be accessible while the closure is alive.
    closure = { [unowned obj] in obj.sayHi() }
    closure()  // The object is still alive; prints "Hello"
} // obj goes out of scope and is deallocated; prints "Goodbye"
closure()  // crash! obj is being accessed after it's deallocated.

अधिक जानकारी के लिए, मेमोरी प्रबंधन विषय और स्विफ्ट प्रोग्रामिंग भाषा के स्वचालित संदर्भ गणना अनुभाग देखें।

चक्रों को बनाए रखें

यदि कोई वस्तु किसी क्लोजर पर रहती है, जो ऑब्जेक्ट के लिए एक मजबूत संदर्भ भी रखती है, तो यह एक चक्र है । जब तक चक्र टूट नहीं जाता है, तब तक ऑब्जेक्ट और क्लोजर को स्टोर करने वाली मेमोरी लीक हो जाएगी (कभी पुनः प्राप्त नहीं)।

class Game {
    var score = 0
    let controller: GCController
    init(controller: GCController) {
        self.controller = controller

        // BAD: the block captures self strongly, but self holds the controller
        // (and thus the block) strongly, which is a cycle.
        self.controller.controllerPausedHandler = {
            let curScore = self.score
            print("Pause button pressed; current score: \(curScore)")
        }

        // SOLUTION: use `weak self` to break the cycle.
        self.controller.controllerPausedHandler = { [weak self] in
            guard let strongSelf = self else { return }
            let curScore = strongSelf.score
            print("Pause button pressed; current score: \(curScore)")
        }
    }
}

अतुल्यकालिक कोडिंग के लिए क्लोजर का उपयोग करना

क्लोज़र का उपयोग अक्सर अतुल्यकालिक कार्यों के लिए किया जाता है, उदाहरण के लिए जब किसी वेबसाइट से डेटा प्राप्त करना।

3.0
func getData(urlString: String, callback: (result: NSData?) -> Void) {

    // Turn the URL string into an NSURLRequest.
    guard let url = NSURL(string: urlString) else { return }
    let request = NSURLRequest(URL: url)
    
    // Asynchronously fetch data from the given URL.
    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {(data: NSData?, response: NSURLResponse?, error: NSError?) in

        // We now have the NSData response from the website.
        // We can get it "out" of the function by using the callback 
        // that was passed to this function as a parameter.

        callback(result: data)
    }
        
    task.resume()
}

यह फ़ंक्शन अतुल्यकालिक है, इसलिए उस थ्रेड को ब्लॉक नहीं किया जाएगा जिस पर इसे बुलाया जा रहा है (यह आपके GUI एप्लिकेशन के मुख्य थ्रेड पर कॉल किए जाने पर इंटरफ़ेस को फ़्रीज़ नहीं करेगा)।

3.0
print("1. Going to call getData")

getData("http://www.example.com") {(result: NSData?) -> Void in

    // Called when the data from http://www.example.com has been fetched.
    print("2. Fetched data")
}

print("3. Called getData")

क्योंकि कार्य अतुल्यकालिक है, आमतौर पर आउटपुट इस तरह दिखेगा:

"1. Going to call getData"
"3. Called getData"
"2. Fetched data"

क्‍योंकि क्‍लोजर के अंदर का कोड, print("2. Fetched data") , तब तक नहीं कहा जाएगा जब तक कि URL का डेटा प्राप्‍त नहीं हो जाता।

क्लोजर और टाइप उपनाम

एक क्लोजर को typealias साथ परिभाषित किया जा सकता है। यह एक सुविधाजनक प्रकार प्लेसहोल्डर प्रदान करता है यदि एक ही क्लोजर हस्ताक्षर का उपयोग कई स्थानों पर किया जाता है। उदाहरण के लिए, सामान्य नेटवर्क अनुरोध कॉलबैक या उपयोगकर्ता इंटरफ़ेस इवेंट हैंडलर एक प्रकार के उपनाम के साथ "नाम" होने के लिए महान उम्मीदवार बनाते हैं।

public typealias ClosureType = (x: Int, y: Int) -> Int

तब आप टाइपेलियास का उपयोग करके एक फ़ंक्शन को परिभाषित कर सकते हैं:

public func closureFunction(closure: ClosureType) {
    let z = closure(1, 2)
}
    
closureFunction() { (x: Int, y: Int) -> Int in return x + y }


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow