Swift @autoclosure nasıl kullanılır


149

assertSwift ile yazarken ilk değerin şu şekilde yazıldığını fark ettim:

@autoclosure() -> Bool

jenerik bir Tdeğer döndürmek için aşırı yüklenmiş bir yöntemle LogicValue protocol,.

Ancak eldeki soruya kesinlikle bağlı kalıyor. A @autoclosuredöndüren bir istiyor gibi görünüyor Bool.

Hiçbir parametre almayan ve bir Bool döndüren gerçek bir kapanış yazmak işe yaramaz, derlemek için kapanışı çağırmamı ister, şöyle:

assert({() -> Bool in return false}(), "No user has been set", file: __FILE__, line: __LINE__)

Ancak basitçe bir Bool geçmek işe yarar:

assert(false, "No user has been set", file: __FILE__, line: __LINE__)

Peki neler oluyor? Nedir @autoclosure?

Düzenleme: @auto_closure yeniden adlandırıldı@autoclosure

Yanıtlar:


271

Tek bir argüman alan, argüman içermeyen basit bir kapanışı düşünün:

func f(pred: () -> Bool) {
    if pred() {
        print("It's true")
    }
}

Bu işlevi çağırmak için bir kapanış yapmalıyız

f(pred: {2 > 1})
// "It's true"

Kaşlı ayraçları atlarsak, bir ifadeyi iletmiş oluruz ve bu bir hata:

f(pred: 2 > 1)
// error: '>' produces 'Bool', not the expected contextual result type '() -> Bool'

@autoclosureifade etrafında otomatik bir kapanma yaratır. Yani arayan kişi gibi bir ifade yazdığında 2 > 1, otomatik olarak kapatılmadan {2 > 1}önce kapatılır f. Yani bunu işleve uygularsak f:

func f(pred: @autoclosure () -> Bool) {
    if pred() {
        print("It's true")
    }
}

f(pred: 2 > 1)
// It's true

Böylece, onu kapatmaya gerek kalmadan sadece bir ifadeyle çalışır.


2
Aslında sonuncusu çalışmıyor. O olmalıf({2 >1}())
Rui Peres

@JoelFischer @JackyBoy ile aynı şeyi görüyorum. Arama f(2 > 1)çalışır. Çağrı f({2 > 1})başarısız olur error: function produces expected type 'Bool'; did you mean to call it with '()'?. Bir oyun alanında ve Swift REPL ile test ettim.
Ole Begemann

Bir şekilde son cevap olarak ikinciden son cevaba kadar okudum, iki kez kontrol etmem gerekecek, ancak temelde bir kapanış içine bir kapatma koyduğunuz için, anladığım kadarıyla başarısız olursa mantıklı olacaktır.
Joel Fischer


5
Harika açıklama. Ayrıca Swift 1.2'de 'otomatik kapanma'nın artık parametre bildiriminin bir özniteliği olduğuna dikkat edin, bu yüzdenfunc f(@autoclosure pred: () -> Bool)
Masa

30

İşte pratik bir örnek - benim printgeçersiz kılmam (bu Swift 3):

func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
    #if DEBUG
    Swift.print(item(), separator:separator, terminator: terminator)
    #endif
}

Dediğinizde print(myExpensiveFunction()), benim printgeçersiz kılmam Swift'in gölgesini düşürür printve çağrılır. myExpensiveFunction()bu nedenle bir kapağa sarılır ve değerlendirilmez . Release modundaysak, asla değerlendirilmeyecek çünkü item()çağrılmayacak. Dolayısıyla print, Release modunda argümanlarını değerlendirmeyen bir versiyonumuz var .


1
Partiye geç kaldım ama değerlendirmenin etkisi myExpensiveFunction()nedir? Otomatik kapatmayı kullanmak yerine işlevi yazdırmaya benzer şekilde geçirirseniz print(myExpensiveFunction), etkisi ne olur? Teşekkürler.
crom87

11

Dokümanlardan otomatik kapanmanın açıklaması:

Auto_closure özniteliğini parametre türü () olan ve bir ifadenin türünü döndüren bir işlev türüne uygulayabilirsiniz (bkz. Tür Öznitelikleri). Bir otomatik kapama işlevi, ifadenin kendisi yerine belirtilen ifade üzerinde örtük bir kapanışı yakalar. Aşağıdaki örnek, çok basit bir assert işlevini tanımlarken auto_closure özniteliğini kullanır:

Ve işte elmanın onunla birlikte kullandığı örnek.

