Non - '@ objc' yöntemi, '@objc' protokolünün isteğe bağlı gereksinimini karşılamıyor


104

Genel Bakış:

  • Objective-C isteğe bağlı işlevlerinden birinin varsayılan uygulamasını sağlayan bir P1 protokolüne sahibim.
  • İsteğe bağlı işlevin varsayılan uygulamasını sağladığımda bir uyarı alıyorum

Derleyici Uyarısı:

Non-'@objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '@objc' protocol 'UIAdaptivePresentationControllerDelegate'

Versiyon:

  • Hızlı: 3
  • Xcode: 8 (genel sürüm)

Yapılan denemeler:

  • Eklemeye çalıştı @objcama yardımcı olmadı

Soru:

  • Bunu nasıl çözerim?
  • Etrafta bir çalışma var mı?

Kod:

@objc protocol P1 : UIAdaptivePresentationControllerDelegate {

}

extension P1 where Self : UIViewController {

    func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        return UIViewController()
    }
}


class A : UIViewController, P1 {

}

Xcode'un en yeni sürümüne sahip misiniz? @objc
Kaldırırsam

Xcode 8 (en son genel sürüm) kullanıyorum. Hata yok, ancak bir uyarı olacak
user1046037

Yanıtlar:


184

Sorunuza cevap verebileceğimi düşünsem de hoşunuza gidecek bir cevap değil.

TL; DR: @objc işlevler şu anda protokol uzantılarında olmayabilir. Bunun yerine bir temel sınıf oluşturabilirsiniz, ancak bu ideal bir çözüm değildir.

Protokol Uzantıları ve Amaç-C

