All streams
Search
Write a publication
Pull to refresh
18
0
Кирилл @ws233

Руководитель мобильной разработки

Send message

А можно чуть более подробно, почему в UDF не нужны протоколы? В статье об этом дважды, но оба раза просто констатация факта, без пояснений. Причем, во втором случае написано, что есть проблема, которую бы могли решить протоколы (связность и абстракции), но приходится почему-то искать другие способы.

И в продолжение нашего разговора в предыдущей статье, кажется, что после разбиения доменной логики на независимые кусочки, каждый такой кусочек вправе обладать своим собственным состоянием. А состояния разных кусочков агрегируются медиатором. Медиатор самого высокого уровня будет иметь общее единое состояние, если оно кому-то будет нужно.

Спасибо. Статья интересная. Ждем продолжения.

ну, если вы понимаете это, то вам не составит труда и разделять стейты и разносить их по зонам ответственности. Тогда и не придется столько сопутствующих проблем решать. Но хозяин -- барин, как говорится.

Тут еще такие мысли. Кажется, что при переходе не реактивное слежение за сигналами, читать явно значения переменных не стоит. Т.е. не стоит смешивать активное и реактивное программирование. Если уж у вас везде реакции на изменения, то убрав явное чтение значений в каждый конкретный момент, Вы исключаете саму возможность прочитать неверное состояние. А сигналы рано или поздно дойдут до всех подписчиков в актуальном состоянии.

Кажется, что единый Стейт все же не решает проблемы многопоточности, ну вот никак. Даже с единым стейтом вы все равно не застрахованы, что ваше приложение не окажется в конкретный момент времени в непредусмотренном состоянии. Даже с единым стейтом, система в любой момент времени может перекинуть вас на другой поток. Даже во время вашего перехода из одного стейта в другой, пока этот самый Стейт еще не обновился полностью. И вы получите тот же самый рассинхрон, которого Вы так опасаетесь. Проблему мог бы решить единственный поток, в котором и происходят все смены состояний и их чтения, но мы прекрасно знаем, что в iOS работать в одном единственном потоке практически невозможно. А значит, единый Стейт для Вас - это лишь попытка себя обмануть. Кажется, что единственный возможный выход из данной проблемы - это правильная работа в многопоточной среде с атомарными записями и чтениями состояния. И тут уже не важно, единый у вас Стейт или распределенный, но обеспечивающий атомарность записи и чтения. Тем более, что Вы тоже согласны, что Стейт в Redux не обязан быть единым. Кажется, что единый Стейт обеспечивал бы те плюсы, что Вам нужны, в каком-то другом программерском мире, но не в иосном. Возможно, я ошибаюсь.

Подскажите, а почему State единый и такой массивный? Чем это обусловлено? Кажется, что если просто поделить один божественный State на кучу мелких, но подписывающихся друг на друга, большая часть ваших проблем, если не все, уйдут. Тот же профиль вы выделяете в отдельную сущность, за которой потом следит тот, кому нужен профиль, без слежения за всем "богом". Кажется, что в Redux не должно быть ограничения на то, что состояние обязано быть одним. Но даже если и так, можно же завести под каждый UI-компонент свой фасад, который будет под собой объединять по нужной Вам логике отдельные независимые стейты. Кажется, что корень проблем модуляризации у Вас -- это попытка затянуть God-объект, связывающий в большой клубок все ваще приложение, в модульную архитектуру. Кажется, что так не выйдет. Модуляризация подразумевает в том числе разнесение модели и стейта на независимые куски.

В чем на Ваш взгляд заключается усложнение?

Портянкой нет. А вот пояснить выбор именно такого решения даже в атомарном коммите нужно. Иначе, появятся вопросы про оверхед или "я бы сделал вот так". А потом долгая переписка о том, почему же все-таки было выбрано именно такое решение. Вроде всем было бы проще, если бы даже атомарный коммит сразу ответил на вопрос "Почему?".

Спасибо. Снова хорошие мысли.

Вопрос контроля. Не смотрел на проблемы собственных реализаций с этой стороны. А ведь действительно. Многие могут реализовывать свои решения потому, что не понимают, как управлять тем, что есть у Apple. Что касается управления кешом через dataTask, так я, наверное, и не знаю-то, как это делается в деталях ^.^ Вы тоже натолкнули меня на хорошие мысли, спасибо за это.