func simpleAssert(condition: @auto_closure () -> Bool, message: String) {
    if !condition() {
        println(message)
    }
}
let testNumber = 5
simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.")

Temel olarak anlamı, bir boolean ifadesini bir kapanış yerine ilk argüman olarak geçirmeniz ve sizin için otomatik olarak ondan bir kapanış yaratmasıdır. Bu yüzden yönteme yanlış geçebilirsiniz çünkü bu bir mantıksal ifade olduğundan, ancak bir kapanışı geçemezsiniz.


15
Aslında @auto_closureburada kullanmanız gerekmediğini unutmayın . Kod onsuz cezası çalışır: func simpleAssert(condition: Bool, message: String) { if !condition { println(message) } }. Kullanım @auto_closuredefalarca bir tartışmayı değerlendirmek gerektiğinde (örneğin, bir uygulayıcı olsaydı whilebenzeri fonksiyonu) veya bir argüman (eğer uygulamaya olsaydı mesela, kısa devre gecikme değerlendirilmesi gerekir &&).
nathan

1
@nathan Merhaba, nathan. Bana kullanımına ilişkin bir örnek alıntı misiniz autoclosurebir ile whilebenzeri işleve? Sanırım bunu çözemiyorum. Şimdiden çok teşekkürler.
Unheilig

@connor Swift 3 için cevabınızı güncellemek isteyebilirsiniz.
jarora

4

Bu, https://airspeedvelocity.net/2014/06/28/extending-the-swift-language-is-cool-but-be-careful/ için yararlı bir durumu gösterir.@autoclosure

Şimdi, ilk parametre olarak geçilen koşullu ifade, otomatik olarak bir kapanış ifadesine sarılır ve döngü etrafında her seferinde çağrılabilir.

func until<L: LogicValue>(pred: @auto_closure ()->L, block: ()->()) {
    while !pred() {
        block()
    }
}

// doSomething until condition becomes true
until(condition) {
    doSomething()
}

2

Bu sadece bir kapatma çağrısında küme parantezlerinden kurtulmanın bir yolu, basit bir örnek:

    let nonAutoClosure = { (arg1: () -> Bool) -> Void in }
    let non = nonAutoClosure( { 2 > 1} )

    let autoClosure = { (arg1: @autoclosure () -> Bool) -> Void in }
    var auto = autoClosure( 2 > 1 ) // notice curly braces omitted

0

@autoclosurebir pişmiş işlevi (veya döndürülen türü) kabul eden bir işlev parametresidir, bu arada genel closurebir ham işlevi kabul eder

  • @autoclosure bağımsız değişken türü parametresi '()' olmalıdır
    @autoclosure ()
    
  • @autoclosure yalnızca uygun döndürülen türle herhangi bir işlevi kabul eder
  • Kapanmanın sonucu talebe göre hesaplanır

Örneğe bir göz atalım

func testClosures() {

    //closures
    XCTAssertEqual("fooWithClosure0 foo0", fooWithClosure0(p: foo0))
    XCTAssertEqual("fooWithClosure1 foo1 1", fooWithClosure1(p: foo1))
    XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: foo2))

    XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: { (i1, i2) -> String in
        return "fooWithClosure2 " + "foo2 " + String(i1 + i2)
    }))

    //@autoclosure
    XCTAssertEqual("fooWithAutoClosure HelloWorld", fooWithAutoClosure(a: "HelloWorld"))

    XCTAssertEqual("fooWithAutoClosure foo0", fooWithAutoClosure(a: foo0()))
    XCTAssertEqual("fooWithAutoClosure foo1 1", fooWithAutoClosure(a: foo1(i1: 1)))
    XCTAssertEqual("fooWithAutoClosure foo2 3", fooWithAutoClosure(a: foo2(i1: 1, i2: 2)))

}

//functions block
func foo0() -> String {
    return "foo0"
}

func foo1(i1: Int) -> String {
    return "foo1 " + String(i1)
}

func foo2(i1: Int, i2: Int) -> String {
    return "foo2 " + String(i1 + i2)
}

//closures block
func fooWithClosure0(p: () -> String) -> String {
    return "fooWithClosure0 " + p()
}

func fooWithClosure1(p: (Int) -> String) -> String {
    return "fooWithClosure1 " + p(1)
}

func fooWithClosure2(p: (Int, Int) -> String) -> String {
    return "fooWithClosure2 " + p(1, 2)
}

//@autoclosure
func fooWithAutoClosure(a: @autoclosure () -> String) -> String {
    return "fooWithAutoClosure " + a()
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.