Comments 23
Что selector'ов нет у всего, что не наследуется от NSObject, это, конечно, плохо, согласен. И что в protocol теперь только обязательные функции объявлять можно.
Но с другой стороны, какой ещё язык программирования позволяет запретить передавать пустые указатели в compile time? Насколько знаю, optional в Java — решение лишь отчасти, позволяет лишь приделать костыль. А какой ещё язык программирования будет следить вот так:
class Something {
var someURL: URL
var someData: Data//допустим, это добавли позже
init(someURL: URL) {
self.someURL = someURL//нет, не компилируется, забыть инициализровать someData не получится
}
}
Ещё очень полезная вещь — enum с параметрами.
enum SomeResult {
case valid(data: Data)
case failure(error: Error)
}
Результат и ошибка одновременно не получатся никак. А ещё, в отличие от Java, структуры объявлять можно, и управлять их изменяемостью. Где ещё так? Хоть недостатков у Swift тоже немало, например, нет объявления атрибутов.
— Нативная для ObjC runtime
— Нативная для Swift (`Mirror(for: <..>)`)
— Стороняя реализация: github.com/Zewo/Reflection
— KeyPath? (который как бы и не совсем рефлексия, так как его нельзя полноценно создавать в рантайме)
Но ни одна из реализаций не является полноценной (и даже в сумме они не покрывают всех вариантов):
ObjC runtime работает только с ObjC типами (не обязательно с NSObject, но с теми, у которых есть указатель isa)
Mirror может только читать почти всё (за редкими, но важными, исключениями, вроде Enum)
Библиотека Reflection может читать и писать, но только структуры
KeyPath — такая странная хрень, которая таки дёргает внутреннее апи, никак не доступное рядовому разработчику, но делает это бесполезно: его можно создать либо из ObjC селектора, либо синтаксическим сахаром, который надо явно написать.
И ничего из этого не может писать любые поля класса, не транслируемого так или иначе в ObjC runtime и любые поля Swift типа в любом классе. В общем не быть типу int? использованным в динамическом json маппинге.
> Но с другой стороны, какой ещё язык программирования позволяет запретить передавать пустые указатели в compile time?
Вроде бы как в Kotlin есть такое (впрочем, я никогда не писал на этом языке).
По поводу enum с ассоциированными значениями… Во первых, это наверняка можно реализовать без Enum (не пробовал, в голову приходят абстрактный тип с наследниками), во вторых лично мне не нравится такой подход из-за того, что он провоцирует божественный метод-свалку c switch..case, в который запихнуто всё.
Ваш конкретный пример — я бы посоветовал посмотреть на RxSwift или ReactiveX (реализации Reactive паттерна для Swift). К тому же иногда таки нужно передать и данные, и ошибку: «запрос к серверу не выполнен и вот актуальное состояние», например. В прочем, создать кастомный Error с полем данных.
Если бы всё было так просто… В Swift есть ТРИ (четыре?) разных вида рефлексии
Именно об этом я и упомянул кратко. Для CoreData всё равно нужен NSObject, так что там можно через selector'ы делать. Для JSON и впрямь ситуация оставлят желат лучшего, тем более, что объявления атрибутов нет, в Java и C# обычно ими и пользуются.
Во первых, это наверняка можно реализовать без Enum (не пробовал, в голову приходят абстрактный тип с наследниками)
Тогда в абстрактный может пойти лишь статус, ибо если объявить метод getError
, в наследуемом классе его уже не спрятать, хотя, Java (в отличие от C#) позволяет protected метод сделать public. Но даже если так, это надо знать, к какому типу данных приводить в каждом случае, а если кто-то захотел что-то поменять в этих классах, да ещё так, что где-то возвращает null
...
во вторых лично мне не нравится такой подход из-за того, что он провоцирует божественный метод-свалку c switch..case, в который запихнуто всё
Любую полезную возможность можно использовать во вред, что уж говорить...
Ваш конкретный пример — я бы посоветовал посмотреть на RxSwift или ReactiveX
Ну это, наверно, неудачный пример. Добавляем loading
, recovering
и прочее, и уже не всегда будет нужная реализация. Кстати, RxSwift результат выражает именно через enum с параметрами.
Но с другой стороны, какой ещё язык программирования позволяет запретить передавать пустые указатели в compile time?
Возможно я не понимаю что имеется ввиду но разве в Kotlin этого нет? Если тип не nullable — передать туда null (или даже возможность передать его) — ошибка (да, есть способы выстрелить себе в ногу, если оно хочется). kotlinlang.org/docs/reference/null-safety.html например.
Я допускаю, что Kotlin и, может, и ещё какой-то язык программирования это умеют. Я не знаток Kotlin, но слышал, что он основан на JVM, в котором, если не путаю, не предусмотрено возможности объявления структур. На мой взгляд, это большое упущение, а вот в каком языке программирования эти возможности одновременно присутствуют? К тому ж, не знаю насколько так, но слышал, что в Kotlin ключевое слово throws
не является обязательным. Если это так, по мне это плохо. Кстати, я всегда это называл главным недостатком C#.
и nullable reference типы добавили недавно
Хорошо, если наконец-то добавили. А то для Singularity, пусть в немного другом виде, оно сколько лет было, а в массы не шло. Несколько лет было, что в Swift возможность уже была, а в C# — когда-нибудь.
Но вот если б в C# ещё б аналогичным образом ввели возможность делать обязательным throws
— вот это было б дело, а то в нём часто приходится на всякий случай ставить catch
на всё, ибо типичная реальная ситуация с документацией хорошо известна. Конечно, у C# со структурами всегда были неудобства в плане их изменяемости. Но по сравнению с Swift как минимум преимущество — возможность объявления атрибутов. Классический синтаксис мне всё ж больше нравится. Ну и с пространствами имён удобнее, хотя с ними плохо стыкуются typealias
/using ... =
. Ну и PLINQ — преимущество C#, Swift для параллельной обработки ничего лучше closure пока не предлагает.
Значит это было ошибкой проектирования. На крайний случай можно поставить такую заглушку:
func SomeUnsafeFunction() throws {
//..
if (something) {
throw Errors.someError
}
}
func SomeOtherFunction() {
do {
try SomeUnsafeFunction()
}
catch {
print("That must be implemented correctly after the urgent release")
}
}
Конкретно в этом примере хотелось обработать ошибку в одном конкретном месте и крашить приложение в других.
Вообще, в Swift, особенно начиная с 5-го, эти exceptions и вовсе не нужны. Это больше обратная совместимость и возможность унификации с другими средами, как RxSwift. Для сравнения, в ReactiveSwift, который отходит от этого стандарта, вообще никакого throw
нет. А есть тип данных Result
, в котором как раз enum с параметрами. В 5-м Swift этот тип данных вошёл в стандартный набор. Но где уже всё обросло throw
, приходится находить способы адаптации.
Кто-то еще замечал такое?
Swift 5.2 — что нового?