Ну, и желаю Вам опубликовать статью по итогам исследований нативного кеширования. Материала должно набраться много и очень толкового.

Снова спасибо. Опять хороший ответ.

Итак, зафиксируемся:
1. dataTask умеет кешировать запросы из коробки
2. dataTask можно использовать для загрузки небольших изображений. Нигде явного лимита не указано. И лимит этот косвенно можно выявить из других признаков и рассуждений (хоть и не тех, что вы привели, по моему мнению, но об этом уже не будем).
вы с этим согласны.

Но вы опасаетесь, что dataTask сохраняет ВСЮ загруженную информацию сразу в память, что по Вашему мнению может привести к закрытию приложения системой. Вы, видимо, зацепились за первое предложение из вот этой ссылки, что сами и предоставили.

Ок, давайте разбираться.

Мы же с Вами уже разобрались, что dataTask кешурет данные. Т.е.они все же складываются на диск. А раз они складываются на диск, то с диска же они и читаются в оперативную память. Так? Уверен, что так.

Давайте теперь разберемся, а как же они читаются? А читаются они стандартным для iOS способом, применимым повсюду. Через NSData. В этом можно убедиться в конкретном примере, посмотрев тип данных первого параметра замыкания, вызываемого в результате выполнения dataTask.

Что ж открываем документацию, читаем:

The size of the data is subject to a theoretical limit of about 8 exabytes (1 EB = 10¹⁸ bytes; in practice, the limit should not be a factor).

Переведу и поясню. Теоретически, размер буфера в NSData ограничен 10ю в 18й степени (безумное число), а практически, вы не должны заботиться об этом лимите. Все работает для вас с такими объемами.

Как Вы думаете, как Apple удалось такого достичь на мобильных телефонах с гораздо-гораздо меньшим объемом оперативной памяти?

Все верно! Буфер NSData хранится на диске, а не в оперативной памяти. Это очень легко проверить, открыв какой-нить фильмец на пару гигабайт размером через NSData.

Таким образом, первое предложение, за которое вы зацепились, значит не что иное, как то, что данные вам возвращаются в виде объекта NSData, с которым вы можете работать, как-будто они находятся в оперативной памяти. Вам просто не нужно это делать самостоятельно, как если бы вы работали с downloadTask.

Что же касается замечания о том, что надо конструировать UIImage. Так он (как и Image в Свифт) работает поверх NSData все с тем же механизмом. Я это четко осознал, когда бил на тайлы 100МБ jpeg военную карту Москвы на 4S. Просто прикиньте, какой размер эта карта занимает попиксельно после декодирования jpeg. И тем не менее данная карта прекрасно отображалась на экранчике мобильного телефона. Правда, плохо масштабировалась, что я позже и решал тайлингом.

Таким образом, ваши опасения о том, что dataTask истратит всю вашу оперативную память, выглядят сомнительно. В Apple не глупые люди сидят и все для вас они уже сделали. Смысла повторять их работу я пока не увидел.

Есть еще аргументы для меня и сообщества в пользу ручного кеширования и повторения работы Apple самостоятельно вместо того, чтобы взять коробочное решение?

Хорошее пояснение, спасибо. А есть у Вас ссылки соответсвующие? И еще вопрос, а где-то указан этот барьер между небольшим и большим количеством данных? Бывают же и джейсоны на пару мегабайт, и картинки на сотни или даже десятки килобайт. Т.е.похоже, что тут не столько на размер надо обращать внимание, сколько на применимость как раз. Т.е.если нужен Кеш, пожалуйста, бери себе dataTask, нет? Ну, и если я буду использовать dataTask, а не downloadTask, Apple может зареджектить? Есть у вас чуть больше инфы на эту тему? Сам хочу разобраться.

Потому что это работает и на ObjC. Если внимательно присмотритесь, то команды дебагеру даются и на ObjC тоже. Собственно, от языка это не зависит. На мой взгляд, изначальная реализация кода никак не влияет на то, о чем говорится в статье, и ребятам, пишущим на ObjC, это будет не менее полезно. Извините, если это потревожило вашу ленту и вам это не нужно.

