что такое ptr птр pull to refresh
Выбор Pull To Refresh инструмента
100; подгрузка элементов по требованию; набор списков располагается в самописном компоненте, аля ViewPager) это действительно оказалось проблематично. О всех моих изысканиях в данном направлении читайте под катом.
Pull To Refresh — фишка, насколько мне известно, перекочевавшая на Android из iPhone’а. Удобный способ обновления списка.
Рассмотрим его на примере нашего новостного приложения (в него-то собственно и возникла необходимость внедрить эту фичу): есть список новостей, который обновляется через новостной сервер. Обновление ручное, так что внизу торчит кнопочка «Обновить», которая занимает некоторое место на экране. А зачем тратить драгоценное экранное пространство на кнопочку, которая не так уж и часто используется, если можно вопользоваться приемом Pull To Refresh: находясь вверху списка потяните список вниз, а затем отпустите, чтобы список обновился. Новые новости (каламбурчик) подгрузятся и отобразятся. Выглядит это примерное так:
Идея довольно удачная, поэтому и используется во многих приложения, включая популярные Facebook и Twitter клиенты. Так вот и мы решили внедрить в свой новостной проект такую фишку.
Но зачем писать с нуля то, что уже есть в готовом виде? Быстрый поиск в Google, великий и могучий StackOverFlow — и вот найден самый популярный инструмент android-pulltorefresh от Йохана Нильсона. Взял последнюю версию с GitHub’а и заюзал у себя. Не тут-то было! Проект вроде бы уже почти год развивается, но… вот что Я вижу в случае малых списков:
И такой вопрос сразу возникает: WTF? Проект watch’ат 418 человек, аж 71 человек его форкнул, а тут такое
каличное некорректное поведение. А все почему? Потому что вот этот вот «Tap to refresh. » — это header у ListView. И прячется он, в реализации от Йохана, банальным ListView.setSelection(1). А в случае коротких списков этот setSelection(1) вежливо посылает на фек не работает.
Но потом замечаю, что у проекта оказывается есть ещё два branch’а: enhancedpull (который уже был смержен с главным брэнчем) и, отоноче, scrollfix_for_short_list 🙂
Вытягиваю последнюю версию брэнча scrollfix_for_short_list, прикручиваю в проект: короткий список вроде выглядит нормально, только отчего ж у меня начал так тормозить UI? А дело вот в чем: список мой — не простой, а с подгрузкой по требованию (on-demand), т.е. саначала показываются первые 10 элементов, а если промотать до конца, то в список догружается следующая порция. А в чем же состоит fix для коротких списков по версии Йохана?
«А давайте добавим в footer ListView пустую вьюху ровно такой высоты, чтобы setSelection(1) снова смог нормально скрыть header», — сказал Йохан и приступил к вычилениям высоты footer’а. Чтобы вычислить его высоту надо знать суммарную высоту всех элементов в списке (кроме header’а конечно). Тогда мы отнимем эту высоту от высоты ListView и получим высоту для footer’а. А чтобы узнать высоту каждого элемента списка, откуда-то было взято «гениальное» решение перебрать с помощью адаптера все элементы (с помощью getView()) и каждому сделать measure(), т.е. по сути отрисовать их (даже если они невидимы). В результате мой список думал, что его все проматывают и проматывают — и все подгружал и подгружал новые порции до тех пор, пока элементы то и не закончатся. А элементов у меня обычно за 50 в списке, и списков несколько (пролистываются наподобие недавно появившегося ViewPager‘а). В общем вот такая реализация подсчета суммарной высоты элементов списка:
то здесь надо вручную создавать header, и по приходу событий менять текст в нем, ну и список конечно обновлять:
Не знаю, может это и более гибко, но… неудобно. К тому же оказалось, что данная реализация не очень корректно работает в Pager’е: стало возможно пролистывать список по вертикали и Pager по горизонтали одновременно.
В итоге со слезами на глазах Я стал ставить на костыли версию Йохана из брэнча scrollfix_for_short_list, чтобы она не заставляла списки подгружаться до бесконечности. Кое-как было сделано, но работало, мягко говоря, нестабильно. На горизонте замаячила перспектива писать компонент самому, и Я решил ещё раз пересерфить интренет. И, о чудо!, Я наткнулся на ещё одну реализацию — от Криса Бэйнса. Проект базировался на версии Йохана, но был с тех пор существенно улучшен (как пишет автор). Испытание подтвердило: данная реализация дествительно лишена всех багов, присущих версии Йохана, и выглядит более приятно (за счет дополнительной анимации).
Так вот, к чему Я тут так распространился? А все затем чтобы выдать мораль:
Если вы хотите использовать в своем проекте механизм Pull To Refresh — используйте реализацию от Криса Бэйнса. На мой взгляд, на данный момент это самая качественная реализация приема Pull To Refresh.
Почтовая кухня #1: DNS
Думаю, многим будет интересно наконец-то узнать, как работает почта. В нескольких статьях я попытаюсь максимально простым языком расписать все основные вопросы, связанные с работой электронной почты вообще и нужными настройками — в частности.
Для простоты и сокращения количества букв мы рассмотрим простейшую (и наиболее распространенную) ситуацию:
1 доменное имя (example.com).
1 почтовый домен (*@example.com).
1 почтовый сервер (mail.example.com).
1 IP-адрес (127.127.127.127).
Касательно почты, в DNS нас интересует четыре типа записей.
Второй — обязательный, без него почта в 99% случаев не будет ходить вообще. Без остальных, в принципе, можно обойтись, но шансы, что ваше письмо будет отвергнуто как спам возрастают в разы — тот же mail.ru отбрасывает практически всю почту, чьи IP-адреса не имеют PTR, либо PTR относится к dial-up провайдерам. И это правильно.
A-запись
A (Address) — запись, указывающая IP-адрес нужного нам доменного имени. Для корректной работы почты требуется A-запись сервера почты (mail.example.com). Выглядеть, в нашем случае, она будет так:
mail IN A 127.127.127.127
Где:
mail — домен.
IN A — тип записи.
127.127.127.127 — IP нашего почтового сервера.
MX-записи.
MX (Mail eXchange) — основная DNS-запись для электопочты. Она указывает, какими серверами обрабатывается почта для нашего домена.
У нас есть один почтовый домен — example.com. И один почтовый сервер — mail.example.com. Соответственно, запись будет выглядеть так:
example.com. IN MX 10 mail.example.com
Где:
example.com — домен, для которого обробатывается почта.
IN MX — тип записи.
10 — приоритет записи (Подробнее — ниже).
mail.example.com — A-имя почтового сервера.
MX-запись должна указывать именно на A-запись почтового сервера. Ставить MX указателем на IP или CNAME — не правильно.
Приоритет MX-записи нужен тогда, когда существует больше одного почтового сервера для одного домена (например у Google Mail их шесть). Он указывает, к какому серверу идет обращение в первую очередь, во вторую и так далее (если первый (второй, десятый) сервер недоступен или перегружен или по другим причинам не может принять письмо). Логика простая — приоритетнее тот, цифра которого меньше. Порядок цифр — не ограничен, хоть 10-20-30, хоть 1000-2000-3000.
Если у домена нет ни одной MX-записи, либо ни один из MX-серверов не доступен, сервер отправителя попытается доставить почту на IP, указанный в A-записи домена. Это назвается А-доставкой, но в принципе не кошерно и не используется многими серверами — нужно указывать MX, даже если он всего один.
PTR-запись.
PTR (PoinTeR) — так называемая «обратная запись». Она позволяет обратное разрешение (reverse resolving) IP-адреса в FQDN-хост.
Наш IP в виде reverse будет выглядеть так: 127.127.127.127.in-addr.arpa. В данном примере видно плохо, но адрес инвертируется в обратной зоне. Т.е. IP 192.168.0.1 будет выглядеть как 1.0.168.192.in-addr.arpa.
Для корректного распознования хоста, запись IP-адреса, с которого отправляется должна соответсовать hostname почтового сервера, отправляемому в HELO\EHLO.
PTR-запись в нашем случае, соответственно:
127.127.127.127.in-addr.arpa IN PTR mail.example.com.
Прописать эту запись может владелец блока IP-адресов (Читайте мою статью про распределение адресного пространства). Если вы таковым не являетесь и получили адреса от провайдера — обращайтесь к вашему провайдеру или дата-центру, чтобы запись установил он.
TXT-запись и SPF.
TXT (TeXT) — текстовая запись DNS. Нам она интересна только тем, что может (и в современном мире — должна) содержать в себе SPF.
SPF (Sender Policy Framework) — запись, позволяющая вам указать, какие сервера имеют право отправлять почту от имени вашего домена (представившись вашим сервером, либо с обратным адресом в вашем домене).
Если этой записи нет, и кто-то пытается отправить письмо (как правило, спам) с обратным адресом в вашем домене — оно будет отклонено большинством серверов. Или не будет, и вы получите большие проблемы со своим дата-центром или провайдером и репутацию спамера 🙂
SPF-запись выглядит так:
v=spf1 — версия протокола.
(+\-)a — разрешение или запрет отправки почты с IP, соответствующего A-записи домена.
(+\-)mx — разрешение или запрет отправки почты с IP, соответствующего MX-записи домена.
ip4:IP — явное указание IP, с которого можно принимать почту от имени домена.
(
\-all) — отвергать или принимать почту от IP, не перечисленных в списке и не указанных явно.
В нашем случае TXT SPF запись будет такой:
Таким образом, мы разрешили прием почты от имени домена с IP, соотвествующих A или MX записям и запретили прием от других адресов — никто не сможет наспамить прикинувшись нами или обмануть наших пользователей, отправив фишинговую ссылку от имени тех. поддержки.
Буду рад комментариям, готов ответить на вопросы.
В следующих статьях я напишу об SMTP, Greylisting-е и RBL.
А еще вы можете вступить в блог и тоже о чем-то рассказать.
Одминский блог
Блог о технологиях, технократии и методиках борьбы с граблями
PTR запись зоны обратного просмотра
Вторую неделю воюю с замечательным провайдером Горком на предмет поддержки ими зоны обратного просмотра, она же реверсивная зона. Точнее внесения в неё моего MX сервера, поскольку при отправке почты на удаленные сервера, многие почтари проверяют наличие у сайта PTR записи как таковой и в случае её отсутствия, расценивают отправителя как спамера. Хитрованы же из Горкома убеждают меня в том, что это должен делать я сам и вообще письма от меня не доходят вовсе не из-за реверса упоминаемого в коде ошибки, а потому что у меня дескать не правильно настроен ns сервер.
Для задачи поддержки обратной зоны существует специальный домен IN-ADDR.ARPA. Сам файл описания зоны домена обратного просмотра состоит преимущественно из записей PTR типа “Pointer”.
[name][ttl] IN PTR [host]
то есть для сервера ns.server.ru c IP= A.B.C.D запись будет следующего вида
$ORIGIN C.B.A.in-addr.arpa.
D PTR ns.server.ru.
123.46.181.62.in-addr.arpa. IN PTR ns.server.ru.
просмотреть информацию об имеющейся реверсивной записи можно командой:
там же, кстати, можно увидеть и того, кто является ответственным за поддержку пула адресов. Также посмотреть ситуацию по по PTR в том числе можно на сайте http://www.squish.net/dnscheck
Опять же информация по ответственному за пул адресов можно получить командой:
# whois IP
В связи с чем еще раз хочу сказать с полной ответственностью- PTR запись размещает у себя провайдер, или тот кто является обладателем пула IP адресов. Хотя ленивый провайдер и может делегировать право ведения обратной зоны и серверу ответственному за поддержку основной (в смысле организации) или предоставить возможность удаленного управления своим сегментом.Делегирование осуществляется перенаправлением запросов на другие сервера, путем создания обратной зоны, состоящей из записи синонимов CNAME.
Например для сети провайдера 172.16.10.0/24, разбитой на подсети 172.16.10.0/25 ; 172.16.10.128/26 ; 172.16.10.192/26 это будет выглядеть так:
############################################
$ORIGIN 10.16.172.in-addr.arpa.
@ IN SOA ns.provider.ru. dnsmaster.provider.ru. (…)
;
; пулл 0-127 /25
;
0/25 IN NS ns.a.ru.
0/25 IN NS ns.a-slave.ru.
;
1 IN CNAME 1.0/25.10.16.172.in-addr.arpa.
2 IN CNAME 2.0/25.10.16.172.in-addr.arpa.
;
; пулл 129-191 /26
;
128/26 IN NS ns.b.ru.
128/26 IN NS ns.b-slave.ru.
;
129 IN CNAME 129.128/26.10.16.172.in-addr.arpa.
130 IN CNAME 130.128/26.10.16.172.in-addr.arpa.
;
; пулл 193-255 /26
;
192/26 IN NS ns.c.ru.
192/26 IN NS ns.c-slave.ru.
;
193 IN CNAME 193.192/26.10.16.172.in-addr.arpa.
194 IN CNAME 194.192/26.10.16.172.in-addr.arpa.
############################################
Сама же организация обязана вести на своем NS сервере запись вида
Для двух других блоков адресов SOA записи будут соответственно:
$ORIGIN 128/26.10.16.172.in-addr.arpa.
@ IN SOA ns.b.ru dnamaster.b.ru (…)
$ORIGIN 192/26.10.16.172.in-addr.arpa.
@ IN SOA ns.c.ru dnamaster.c.ru (…)
*** По мотивам двухчасового разговора по аське с гуру piv_m
Что Такое PTR Запись и Как Сделать Обратный IP-поиск?
Введение
Существует много типов DNS записей и для новичков может быть слишком сложно понять, какая функция у этой DNS-записи или как ее настроить. В этом уроке вы узнаете, что такое PTR запись и как проверить настроена ли она для IP-адреса.
Переходите на почтовый хостинг от Hostinger, чтобы иметь полный доступ ко всем настройкам и возможностям почтового сервиса.
Что такое PTR запись
Зачем необходима PTR запись
Она полезна для исходящих почтовых серверов. Эта запись повышает надежность отправляющего сервера и позволяет получить обратный ответ для проверки имени хоста через IP-адрес. Это отличный способ защиты от всех видов спамеров, которые используют мошеннические доменные имена для рассылки спама. Вот почему некоторые крупные провайдеры услуг почты, такие как yahoo.com, gmail.com делают обратный поиск в DNS, прежде чем принимать входящие письма.
Что вам понадобится
Перед тем, как вы начнете это руководство, вам понадобится следующее:
Способ 1 — Проверка PTR с помощью nslookup или dig
Windows, Unix и схожие операционные системы (Linux, MacOS) имеют встроенные инструменты для проверки DNS записей. Если вы являетесь пользователем Windows, следуйте данным этапам:
Как видно из результата, PTR запись — ec2-54-243-154-49.hostinger.com.
Для пользователей Linux или Mac процесс схож:
Из раздела ANSWER SECTION можно узнать значение PTR записи — ec2-54-243-154-49.hostinger.com
Способ 2 — Использование онлайн инструментов
Еще один способ для получения информации об имени хоста IP-адреса, это использовать инструмент для обратного поиска MxToolBox. Все что нужно, это вписать в поле IP адрес и нажать кнопку Reverse Lookup (Обратный поиск).
Заключение
К сожалению, если поиск показывает, что запись не настроена для IP-адреса, в большинстве случаев вам придется обратиться к вашему хостинг-провайдеру или интернет-провайдеру с просьбой создать ее. Однако теперь вы знаете, что такое PTR запись и как проверить есть ли у нее IP-адрес. Это полезно если вы столкнулись с ошибками DNS и возвратами при попытке отправить электронную почту, поскольку это поможет вам устранить проблему.
Пять подводных камней при использовании shared_ptr
Класс shared_ptr — это удобный инструмент, который может решить множество проблем разработчика. Однако для того, чтобы не совершать ошибок, необходимо отлично знать его устройство. Надеюсь, моя статья будет полезна тем, кто только начинает работать с этим инструментом.
Перекрестные ссылки
Данная проблема является наиболее известной и связана с тем, что указатель shared_ptr основан на подсчете ссылок. Для экземпляра объекта, которым владеет shared_ptr, создается счетчик. Этот счетчик является общим для всех shared_ptr, указывающих на данный объект.
При конструировании нового объекта создается объект со счетчиком и в него помещается значение 1. При копировании счетчик увеличивается на 1. При вызове деструктора (или при замене указателя путем присваивания, или вызова reset), счетчик уменьшается на 1.
Что произойдет при выходе объектов a и b из области определения? В деструкторе уменьшатся ссылки на объекты. У каждого объекта будет счетчик = 1 (ведь a все еще указывает на b, а b — на a). Объекты “держат” друг друга и у нашего приложения нет возможности получить к ним доступ — эти объекты “потеряны”.
Для решения этой проблемы существует weak_ptr. Одним из типичных случаев создания перекрестных ссылок является случай, когда один объект владеет коллекцией других объектов
При таком устройстве каждый Widget будет препятствовать удалению RootWidget и наоборот.
В таком случае нужно ответить на вопрос: “Кто кем владеет?”. Очевидно, что именно RootWidget в данном случае владеет объектами Widget, а не наоборот. Поэтому модифицировать пример нужно так:
Слабые ссылки не препятствуют удалению объекта. Они могут быть преобразованы в сильные двумя способами:
1) Конструктор shared_ptr
Вывод:
В случае возникновения в коде кольцевых ссылок, используйте weak_ptr для решения проблем.
Безымянные указатели
Проблема безымянных указателей относится к вопросу о “точках следования” (sequence points)
Из этих двух вариантов документация рекомендует всегда использовать второй — давать указателям имена. Рассмотрим пример, когда функция bar определена вот так:
Если во время второго шага будет сгенерировано исключение (а оно в нашем примере сгенерировано будет), то Widget будет считаться сконструированным, но shared_ptr еще не будет им владеть. В итоге ссылка на этот объект будет потеряна. Я проверил данный пример на gcc 4.7.2. Порядок вызова был таким, что shared_ptr+new вне зависимости от опций компиляции не разделялись вызовом bar. Но полагаться именно на такое поведение не стоит – это не гарантировано. Буду признателен, если мне подскажут компилятор, его версию и опции компиляции, для которых подобный код приведет к ошибке.
Еще одна возможность для обхода проблемы анонимных shared_ptr — это использование функций make_shared или allocate_shared. Для нашего примера это будет выглядеть так:
Данный пример выглядит даже более лаконично, чем исходный, а так же обладает рядом преимуществ в плане выделения памяти (вопросы эффективности оставим за пределами статьи). Допустим вызов make_shared с любым количество аргументов. Например следующий код вернет shared_ptr на строку, созданную через конструктор с одним параметром.
Вывод:
Давайте shared_ptr имена, даже если код будет от этого менее лаконичным, либо воспользуйтесь для создания объектов
функциями make_shared и allocate_shared.
Проблема использования в разных потоках
Подсчет ссылок в shared_ptr построен с использованием атомарного счетчика. Мы без опаски используем указатели на один и тот же объект из разных потоков. Во всяком случае, мы не привыкли беспокоиться о подсчете ссылок (потокобезопасность самого объекта – другая проблема).
Допустим, у нас есть глобальный shared_ptr:
Запустите вызов read из разных потоков и вы увидите, что никаких проблем в коде не возникает (до тех пор, пока вы выполняете над Widget потокобезопасные для этого класса операции).
Допустим, есть еще одна функция:
Устройство shared_ptr достаточно сложно, поэтому я приведу код, который схематически поможет показать проблему. Разумеется, настоящий код выглядит иначе.
Можно написать подобный код:
Теперь, используя такие функции, можно безопасно работать с этим shared_ptr.
Вывод: если какой-то экземпляр shared_ptr доступен разным потокам и может быть модифицирован, то необходимо позаботиться о синхронизации доступа к этому экземпляру shared_ptr.
Особенности времени разрушения освобождающего функтора для shared_ptr
Данная проблема может иметь место только в том случае, если вы используете собственный освобождающий функтор в сочетании со слабыми указателями (weak_ptr). Например, вы можете создать shared_ptr на основе другого shared_ptr, добавив новое действие перед удалением (по сути шаблон “Декоратор”). Так вы могли бы получить указатель для работы с базой данных, изъяв его из пула соединений, а по окончании работы клиента с указателем — вернуть его обратно в пул.
Проблема заключается в том, что объект, переданный в качестве освобождающего функтора для shared_ptr, будет разрушен только тогда, когда все ссылки на объект будут уничтожены — как сильные(shared_ptr), так и слабые(weak_ptr). Таким образом, если ConnectionReleaser не позаботится о том, чтобы “отпустить” переданный ему указатель (connectionToRelease), он будет держать сильную ссылку, пока существует хотя бы один weak_ptr от shared_ptr, созданного функцией getConnection. Это может привести к достаточно неприятному и неожиданному поведению вашего приложения.
Возможен так же вариант, когда вы воспользуетесь bind для создания освобождающего функтора. Например так:
Помните, что bind копирует переданные ему аргументы (кроме случая с использованием boost::ref), и если среди них будет shared_ptr, то его тоже следует очистить, дабы избежать уже описанной проблемы.
Вывод: Выполните в освобождающей функции все действия, которые необходимо совершить при разрушении последней сильной ссылки. Сбросьте все shared_ptr, которые по какой-то причине являются членам вашего функтора. Если вы используете bind, то не забывайте, что он копирует переданные ему аргументы.
Особенности работы с шаблоном enable_shared_from_this
Иногда требуется получить shared_ptr из методов самого объекта. Попытка создания нового shared_ptr от this приведет к неопределенному поведению (скорее всего к аварийному завершению программы), в отличие от intrusive_ptr, для которого это является обычной практикой. Для решения этой проблемы был придуман шаблонный класс-примесь enable_shared_from_this.
Шаблон enable_shared_from_this устроен следующим образом: внутри класса содержится weak_ptr, в который при конструировании shared_ptr помещается ссылка на этот самый shared_ptr. При вызове метода shared_from_this объекта, weak_ptr преобразуется в shared_ptr через конструктор. Схематически шаблон выглядит так:
Конструктор shared_ptr для этого случая схематически выглядит так:
Важно понимать, что при конструировании объекта weak_this_ еще ни на что не указывает. Правильная ссылка в нем появится только после того, как сконструированный объект будет передан в конструктор shared_ptr. Любая попытка вызова shared_from_this из конструктора приведет к bad_weak_ptr исключению.
К таким же последствиям приведет попытка обратиться к shared_from_this из деструктора, но уже по другой причине: в момент разрушения объекта уже считается, что на него не указывает никаких сильных ссылок (счетчик декрементирован).
Со вторым случаем (деструктор) мало что можно придумать. Единственный вариант — позаботиться о том, чтобы не вызывать shared_from_this и сделать так, чтобы этого не делали функции, которые вызывает деструктор.
С первым случаем все обстоит немного проще. Наверняка вы уже решили, что единственный способ для существования вашего объекта — shared_ptr, тогда будет уместно переместить конструктор объекта в закрытую часть класса и создать статический метод для создания shared_ptr нужного вам типа. Если при инициализации объекта вам нужно выполнить действия, которым потребуется shared_from_this, то для этой цели можно выделить логику в метод init.
Вывод:
Избегайте вызовов (прямых или косвенных) shared_from_this из конструкторов и деструкторов. В случае, если для правильной инициализации объекта требуется доступ к shared_from_this: создайте метод init, делегируйте создание объекта статическому методу и сделайте так, чтобы объекты можно было создавать только с помощью этого метода.
Заключение
В статье рассмотрены 5 особенностей использования shared_ptr и представлены общие рекомендации по избежанию потенциальных проблем.
Хотя shared_ptr и снимает с разработчика множество проблем, знание внутреннего устройства (хоть и примерно) является обязательным для грамотного использования shared_ptr. Я рекомендую внимательно изучить устройство shared_ptr, а так же классов, связанных с ним. Соблюдение ряда простых правил может уберечь разработчика от нежелательных проблем.
Литература
Приложение
В приложении представлены полные тексты программ для иллюстрации описанных в статье случаев