Я случайно увидел это на боковой панели, у меня недавно была такая же проблема. К сожалению, из-за ограничений времени выполнения Objective C вы не можете использовать @objc для расширений протокола, я думаю, что эта проблема была закрыта в начале этого года.
Проблема возникает из-за того, что расширение добавляется после соответствия протокола, поэтому нет способа гарантировать, что соответствие протоколу будет выполнено. Тем не менее, можно вызывать метод как селектор из всего, что подклассов NSObject и соответствует протоколу. Чаще всего это делается с делегированием.
Это означает, что вы можете создать пустой подкласс обертки, соответствующий протоколу, и использовать обертку для вызова его методов из протокола, определенного в обертке, любые другие неопределенные методы из протокола могут быть переданы в делегировать. Существуют другие аналогичные решения, которые используют частное расширение конкретного класса, например UIViewController, и определяют метод, который вызывает метод протокола, но они также связаны с конкретным классом, а не реализацией по умолчанию конкретного класса, который происходит с соответствуют протоколу .
Поймите, что вы пытаетесь реализовать реализацию протокола по умолчанию, которая использует другую из своих собственных функций протокола, чтобы определить значение для своей собственной реализации. гмм!
Протокол:
public protocol CustomViewDelegate {
func update()
func nonDelegatedMethod()
}
Представление:
Использование делегата и определение метода обертки для безопасного развертывания метод делегата.
class CustomView: UIView {
let updateButton: UIButton = {
let button = UIButton(frame: CGRect(origin: CGPoint(x: 50, y: 50), size: CGSize(width: 150, height: 50)))
button.backgroundColor = UIColor.lightGray
button.addTarget(self, action: #selector(doDelegateMethod), for: .touchUpInside)
return button
}()
var delegate:CustomViewDelegate?
required init?(coder aDecoder: NSCoder) {
fatalError("Pew pew, Aghh!")
}
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(updateButton)
}
@objc func doDelegateMethod() {
if delegate != nil {
delegate!.update()
} else {
print("Gottfried: I wanted to be a brain surgeon, but I had a bad habit of dropping things")
}
}
}
ViewController:
Согласуйте контроллер представления с представителем представления: и реализуйте метод протокола.
class ViewController: UIViewController, CustomViewDelegate {
let customView = CustomView(frame: CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 200, height: 200)))
override func viewDidLoad() {
super.viewDidLoad()
customView.backgroundColor = UIColor.red
customView.delegate = self //if delegate is not set, the app will not crash
self.view.addSubview(customView)
}
// Protocol -> UIView Button Action -> View Controller's Method
func update() {
print("Delegating work from View that Conforms to CustomViewDelegate to View Controller")
}
//Protocol > View Controller's Required Implementation
func nonDelegatedMethod() {
//Do something else
}
}
Обратите внимание, что контроллер представления должен был соответствовать только делегату и не устанавливал селектор какого-либо свойства представления, это отделяет представление (и его протокол) от контроллера представления.
У вас уже есть UIView с именем TapView, который наследует от UIView и Tappable, поэтому ваша реализация может быть:
Протокол:
protocol TappableViewDelegate {
func tapGestureDetected(gesture:UITapGestureRecognizer)
}
TappableView:
class TappableView: UIView {
var delegate:TappableViewDelegate?
required init?(coder aDecoder: NSCoder) {
fatalError("Pew pew, Aghh!")
}
override init(frame: CGRect) {
super.init(frame: frame)
let gesture = UITapGestureRecognizer(target: self, action: #selector(doDelegateMethod(gesture:)))
addGestureRecognizer(gesture)
}
@objc func doDelegateMethod(gesture:UITapGestureRecognizer) {
if delegate != nil {
delegate!.tapGestureDetected(gesture: gesture)
} else {
print("Gottfried: I wanted to be a brain surgeon, but I had a bad habit of dropping things")
}
}
}
ViewController:
class ViewController: UIViewController, TappableViewDelegate {
let tapView = TappableView(frame: CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 200, height: 200)))
override func viewDidLoad() {
super.viewDidLoad()
tapView.backgroundColor = UIColor.red
tapView.delegate = self
self.view.addSubview(tapView)
}
func tapGestureDetected(gesture: UITapGestureRecognizer) {
print("User did tap")
}
}
Spring сам дает интерфейсам общие имена, а затем имена классов на основе деталей реализации. Это один из примеров, который приходит на ум:
interface: Controller
abstract classes: AbstractController, AbstractCommandController,
SimpleFormController, MultiActionController
Я не думаю, что такие имена, как SimpleUserAccountManager или UserAccountDbManager, являются глупыми, поскольку они передают некоторую информацию о реализации менеджера / службы.
Я считаю глупым обычное соглашение чтобы добавить суффикс «Impl» в классы реализации:
my/package/UserAccountManager
my/package/impl/UserAccountManagerImpl
Некоторые люди предпочитают это.
Я считаю, что суффикс именования службы vs. Manager является чисто предпочтительным. Единственный случай, когда «Сервис» когда-либо нас смущал, - это когда у нас также есть веб-сервисы, расположенные на вершине нашего сервисного уровня. В некоторых проектах мы просто называли классы веб-сервисов посредниками, поскольку все, что они делали, это служили для преобразования или посредничества вызова веб-сервиса в вызовы нашего уровня сервиса.
Я согласен с kgiannakakis, который добавлял к вашим реализациям суффикс «Impl "не лучший подход. Я также сталкивался с лучшими практиками кодирования, в которых упоминается, что этого нельзя делать. Называть интерфейс после абстракции - это общепринятая передовая практика. Называя класс реализации после интерфейса с некоторым индикатором его назначения или типа, как предложил Кгианнакакис, кажется общепринятым подходом.
Когда у нас есть DAO на основе веб-сервисов и DAO на основе ORM, мы используем и пакеты, и имена классов, чтобы отличать классы реализации от их интерфейсов и друг от друга. Я думаю, что размещение реализаций в разных пакетах сводится к тому, сколько классов у вас есть в пакете, насколько по-разному они реализованы и насколько вы хотите разделить вещи.
Вот что мы используем:
Для реализации мы добавляем суффикс Impl (XxxServiceImpl), чтобы отличить его от интерфейса, и если существует несколько реализаций или мы хотим добавить дополнительную информацию, мы добавляем его как префикс (JdbcXxxDaoImpl, GoogleMapsGeocodingServiceImpl и т. Д.). Имена классов в этом случае становятся немного длинными, но они очень информативны и самодокументируются.
В приведенном вами примере я бы использовал имена реализации, которые отражают то, как класс выполняет операции, например HibernateUserAccountManager, или JPAUserAccountManager, или JDBCUserAccountManager и т. Д., Или, возможно, просто UserAccountManagerDAO.
Вы также можете назвать интерфейс IUserAccountManager (это соглашение используется, например, в Eclipse RCP), а затем использовать UserAccountManager для реализации по умолчанию.