Но ведь NSURLSession уже умеет работать с NSURLCache. И на самом деле городить свое собственное кеширование смысла нет. Достаточно правильно настроить кеширование запросов сессии (с обеих сторон: клиентской и серверной) и тупо дергать каждый раз запрос -- первый раз он скачает инфу с сервера, дальше будет брать из кеша, пока этот Кеш не почистится системой или не инвалидируется. просто и легко.

Спасибо, за развернутый ответ. Ждем больше "бизнес" характеристик -- техническая часть раскрыта достаточно глубоко :)

Подскажите, пожалуйста, а чем вызван такой высокий уровень абстракции? Кажется, что дерево контроллеров задается один раз и меняется довольно редко. А если и меняется, то все равно не целиком, а лишь переносятся целые ветки с места на место. В таком случае менять конкретный роутер не так уж и сложно. А с такой высокоуровневой абстракцией повышается и порог входа и стоимость реализации, а ради чего все? Да, вы указали Ваши требования к идеальному роутингу, но что стоит за ними? Какого причины такого усложнения?

Думаю, с этой информации и стоило начинать статью, чтобы помочь читателю правильно выбрать инструмент.

Евгений, по прошествии 3х лет что бы Вы могли добавить по теме геймификации рабочего процесса?

Наигрались ли ребята?

Какие проблемы возникли, если возникли?

Расширили ли Вы практику на большее число команд или практика умерла сама собой?

Перенял ли ее кто-то из коллег-руководителей? Какой был у них опыт? С чем они столкнулись? Взлетело ли у них?

А пока можно, например, пофантазировать, как бы этот код мог выглядеть (там, правда, все довольно просто), попробовать перо в собственном гитхабе, поделиться ссылками на такие пробы, обсудить, а потом сравнить с тем, что получится у нас. С радостью приглашаем к диалогу! :)

Вот хорошая статья для ознакомления, как можно сделать неплохой наблюдатель на Свифт: https://www.swiftbysundell.com/articles/first-class-functions-in-swift/. Удовлетворяет всем критериям, что вы обозначили? Проведете сравнение вместе с остальными нативными реализациями наблюдателя и добавите в статью, чтобы она получилась всеобъемлющей?

А еще кажется, что KVO, property wrappers и даже observer blocks (те самые didSet, willSet) тоже являются различными реализациями паттерна наблюдатель, но в статье они не рассмотрены даже бегло, как и реализации в Combine.

Очень даже обоснованный. Responder chain, как раз то, что делает addTarget: таким мощным средством. Как выглядит типичный роутинг в 90% iOS приложений? Правильно: одни и те же экшены в каждом контроллере, из которого необходимо открывать, например, одно и то же модальное окно. Теперь как это можно улучшить воспользовавшись Responder chain:
1. Оставляем лишь один action
2. Переносим его в контейнер контроллер, например в таббар-контроллер.
3. В addTarget указываем nil вместо конкретного контроллера, в котором находится UI-элемент, к которому подключается подписка.
4. В принципе все. Из-за того, что мы не указали конкретный таргет, отправка нотификации уходит по Responder chain и доходит от каждого конкретного контроллера до таббар-контроллера и показывается то самое необходимое модальное окно. Только action теперь в едином экземпляре со всеми вытекающими. Данный подход перенесен на iOS из Mac, в котором он использовался для работы с командами меню. Если проследить аналогии, то понятно, что команда должна быть одна, а вот вызываться она может, когда совершенно разные UI-элементы и целые окна могут быть активны. Именно этим и надо пользоваться в iOS.

Как сделать то же самое с вашим универсальным наблюдателем? Именно поэтому я и сделал акцент на том, что каждый инструмент должен применяться в тех случаях, под которые он заточен.

Спасибо за статью. Качественно.
Только, наверное, стоит отметить в статье, что каждый инструмент нужно использовать по назначению, а не пробовать построить универсальный молоток под саморезы и шурупы.
В частности, необходимо отметить, что тот же `addTarget:` задумывался под конкретную цель. А именно -- работу с responder chain. Попытки заменить `addTarget:` на ваш "идеальный наблюдатель" приведут к необходимости дублировать экшены из контроллера в контроллер, из вьюхи во вьюху или же добавят сильной связности между ними, если вдруг вы попытаетесь не дублировать экшены, а прокидывать их между перечисленными классами.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Works in
Registered
Activity