İlk olarak, bu soru / cevap ( Hedef-c'de Erişilen Protokollerde Eklentilerde Tanımlanan Can Swift Yöntemi ), protokol uzantılarının başlık altında gönderilme şekli nedeniyle, protokol uzantılarında bildirilen yöntemlerin objc_msgSend()işlev tarafından görülemediğini ve bu nedenle Objective-C kodu tarafından görülmez. Uzantınızda tanımlamaya çalıştığınız yöntemin Objective-C tarafından görülebilmesi gerektiğinden (bu yüzden UIKitonu kullanabilirsiniz), dahil etmediğiniz için size bağırır @objc, ancak bir kez dahil ettiğinizde size bağırır çünkü @objciçeri girmesine izin verilmez. protokol uzantıları. Bunun nedeni muhtemelen protokol uzantılarının şu anda Objective-C tarafından görülememesidir.

Ayrıca, @objc"@objc yalnızca sınıfların üyeleri, @objc protokolleri ve sınıfların somut uzantıları ile kullanılabilir" durumlarını eklediğimizde hata mesajını görebiliriz . Bu bir sınıf değil; @objc protokolüne bir uzantı, protokol tanımının kendisinde (yani gereksinimlerde) olmakla aynı değildir ve "somut" kelimesi, bir protokol uzantısının somut bir sınıf uzantısı olarak sayılmadığını gösterir.

Geçici çözüm

Ne yazık ki, bu, varsayılan uygulamaların Objective-C çerçevelerine görünür olması gerektiğinde protokol uzantılarını kullanmanızı neredeyse tamamen engeller. İlk başta, @objcSwift Derleyici uygun türlerin sınıflar olacağını garanti edemeyeceği için protokol uzantınızda izin verilmediğini düşündüm (özellikle belirtmiş olsanız bile UIViewController). Bu yüzden bir classşart koydum P1. Bu işe yaramadı.

Belki de tek geçici çözüm, burada bir protokol yerine basit bir sınıf kullanmaktır, ancak bu kesinlikle ideal değildir çünkü bir sınıf yalnızca tek bir temel sınıfa sahip olabilir ancak birden çok protokole uyabilir.

Bu rotaya gitmeyi seçerseniz, lütfen bu soruyu ( Swift 3 ObjC İsteğe Bağlı Protokol Yöntemi Alt Sınıfta Çağrılmadı ) dikkate alın. Görünüşe göre Swift 3'teki diğer bir güncel sorun, alt sınıfların üst sınıflarının isteğe bağlı protokol gereksinimi uygulamalarını otomatik olarak devralmamasıdır. Bu soruların cevabı, @objcetrafından dolaşmak için özel bir uyarlama kullanır .

Sorunun Bildirilmesi

Bunun Swift açık kaynak projelerinde çalışanlar arasında zaten tartışıldığını düşünüyorum, ancak sonunda Swift Core Ekibine gidecek olan Apple'ın Bug Reporter'ını veya Swift'in hata muhabirini kullanarak onların farkında olduklarından emin olabilirsiniz . Ancak bunlardan herhangi biri, hatanızı çok geniş veya zaten biliniyor bulabilir. Swift ekibi, aradığınızı yeni bir dil özelliği olarak da düşünebilir, bu durumda önce posta listelerine göz atmalısınız .

Güncelleme

Aralık 2016'da, bu sorun Swift topluluğuna bildirildi . Sorun, orta öncelikli olarak hala açık olarak işaretlendi, ancak aşağıdaki yorum eklendi:

Bu amaçlanmıştır. Uzatma protokole uygunluktan sonra eklenebildiğinden, her evlat edene yöntemin uygulamasını eklemenin bir yolu yoktur. Sanırım uzantı protokol ile aynı modülde ise buna izin verebiliriz.

Protokolünüz uzantınız ile aynı modülde olduğu için, bunu Swift'in gelecekteki bir sürümünde yapabilirsiniz.

Güncelleme 2

Şubat 2017'de bu sayı , Swift Core Ekibi üyelerinden biri tarafından aşağıdaki mesajla "Yapmayacak" olarak resmen kapatıldı :

Bu bilinçlidir: protokol uzantıları, Objective-C çalışma zamanının sınırlamaları nedeniyle @objc giriş noktalarını tanıtamaz. NSObject'e @objc giriş noktaları eklemek istiyorsanız, NSObject'i genişletin.

Genişletmek, NSObjecthatta UIViewControllertam olarak istediğinizi başaramayacak ama maalesef mümkün olacak gibi görünmüyor.

(Çok) uzun vadeli bir gelecekte, @objcyöntemlere olan bağımlılığı tamamen ortadan kaldırabiliriz , ancak Cocoa çerçeveleri şu anda Swift ile yazılmadığından (ve kararlı bir ABI'ye sahip olana kadar olamaz) bu zaman muhtemelen yakın zamanda gelmeyecektir. .

Güncelleme 3

Sonbahar 2019'dan itibaren bu daha az sorun olmaya başladı çünkü Swift'de gittikçe daha fazla Apple çerçevesi yazılıyor. Örneğin, SwiftUIyerine kullanırsanız UIKit, problemden tamamen kaçarsınız çünkü @objcbir SwiftUIyönteme atıfta bulunurken asla gerekli olmayacaktır .

Swift ile yazılan Apple çerçeveleri şunları içerir:

  • SwiftUI
  • RealityKit
  • Birleştirmek
  • CryptoKit

Bu modelin zamanla devam etmesi beklenebilir, çünkü Swift resmi olarak ABI ve sırasıyla Swift 5.0 ve 5.1'den itibaren modül kararlı.


1
@ user1046037 Ben de yapıyorum, çünkü gelecekteki geliştirmelerde kendimi birçok kez bu sorunla karşılaştığımı görebiliyorum.
Matthew Seaman

2
Haklısınız, cevabınız Swift 4şu an için başka bir alternatif olmamasına rağmen hala geçerli .
user1046037

1
Bir süredir benim için çalışan tamamen aynı kod vardı, ancak daha sonraki bir Xcode sürümünde kırıldım. Bu oldukça rahatsız edici. Objective-C protokollerinde çok sayıda isteğe bağlı yöntem vardır.
Departamento B

0

Kullandığım hızlı bir çerçevede 'modül kararlılığını' etkinleştirdikten sonra ('Dağıtım için kitaplıklar oluştur' seçeneğini açtıktan sonra) bununla karşılaştım.

Sahip olduğum şey şuydu:

class AwesomeClass: LessAwesomeClass {
...
}

extension AwesomeClass: GreatDelegate {
  func niceDelegateFunc() {
  }
}

Uzantıdaki işlev şu hataları aldı:

  • 'LessAwesomeClass' alt sınıfının uzantısındaki '@objc' örnek yöntemi iOS 13.0.0 gerektirir

  • Non - '@ objc' yöntemi 'niceDelegateFunc', '@objc' protokolü 'GreatDelegate' gereksinimini karşılamıyor

İşlevlerin bir uzantı yerine sınıfa taşınması sorunu çözdü.


0

İşte başka bir geçici çözüm. Bu sorunla da karşılaştım ve henüz UIKit'ten SwiftUI'ye geçemiyorum. Varsayılan uygulamaları ortak bir temel sınıfa taşımak benim için de bir seçenek değildi. Varsayılan uygulamalarım oldukça kapsamlıydı, bu yüzden tüm bu kodun kopyalanmasını gerçekten istemedim. Sonlandırdığım geçici çözüm, protokolde sarmalayıcı işlevlerini kullanmak ve ardından her sınıftan bu işlevleri basitçe çağırmaktı. Hoş değil, ancak duruma bağlı olarak alternatif olandan daha iyi olabilir. Kodunuz daha sonra şuna benzer:

@objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}

extension P1 where Self : UIViewController {
    func wrapPresentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        return UIViewController()
    }
}

class A : UIViewController, P1 {
    func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        return wrapPresentationController(controller, viewControllerForAdaptivePresentationStyle: style)
    }
}
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.