Normal yöntemden protokol varsayılan uygulamasını çağırma


83

Böyle bir şeyi başarmanın mümkün olup olmadığını merak ediyorum.
Bunun gibi bir Oyun Alanım var:

protocol Foo {
    func testPrint()
}

extension Foo {
    func testPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        // Calling self or super go call default implementation
        self.testPrint()
        print("Call from struct")
    }
}


let sth = Bar()
sth.testPrint()

İçinde varsayılan bir uygulama sağlayabilirim, extensionancak ya varsayılan uygulamadaki Barher şeye ve ek şeylere ihtiyaç duyuyorsa?
Bu bir şekilde her özelliği uygulama vb. Gereksinimlerini yerine getirmek için super.yöntem çağırmaya benzer, classancak aynı şeyi başarma imkanı görmüyorum structs.


Kullanırım Foo.testPrint(self)()- sorun, segmentasyon hatası nedeniyle başarısız olmasıdır (hem 7.0 GM hem de 7.1 beta üzerinde test edilmiştir)
Antonio

1
Bu sunduğunuz garip bir yapı 😯
cojoj

4
Her örnek yöntemi, bir örneği ilk parametresi olarak alan statik bir curried yöntemdir
Antonio

Ancak uzantıyı kaldırmayı denedim ve aynı segmentasyon hatasını veriyor. Muhtemelen bunun protokollerle çalışmaması gerekiyor
Antonio

Hmmm, kendimi kodda tekrar etmem ve bu, varsayılan uygulama kullanılarak kolayca düzeltilebilirken utanç verici ...
cojoj

Yanıtlar:


90

Hala buna bir cevap arıyor musunuz bilmiyorum, ancak bunu yapmanın yolu, işlevi protokol tanımından kaldırmak, nesnenizi atamak Foove ardından yöntemi çağırmaktır:

protocol Foo { 
    // func testPrint() <- comment this out or remove it
}

extension Foo {
    func testPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        print("Call from struct")
        (self as Foo).testPrint() // <- cast to Foo and you'll get the  default
                                  //    function defined in the extension
    }
}

Bar().testPrint()

// Output:    "Call from struct"
//            "Protocol extension call"

Bazı nedenlerden dolayı, yalnızca işlev protokolün bir parçası olarak bildirilmemişse, ancak protokolün bir uzantısı olarak tanımlanmışsa çalışır. Şekle gidin. Ama işe yarıyor.


1
Aman Tanrım! Seni protokolden işe yaramaya zorladıklarına inanamıyorum! iyi cevap, teşekkür ederim!
SkyWalker

5
Bu benim için bir hata gibi görünüyor, sadece aynı örneği çevirerek farklı bir yöntem uygulaması elde etmek mümkün olmamalı.
MANIAK_dobrii

15
Bu aslında protokol + uzantısının anlamını büyük ölçüde değiştirir. Bildirimi protokolün dışında bırakırsanız, protokole uygun bir türde işlevi çağırırken statik gönderim elde edersiniz - bu nedenle uzantıdan uygulamayı çevirebilir ve alabilirsiniz. Bildirimi protokole eklerseniz, işlev çağrınız dinamik olarak gönderilir.
Thorsten Karrer

2
Bu, yalnızca Fooprotokol başka herhangi bir protokolden miras almıyorsa işe yarar .
iyuna

4
Bu sonsuz bir döngüye yol açmaz mı?
stan liu

9

Pekala, protokole uygun iç içe geçmiş bir tür oluşturabilir, onu başlatabilir ve yöntemi ondan çağırabilirsiniz (protokol uzantısının içindeki uygulama buna zaten başvuruda bulunamayacağı için türünüzün verilerine erişememeniz önemli değildir). Ama zarif diyebileceğim bir çözüm değil.

struct Bar: Foo {
    func testPrint() {
        // Calling default implementation
        struct Dummy : Foo {}
        let dummy = Dummy()
        dummy.testPrint()
        print("Call from struct")
    }
}

1
Şu anda tek olasılık bu gibi görünüyor (Apple tarafından onaylandı) ... Faydalı olabileceğinden bunun için bir özellik radarı oluşturacağım 👌
cojoj

4

