Да, это фантастический протокол Transferable. Тоже работаю над статьей о Transferable. для своего блога. Но чтобы это оценить, надо понимать, что было до появления этого протокола, то есть до iOS 16+.
За операции Drag & Drop (перетаскивание и "сброс") отвечал класс class NSItemProvider. Именно этот класс делает очень сложные вещи, связанные с передачей данных между процессами. Он также решает проблемы с безопасностью и определенно там есть многопоточность, потому что мы не хотим блокировать UI двух приложений в случае передачи между ними больших по объему изображений.
К сожалению, класс NSItemProvider — это до-Swift класс, и мы должны использовать “as” как “мостик” между такими Swift ТИПами как String, и такими ТИПами старого NS Мира, как NSString, это Objective-C вещи, и “as” является “мостом” в этот старый мир. До появления протокола Transferable технология Drag & Drop (перетаскивание и сброс) была одним из таких мест соприкосновения "старого" и нового Миров.
Почти параллельно с вашей статьей появились две статьи об опыте использования протокол Transferable :
Единственное, до чего не "дотягивает" протокол Transferable и соответствующий ему View модификатор dropDestination(for:action:isTargeted:), это возможность указания нескольких UTType.
Класс Operation имеет метод cancel(), однако использование этого метода только устанавливает свойство isCancelled в true, а что семантически означает «удаление» операции можно определить только при создании subclass Operation. Например, в случае загрузки данных из сети можно определить cancel() как отключение операции от сетевого взаимодействия.
Посмотрите мою другую статью "Concurrency в Swift 3 и 4. Operation и OperationQueue" в Habr, специально посвященную Operation.
Пожалуй, действительно не стоит обсуждать эффективное хранение структур structтем более в Standard Library.
В документации по Swift вовсе не сказано напрямую, что оптимизация хранения структур struct типа массивов Array, словарей Dictionary и строк String предполагает использование "кучи" (heap), там лишь сказано, что используется оптимизация для уменьшения затрат на копирование. Вместо того, чтобы делать копию немедленно, эти коллекции совместно используют память, в которой хранятся элементы, между исходным экземпляром и любыми копиями. Если одна из копий коллекции изменяется, элементы копируются непосредственно перед изменением.
Так что Apple оставляет за собой право, как она будет оптимизировать хранение структур struct, может и изменить это в любой момент времени.
Для нас важно лишь то, что в нашем коде ВСЕГДА у структуры struct будет такое поведение при изменении, как если бы копия была создана немедленно.
По большому счету структуры structявляются "неизменяемыми" (immutable) структурами данных в отличие от классов class.
Именно на основе того, что при любом изменении структуры structмы всегда имеем её новый экземпляр,и строится функциональное программирование.
Это особенно видно в новом фреймворке SwiftUI, в котором архитектура строится на основе MVVM, где View - всегда структуры struct, а ViewModels - классы class. Программирование в SwiftUI - это функциональное программирование.
Вы сами задаете интервал обновления.
А в fetch() уже работает @Published, а точнее CurrentValueSubject<Set, Never>([]), но это близко к @Published, и это воздействует на View SwiftUI.
У меня нет опыта работы с RxSwift, но многие отмечают, что хотя Combine пока по функциональности уступает RxSwift, он существенно превосходит RxSwift по быстродействию. Да это и понятно, ведь RxSwift написан поверх Swift, а Combine — ниже Foundation.
Лично меня поразило быстродействие связки Combine+ SwiftUI в задаче выборки данных о фильмах и их кинопостерах, что относится к ресурсо- затратным задачам. В том примере, который приведен в этой статье, при переходе от одной коллекции фильмов ( от тех, которые сейчас на экране, к популярным), выборка данных происходит столь быстро, что даже нет необходимости в экране «Loading...». «Сто лет» занимаюсь этими задачами и Combine+ SwiftUI преподнесли мне приятный сюрприз.
Да, я знаю, что одним из критериев конкурса было быстродействие.
Я конечно, не замеряла, но быстродействие очень хорошее.
Те, кто специально этим занимался отмечают существенное быстродействие по сравнению с UIKit.
Что касается навигации, то есть одно очень хорошее решение «Recreation of calculator-checklist project in SwiftUI», где очень просто воссоздается CustomSegue:
В Swift массивы Array являются структурами struct и Value ТИПАМИ. В Swift Value ТИПЫ хранятся в стеке.
Каждый поток имеет свое собственное пространство стека, поэтому никакой другой поток не сможет напрямую получить доступ к вашему Value ТИПУ. Следовательно, нет условий для гонки, блокировок, взаимных блокировок или каких-то других сложностей, связанных с синхронизацией потоков.
Если массив Array — очень большой, то для них использует механизм «Copy-on-Write».
Когда вы изменяете (например, добавляете append элементы) массив Array, он использует стандартную библиотечную функцию «isUniquelyReferenced», чтобы увидеть, есть ли какие-либо другие массивы, указывающие на буфер, к которому он собирается добавить. Если они есть, он делает копию, и копия становится ее новым резервным хранилищем. Это называется «Copy-on-Write».
Конечно, в императивном коде вы можете каждый раз создавать новый массив. Даже более того, если вы посмотрите на все эти map, flatMap, filter , то увидите, что именно так они и реализованы, никакой магии тут нет.
Но если вы находитесь в пределах императивного кода, то вам самому нужно все время контролировать себя, не делаете ли вы каких-то изменений «по месту».
Использование же функционального кода вам точно гарантирует «неизменяемость» за счет создания новых массивов ( или последовательностей или еще чего-нибудь) в функциях «высшего порядка».
Теперь map перевыбрасывает (rethrows) ошибку.
Но во всех постах очень известного сайта по функциональному программированию на Swiftpointfree.com во всех map перед transform стоит @escaping.
Но, похоже, вы правы и наличие @escaping сейчас является артефактом с прошлой версии.
Исправим.
По условиям задачи из массива [6, 22, 8, 14, 16, 0, 7, 9]:
— удаляется элемент с индексом 2, то есть 8
— удаляется элемент с индексом 5, то есть 0
— удаляется элемент с индексом 0, то есть 6
— удаляется элемент с индексом 6, то есть 7
И остается [22, 14, 16, 9]
Было восемь элементов, удалили четыре — осталось четыре
Считает все правильно — посмотрите на Playground.
Одна из причин использования @escaping — асинхронное выполнение кода. Если вы используете замыкание асинхронно с помощью DispatchQueгу, то очередь queue будет удерживать ваше замыкание в памяти для того, чтобы использовать его в будущем. В этом случае вы не можете точно сказать, когда замыкание будет выполнено. Все Generic map имеют @escaping замыкание, так как заранее неизвестно в какой среде вы будете его использовать.
Согласна, статья получилась немного затянутой, но хотелось рассказать что-то более интересное, чем просто получение четных чисел с помощью filter(_:) или суммы чисел с помощью reduce(_:,_;).
Что касается Codable, то это АНТИпример того, куда может «завести» функциональный подход, например, для парсинга JSON. К сожалению, я не привела код, но это что-то типа:
Самостоятельно введенные операторы «композиции» функций сильно грузят систему вывода ТИПА из контекста (inference) и очень затягивают время компиляции.
Насколько же было гениальмым решение Codable по сравнению с функциональным подходом.
Я всегда имею ввиду этот пример при выборе решения в пользу протоколов.
Как превратить единственный SwiftUI контейнер в dropDestination для нескольких Transferable типов?
Исправлено. Repository был private
Meet Transferable
Да, это фантастический протокол Transferable. Тоже работаю над статьей о Transferable. для своего блога. Но чтобы это оценить, надо понимать, что было до появления этого протокола, то есть до iOS 16+.
За операции Drag & Drop (перетаскивание и "сброс") отвечал класс class NSItemProvider. Именно этот класс делает очень сложные вещи, связанные с передачей данных между процессами. Он также решает проблемы с безопасностью и определенно там есть многопоточность, потому что мы не хотим блокировать UI двух приложений в случае передачи между ними больших по объему изображений.
К сожалению, класс NSItemProvider — это до-Swift класс, и мы должны использовать “as” как “мостик” между такими Swift ТИПами как String, и такими ТИПами старого NS Мира, как NSString, это Objective-C вещи, и “as” является “мостом” в этот старый мир. До появления протокола Transferable технология Drag & Drop (перетаскивание и сброс) была одним из таких мест соприкосновения "старого" и нового Миров.
Почти параллельно с вашей статьей появились две статьи об опыте использования протокол Transferable :
First Experience With Transferable Implementing Drag And Drop In SwiftUI
Transferable Protocol in SwiftUI – Transferring Alternative Content With ProxyRepresentation
Единственное, до чего не "дотягивает" протокол Transferable и соответствующий ему View модификатор dropDestination(for:action:isTargeted:), это возможность указания нескольких UTType.
Раньше, до iOS 16+:
Теперь, начиная с iOS 16+:
Но выход есть:
Многопоточность (concurrency) в Swift 3. GCD и Dispatch Queues
Класс
Operation
имеет методcancel()
, однако использование этого метода только устанавливает свойствоisCancelled
вtrue
, а что семантически означает «удаление» операции можно определить только при созданииsubclass Operation
. Например, в случае загрузки данных из сети можно определитьcancel()
как отключение операции от сетевого взаимодействия.Посмотрите мою другую статью "Concurrency в Swift 3 и 4. Operation и OperationQueue" в Habr, специально посвященную
Operation.
https://habr.com/ru/post/335756/
По-моему там есть пример.
MVVM на основе Combine в UIKit и SwiftUI приложениях для UIKit разработчиков
По сути, assign(to:) - это прямая передача публикуемого значения другому "издателю" Publisher $currentWeather и никакой reference cycle не возникает.
MVVM на основе Combine в UIKit и SwiftUI приложениях для UIKit разработчиков
Верно. Вы можете использовать вместо этого
Или использовать новый в iOS 14 метод https://developer.apple.com/documentation/combine/fail/assign(to:)
код стал еще проще.
Немного практики функционального программирования в Swift для начинающих
Пожалуй, действительно не стоит обсуждать эффективное хранение структур
struct
тем более в Standard Library.В документации по Swift вовсе не сказано напрямую, что оптимизация хранения структур
struct типа
массивовArray
, словарейDictionary
и строкString
предполагает использование "кучи" (heap), там лишь сказано, что используется оптимизация для уменьшения затрат на копирование. Вместо того, чтобы делать копию немедленно, эти коллекции совместно используют память, в которой хранятся элементы, между исходным экземпляром и любыми копиями. Если одна из копий коллекции изменяется, элементы копируются непосредственно перед изменением.Так что Apple оставляет за собой право, как она будет оптимизировать хранение структур
struct
, может и изменить это в любой момент времени.Для нас важно лишь то, что в нашем коде ВСЕГДА у структуры
struct
будет такое поведение при изменении, как если бы копия была создана немедленно.По большому счету структуры
struct
являются "неизменяемыми" (immutable) структурами данных в отличие от классовclass
.Именно на основе того, что при любом изменении структуры
struct
мы всегда имеем её новый экземпляр,и строится функциональное программирование.
Это особенно видно в новом фреймворке SwiftUI, в котором архитектура строится на основе
MVVM
, гдеView
- всегда структурыstruct
, аViewModels
- классыclass
. Программирование в SwiftUI - это функциональное программирование.Реализация Grid раскладки на SwiftUI
Впечатление от Стэнфордских курсов CS193P Весна 2020 г.: Разработка iOS приложений с помощью SwiftUI
Вы сами задаете интервал обновления.
А в fetch() уже работает @Published, а точнее CurrentValueSubject<Set, Never>([]), но это близко к @Published, и это воздействует на View SwiftUI.
Многопоточность (concurrency) в Swift 3. GCD и Dispatch Queues
API для удаленной асинхронной выборки с помощью Apple Combine
Лично меня поразило быстродействие связки Combine+ SwiftUI в задаче выборки данных о фильмах и их кинопостерах, что относится к ресурсо- затратным задачам. В том примере, который приведен в этой статье, при переходе от одной коллекции фильмов ( от тех, которые сейчас на экране, к популярным), выборка данных происходит столь быстро, что даже нет необходимости в экране «Loading...». «Сто лет» занимаюсь этими задачами и Combine+ SwiftUI преподнесли мне приятный сюрприз.
API для удаленной асинхронной выборки с помощью Apple Combine
Problem Solving with Combine Swift
API для удаленной асинхронной выборки с помощью Apple Combine
SwiftUI для прошлого конкурсного задания Telegram Charts (март 2019 года): все просто
Я конечно, не замеряла, но быстродействие очень хорошее.
Те, кто специально этим занимался отмечают существенное быстродействие по сравнению с UIKit.
Что касается навигации, то есть одно очень хорошее решение «Recreation of calculator-checklist project in SwiftUI», где очень просто воссоздается CustomSegue:
Немного практики функционального программирования в Swift для начинающих
Array
являются структурамиstruct
иValue
ТИПАМИ. В SwiftValue
ТИПЫ хранятся в стеке.Каждый поток имеет свое собственное пространство стека, поэтому никакой другой поток не сможет напрямую получить доступ к вашему
Value
ТИПУ. Следовательно, нет условий для гонки, блокировок, взаимных блокировок или каких-то других сложностей, связанных с синхронизацией потоков.Если массив Array — очень большой, то для них использует механизм «Copy-on-Write».
Когда вы изменяете (например, добавляете append элементы) массив
Array
, он использует стандартную библиотечную функцию «isUniquelyReferenced
», чтобы увидеть, есть ли какие-либо другие массивы, указывающие на буфер, к которому он собирается добавить. Если они есть, он делает копию, и копия становится ее новым резервным хранилищем. Это называется «Copy-on-Write».Немного практики функционального программирования в Swift для начинающих
map
,flatMap
,filter
, то увидите, что именно так они и реализованы, никакой магии тут нет.Но если вы находитесь в пределах императивного кода, то вам самому нужно все время контролировать себя, не делаете ли вы каких-то изменений «по месту».
Использование же функционального кода вам точно гарантирует «неизменяемость» за счет создания новых массивов ( или последовательностей или еще чего-нибудь) в функциях «высшего порядка».
Немного практики функционального программирования в Swift для начинающих
Apple
map
дляSequence
сейчас действительно не имеет@escaping
(хотя раньше имела):func map(_ transform: (Element) throws -> T) rethrows -> [T]
перевыбрасывает (Теперь map
rethrows
) ошибку.Но во всех постах очень известного сайта по функциональному программированию на
Swift
pointfree.com во всехmap
передtransform
стоит@escaping
.Но, похоже, вы правы и наличие
@escaping
сейчас является артефактом с прошлой версии.Исправим.
Немного практики функционального программирования в Swift для начинающих
— удаляется элемент с индексом 2, то есть 8
— удаляется элемент с индексом 5, то есть 0
— удаляется элемент с индексом 0, то есть 6
— удаляется элемент с индексом 6, то есть 7
И остается [22, 14, 16, 9]
Было восемь элементов, удалили четыре — осталось четыре
Считает все правильно — посмотрите на Playground.
Немного практики функционального программирования в Swift для начинающих
Немного практики функционального программирования в Swift для начинающих
@escaping
— асинхронное выполнение кода. Если вы используете замыкание асинхронно с помощьюDispatchQueгу
, то очередьqueue
будет удерживать ваше замыкание в памяти для того, чтобы использовать его в будущем. В этом случае вы не можете точно сказать, когда замыкание будет выполнено. ВсеGeneri
cmap
имеют@escaping
замыкание, так как заранее неизвестно в какой среде вы будете его использовать.Немного практики функционального программирования в Swift для начинающих
filter(_:)
или суммы чисел с помощьюreduce(_:,_;)
.Что касается
Codable
, то это АНТИпример того, куда может «завести» функциональный подход, например, для парсинга JSON. К сожалению, я не привела код, но это что-то типа:blog <*> int(dict,"id")
<*> string(dict,"name")
<*> bool(dict,"needspassword")
<*> (string(dict, "url") >>= toURL
Самостоятельно введенные операторы «композиции» функций сильно грузят систему вывода ТИПА из контекста (inference) и очень затягивают время компиляции.
Насколько же было гениальмым решение
Codable
по сравнению с функциональным подходом.Я всегда имею ввиду этот пример при выборе решения в пользу протоколов.