Gönderi için teşekkürler! İşlev tanımını protokole koyarsanız, nesne protokol olarak dönüştürüldüğünde, yalnızca nesnenin işlevin sürümünü görür ve siz onu kendi içinde çağırdığınız için Apple'ın yeni adresini alırsınız ...

Bunun gibi bir versiyon denedim:

import UIKit
protocol MyProc
{
}

protocol MyFuncProc
{
    func myFunc()
}

extension MyProc
{
    func myFunc()
    {
        print("Extension Version")
    }
}

struct MyStruct: MyProc, MyFuncProc
{
    func myFunc()
    {
        print("Structure Version")
        (self as MyProc).myFunc()
    }
}

(MyStruct() as MyFuncProc).myFunc()

Bu bir çıktı verir:

Structure Version
Extension Version

3

Protokolünüzün associatedTypeveya Selfgereksinimlerin olması durumunda , döküm çalışmayacaktır. Bu sorunu çözmek için, hem normal varsayılan uygulamanın hem de uygun türün çağırabileceği bir "gölge" varsayılan uygulaması oluşturun.

protocol Foo { 
    associatedType Bar
}

extension Foo {
    func testPrint() {
        defaultTestPrint()
    }
}

fileprivate extension Foo { // keep this as private as possible
    func defaultTestPrint() {
        // default implementation
    }
}

struct Bar: Foo {
    func testPrint() {
        // specialized implementation
        defaultTestPrint()
    }
}

Sana daha fazla katılamıyorum. defaultXX()diğer cevaplardan çok daha anlamlı ve okunabilir.
DawnSong

Sanırım Amin Madani cevabınızı geliştirdi.
DawnSong

2

Bunu düzeltmenin böyle bir yolu hakkında ne düşünüyorsun?

protocol Foo {
    func testPrint()
}

extension Foo {
    func testPrint() {
        defaultTestPrint()
    }

    func defaultTestPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        // Calling self or super go call default implementation
        defaultTestPrint()
        print("Call from struct")
    }
}


let sth = Bar()
sth.testPrint()

Bu bir cevap mı?
Anh Pham

@AnhPham bu, varsayılan işlevselliği gerçekleştirmek için başka bir yöntem
Amin Madani

net ve kolay çözüm
Stephan Januar

En etkileyici ve esnek cevap.
DawnSong

2

Bunun için bir çözüm buldum.

Konu

Bir uzantıda varsayılan bir uygulamaya sahip olduğunuzda, protokolü başka bir sınıfa / yapıya uyguladığınızda, yöntemi uygularsanız bu varsayılan uygulamayı kaybedersiniz. Bu tasarım gereğidir, protokoller böyle çalışır

Çözüm

  • Protokolünüz için bir Varsayılan Uygulama oluşturun ve bunu protokolünüzün bir özelliği yapın.
  • Daha sonra bu protokolü bir sınıfta uyguladığınızda, varsayılan uygulamanızı bir alıcı ile sağlayın
  • İhtiyaç duyduğunuzda varsayılan uygulamayı arayın.

Misal


protocol Foo {
    var defaultImplementation: DefaultImpl? { get }
    func testPrint()
}

extension Foo {
    // Add default implementation
    var defaultImplementation: DefaultImpl? {
        get {
            return nil
        }
    }
}

struct DefaultImpl: Foo {
    func testPrint() {
        print("Foo")
    }
}


extension Foo {
    
    func testPrint() {
        defaultImplementation?.testPrint()
    }
}

struct Bar: Foo {
    
    var defaultImplementation: DefaultImpl? {
        get { return DefaultImpl() }
    }
    func testPrint() {
        if someCondition {
            defaultImplementation?.testPrint() // Prints "Foo"
        }
    }
}

struct Baz: Foo {
    func testPrint() {
        print("Baz")
    }
}


let bar = Bar()
bar.testPrint() // prints "Foo"

let baz = Baz()
baz.testPrint() // prints "Baz"


Dezavantajlar

Bu protokolü uyguladığınız yapı / sınıfta gerekli uygulama hatasını kaybedersiniz.


1
Lütfen kodunuzun neden çalıştığını açıkladığınızdan emin olun. Bu, cevabınızı ziyaret edenlerin ondan öğrenmesine yardımcı olur.
AlexH

Evet bu iyi bir uygulama!
juxhin bleta
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.