MrDekk2019-11-07T06:27:07+00:00http://mrdekk.ruMrDekkВнедрение зависимостей (dependency injection) в Swift 5.12019-11-06T00:00:00+00:00http://mrdekk.ru/2019/11/06/swift-dependency-injection<p>Внедрение зависимостей очень горячая тема в любой области разработки, где мы пишем что-то более сложное чем Hello, World. Однако несмотря на казалось бы изученный вдоль и поперек вопрос, вариантов его решения вы можете на просторах интернета найти великое множество. И в каждом месте оно подается как единственно правильное. И как же выбрать? Предлагаю в этой статье немножко рассмотреть подходы, их плюсы и минусы, немножко поиграться со Swift’ом вообще и попробовать его новые фичи в виде @PropertyWrapper’s.</p>
<p>Итак, постановка задачи у нас будет такая - у нас есть два класса BooksRenderer, который просто каким-то образом рисует книжки, и BooksProvider, который ему их поставляет. На Swift это будет выглядеть примерно так:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">BooksRenderer</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">provider</span><span class="p">:</span> <span class="kt">BooksProvider</span> <span class="o">=</span> <span class="o">...</span> <span class="cm">/* за это троеточие и будет вестись основная борьба */</span>
<span class="kd">func</span> <span class="nf">draw</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">books</span> <span class="o">=</span> <span class="n">provider</span><span class="o">.</span><span class="n">books</span>
<span class="cm">/* тут каким-то образом рисуются книги из массива books */</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">protocol</span> <span class="kt">BooksProvider</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">books</span><span class="p">:</span> <span class="p">[</span><span class="kt">Book</span><span class="p">]</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Будем также считать что есть некая реализация протокола BooksProvider, например такая наивная</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">NaiveBooksProvider</span><span class="p">:</span> <span class="kt">BooksProvider</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">books</span><span class="p">:</span> <span class="p">[</span><span class="kt">Book</span><span class="p">]</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">[</span>
<span class="kt">Book</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"Dune"</span><span class="p">,</span> <span class="nv">author</span><span class="p">:</span> <span class="s">"Frank Herbert"</span><span class="p">),</span>
<span class="kt">Book</span><span class="p">(</span><span class="nv">title</span><span class="p">:</span> <span class="s">"Lord of the Rings"</span><span class="p">,</span> <span class="nv">author</span><span class="p">:</span> <span class="s">"John R.R. Tolkien"</span><span class="p">)</span>
<span class="p">]</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Теперь наша задача каким-то образом доставить экземпляр класса NaiveBooksProvider в BooksRenderer. Самый наивный подход такой, создать экземлляр класса прямо на месте:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">BooksRenderer</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">provider</span><span class="p">:</span> <span class="kt">BooksProvider</span> <span class="o">=</span> <span class="kt">NaiveBooksProvider</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Несмотря на то, что этот подход, каким бы наивным он не был, много где применяется, у него есть очевидные недостатки:</p>
<ol>
<li>Мы можем захотеть как-то менять конкретный класс реализации, а он тут “прибит гвоздями”</li>
<li>Мы можем захотеть unit-протестировать класс BooksRenderer, и тогда вместо провайдера захотим вставить какой-нибудь мок
и т.д.</li>
</ol>
<p>Нам надо что-то лучше. И много где предлагают хорошо известный паттер ServiceLocator. Если его применить, то выглядеть это будет примерно так:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">ServiceLocator</span> <span class="p">{</span>
<span class="kd">static</span> <span class="k">let</span> <span class="nv">booksProvider</span><span class="p">:</span> <span class="kt">BooksProvider</span> <span class="o">=</span> <span class="kt">NaiveBooksProvider</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">BooksRenderer</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">provider</span><span class="p">:</span> <span class="kt">BooksProvider</span> <span class="o">=</span> <span class="kt">ServiceLocator</span><span class="o">.</span><span class="n">booksProvider</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Уже лучше, ответственность за выбор конкретного класса мы достали из BooksRenderer и наделили этой почетной обязанностью класс ServiceLocator. И мы даже можем сделать разные ServiceLocator’ы для основного приложения и для тестов, которые будут создавать разные BooksProvider’ы, однако:</p>
<ol>
<li>Теперь 90% кода будет знать про класс ServiceLocator</li>
<li>Класс ServiceLocator будет огромным (кто там что говорил про Massive View Controller?, у нас тут Massive Service Locator)</li>
</ol>
<p>Прежде чем пойти дальше, давайте сделаем некоторое лирическое отступление, разберемся в терминологии зависимостей. Вообще внедряемых зависимостей может быть два типа: прости хоспади singleton (но это не то что вы подумали) и prototype. “singleton” зависимости - это такие зависимости, которые сколько бы вы не внедряли в рамках одного конкретного модуля, это всегда будет один экземпляр. “prototype” же - даст на каждую точку внедрения новый экземпляр.</p>
<p>Поэтому если говорить про наш пример с ServiceLocator’ом, то например booksProvider - это singleton зависимость, а bookUpdateOperation - prototype:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">ServiceLocator</span> <span class="p">{</span>
<span class="kd">static</span> <span class="k">let</span> <span class="nv">booksProvider</span><span class="p">:</span> <span class="kt">BooksProvider</span> <span class="o">=</span> <span class="kt">NaiveBooksProvider</span><span class="p">()</span>
<span class="kd">static</span> <span class="kd">func</span> <span class="nf">bookUpdateOperation</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Operation</span> <span class="o">&</span> <span class="kt">BookUpdate</span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">NaiveBookUpdateOperation</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Теперь давайте сделаем еще одно лирическое отступление, подчерпнутое мной когда я еще занимался “кровавым” enterprise и работал с <a href="http://spring.io">Srping Framework</a>. Хороший DI контейнер это такой контейнер, который не видно. Тут можно еще пофилософствовать и вспомнить <a href="https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=3&cad=rja&uact=8&ved=2ahUKEwjEo_P2mdblAhV8wsQBHaObD_EQFjACegQIDRAG&url=https%3A%2F%2Fru.wikipedia.org%2Fwiki%2F%25D0%25A2%25D0%25B5%25D0%25BE%25D1%2580%25D0%25B8%25D1%258F_%25D1%2580%25D0%25B5%25D1%2588%25D0%25B5%25D0%25BD%25D0%25B8%25D1%258F_%25D0%25B8%25D0%25B7%25D0%25BE%25D0%25B1%25D1%2580%25D0%25B5%25D1%2582%25D0%25B0%25D1%2582%25D0%25B5%25D0%25BB%25D1%258C%25D1%2581%25D0%25BA%25D0%25B8%25D1%2585_%25D0%25B7%25D0%25B0%25D0%25B4%25D0%25B0%25D1%2587&usg=AOvVaw0VcQvVNp9cBvIu5iqYojs9">ТРИЗ</a> с ее идеальным конечным результатом, который на наш DI’ный контекст перефразируется так: “хороший DI контейнер - это такой, которого нет, а зависимости внедряются”.</p>
<p>Таким образом, можно сделать такой DI на базе initializer injection (оно лучше property injection, потому что компилятор в этом случае не даст вам озорничать, а с property injection легко забыть что-нибудь присвоить и грохнуться в рантайме):</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">AppContainer</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">booksProvider</span><span class="p">:</span> <span class="kt">BooksProvider</span>
<span class="nf">init</span><span class="p">(</span><span class="n">with</span> <span class="nv">appDelegate</span><span class="p">:</span> <span class="kt">AppDelegate</span><span class="p">)</span> <span class="p">{</span>
<span class="n">booksProvider</span> <span class="o">=</span> <span class="kt">NaiveBooksProvider</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="n">appDelegate</span><span class="o">.</span><span class="n">booksRenderer</span> <span class="o">=</span> <span class="kt">BooksRenderer</span><span class="p">(</span><span class="nv">provider</span><span class="p">:</span> <span class="n">booksProvider</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">@UIApplicationMain</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">AppDelegate</span><span class="p">:</span> <span class="kt">UIResponder</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">container</span><span class="p">:</span> <span class="kt">AppContainer</span>
<span class="k">var</span> <span class="nv">booksRenderer</span><span class="p">:</span> <span class="kt">BooksRenderer</span><span class="o">!</span>
<span class="kd">func</span> <span class="nf">applicationDidFinishLauncherWithOptions</span><span class="p">(</span><span class="o">...</span><span class="p">)</span> <span class="p">{</span>
<span class="n">container</span> <span class="o">=</span> <span class="kt">AppContainer</span><span class="p">(</span><span class="nv">with</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">BooksRenderer</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">provider</span><span class="p">:</span> <span class="kt">BooksProvider</span>
<span class="nf">init</span><span class="p">(</span><span class="nv">provider</span><span class="p">:</span> <span class="kt">BooksProvider</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">provider</span> <span class="o">=</span> <span class="n">provider</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Причем такой подход будет гарантировать вам проверку компилятором. И при этом про AppContainer будет знать только AppDelegate. Да, корневые зависимости в самом AppDelegate’е будут force unwrapped (что исть не хорошо, но лучше я не придумал), но эта вольность доступна только тут.</p>
<p>prootype зависимости в таком подходе можно оформить либо в виде фабрики</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">SomeFactory</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">superDep</span><span class="p">:</span> <span class="kt">SuperDep</span>
<span class="nf">init</span><span class="p">(</span><span class="nv">superDep</span><span class="p">:</span> <span class="kt">SuperDep</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">superDep</span> <span class="o">=</span> <span class="n">superDep</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">makeSomeDep</span><span class="p">(</span><span class="o">...</span><span class="p">)</span> <span class="o">-></span> <span class="kt">SomeDep</span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">SomeDep</span><span class="p">(</span><span class="n">superDep</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>и потом внедрять это фабрику как singleton зависимость туда где нужно генерить prototype’ные, или в виде замыкания</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">typealias</span> <span class="kt">SomeFactory</span> <span class="o">=</span> <span class="p">(</span><span class="n">_</span> <span class="nv">superDep</span><span class="p">:</span> <span class="kt">SuperDep</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span> <span class="o">-></span> <span class="kt">SomeDep</span> <span class="p">{</span> <span class="kt">SomeDep</span><span class="p">(</span><span class="n">superDep</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span> <span class="p">}</span>
</code></pre></div></div>
<p>и также ее внедрять как singleton зависимость.</p>
<p>Но я обещал немножко Swift 5.1 и @PropertyWrapper, их легко сделать так (пусть будет наш пример с ServiceLocator’ом, хотя его можно легко модифицировать):</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">@propertyWrapper</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="kt">Inject</span><span class="o"><</span><span class="kt">Dep</span><span class="o">></span> <span class="p">{</span>
<span class="kd">private</span> <span class="k">let</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">kept</span><span class="p">:</span> <span class="kt">Dep</span><span class="p">?</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">wrappedValue</span><span class="p">:</span> <span class="kt">Dep</span> <span class="p">{</span>
<span class="n">kept</span> <span class="p">??</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">dependency</span><span class="p">:</span> <span class="kt">Dep</span> <span class="o">=</span> <span class="kt">ServiceLocator</span><span class="o">.</span><span class="nf">resolve</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">name</span><span class="p">)</span>
<span class="n">kept</span> <span class="o">=</span> <span class="n">dependency</span>
<span class="k">return</span> <span class="n">dependency</span>
<span class="p">}()</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="n">convenience</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="kc">nil</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span><span class="p">?)</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">ServiceLocator</span> <span class="p">{</span>
<span class="kd">static</span> <span class="kd">func</span> <span class="n">register</span><span class="o"><</span><span class="kt">T</span><span class="o">></span><span class="p">(</span><span class="k">for</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">resolver</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">T</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// register</span>
<span class="p">}</span>
<span class="kd">static</span> <span class="kd">func</span> <span class="n">resolve</span><span class="o"><</span><span class="kt">T</span><span class="o">></span><span class="p">(</span><span class="k">for</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span><span class="p">?)</span> <span class="o">-></span> <span class="kt">T</span> <span class="p">{</span>
<span class="c1">// do some magic</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">final</span> <span class="kd">class</span> <span class="kt">BooksRenderer</span> <span class="p">{</span>
<span class="kd">@Inject</span> <span class="kd">private</span> <span class="k">var</span> <span class="nv">provider</span><span class="p">:</span> <span class="kt">BooksProvider</span>
<span class="p">}</span>
</code></pre></div></div>
<p>И вуаля! Однако несмотря на всю прелесть такой магии, есть проблема в месте где творится магия (“do some magic”). Если вы вдруг забыли сделать register, то упс, вы получаете рантайм крэш. И сделать это красиво с compile time check непонятно как, так как регистрация динамическая.</p>
<p>Предлагаю подискутировать, оформляйте issues <a href="https://github.com/mrdekk/mrdekk.github.io">тут</a></p>
<p>P.S.</p>
Разрабатываем и отлаживаем С++ в Docker с помощью VSCode (2019 edition - clang8 lldb)2019-05-08T00:00:00+00:00http://mrdekk.ru/2019/05/08/clang8-dev-vscode-ubuntu-docker<p>Какое-то время назад я написал статью <a href="/2018/01/06/vscode-cpp-docker-debugging/">Разрабатываем и отлаживаем С++ в Docker с помощью VSCode</a> в котором для сборки использовался gcc и для отладки gdbserver. В этом обновленном посте хочу затронуть такой же вопрос, но уже на базе clang 8 и lldb-server.</p>
<p>Также в отличии от предыдущей статьи мы не будем каждый раз пересоздавать контейнер, а запустим его один раз и будет с ним работать через <code class="highlighter-rouge">docker exec</code>.</p>
<p>Итак, начнем, Dockerfile</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>FROM ubuntu:18.04
RUN apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential xz-utils curl cmake
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y lldb
RUN curl -SL http://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz | tar -xJC . && \
mv clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04 clang_8.0.0 && \
mv clang_8.0.0 /usr/local
ENV PATH="/usr/local/clang_8.0.0/bin:${PATH}"
ENV LD_LIBRARY_PATH="/usr/local/clang_8.0.0/lib:${LD_LIBRARY_PATH}"
WORKDIR /opt/build
VOLUME ["/opt"]
</code></pre></div></div>
<p>lldb (для lldb-server) используем поставляемый в пакетах (на момент написания статьи для 18.04 это был LLDB 6). Также из пакетов ставим cmake (вроде ничего специфичного пока не надо). clang ставим руками, но уже собранный.</p>
<p>Дальше нам нужен скрипт сборки контейнера (/scripts/docker_build.sh)</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nv">cwd</span><span class="o">=</span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span>
docker build <span class="se">\</span>
<span class="nt">-t</span> something/cmaker <span class="se">\</span>
<span class="nb">.</span>
</code></pre></div></div>
<p>И таска для vscode</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
"label": "build docker container",
"command": "${workspaceFolder}/scripts/docker_build.sh"
}
</code></pre></div></div>
<p>после этого нам понадобится запустить контейнер для последующего использования, для этого нам нужен скрипт (/scripts/docker_run.sh). Тут попутно запускается lldb-server, о нем позже:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nv">cwd</span><span class="o">=</span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span>
docker stop сmaker
docker rm сmaker
docker run <span class="se">\</span>
<span class="nt">-dt</span> <span class="se">\</span>
<span class="nt">--name</span> сmaker <span class="se">\</span>
<span class="nt">-p</span> 7000:7000 <span class="se">\</span>
<span class="nt">-p</span> 7001:7001 <span class="se">\</span>
<span class="nt">-p</span> 7002:7002 <span class="se">\</span>
<span class="nt">-p</span> 7003:7003 <span class="se">\</span>
<span class="nt">-v</span> <span class="k">${</span><span class="nv">cwd</span><span class="k">}</span>:/opt <span class="se">\</span>
<span class="nt">--privileged</span> <span class="se">\</span>
something/cmaker <span class="se">\</span>
<span class="s2">"</span><span class="k">${</span><span class="p">@</span><span class="k">}</span><span class="s2">"</span>
</code></pre></div></div>
<p>и таска для vscode</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
"label": "run container for future builds",
"command": "${workspaceFolder}/scripts/docker_run.sh",
"args": [
"lldb-server", "platform", "--server", "--listen=0.0.0.0:7000", "-m", "7001", "-M", "7003"
],
"options": {
"cwd": "${workspaceFolder}"
}
}
</code></pre></div></div>
<p>После этого можем приступать к сборке проекта. Структуру проекта сразу предусмотрел для многомодульного проекта</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>include
scripts
docker_build.sh
docker_run.sh
hword
include
CMakeLists.txt
main.cpp
CMakeLists.txt
Dockerfile
</code></pre></div></div>
<p>Корневой CMakeLists.txt выглядит так</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cmake_minimum_required(VERSION 3.0)
project(something)
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/build)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
include_directories("${PROJECT_INCLUDE_DIR}")
include_directories("${PROJECT_SOURCE_DIR}")
add_subdirectory(hword)
</code></pre></div></div>
<p>CMakeLists.txt для hword выглядит так</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cmake_minimum_required(VERSION 3.0)
project(hword)
set(PROJECT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
set(HWORD_VERSION_MAJOR 1)
set(HWORD_VERSION_MINOR 0)
set(PROJECT_HWORD_SRCS
${PROJECT_SOURCE_DIR}/main.cpp
)
include_directories("${PROJECT_BINARY_DIR}")
add_executable(${PROJECT_NAME}_r ${PROJECT_HWORD_SRCS})
include_directories("${PROJECT_INCLUDE_DIR}")
</code></pre></div></div>
<p>Обратите внимание что к имени проекта добавлен постфикс <code class="highlighter-rouge">_r</code></p>
<p>Для примера содержимое main.cpp</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#include <unistd.h>
#include <limits.h>
#include <iostream>
using namespace std;
static const int HNAME_MAX = 512;
static const int LNAME_MAX = 512;
int main()
{
char hostname[HNAME_MAX];
char username[LNAME_MAX];
gethostname(hostname, HNAME_MAX);
getlogin_r(username, LNAME_MAX);
cout << "hostname: " << hostname << ", username: " << username << endl;
return 0;
}
</code></pre></div></div>
<p>Теперь соберем все это дело, запустим и отладим</p>
<p>Нам понадобится скрипт для <code class="highlighter-rouge">docker exec</code></p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nv">cwd</span><span class="o">=</span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span>
docker <span class="nb">exec</span> <span class="se">\</span>
<span class="nt">-it</span> <span class="se">\</span>
cmaker <span class="se">\</span>
<span class="s2">"</span><span class="k">${</span><span class="p">@</span><span class="k">}</span><span class="s2">"</span>
</code></pre></div></div>
<p>Таска для cmake</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
"label": "cmake initialize scripts (Makefile, Debug)",
"command": "${workspaceFolder}/scripts/docker_exec.sh",
"args": [
"cmake", "-G", "Unix Makefiles",
"-DCMAKE_BUILD_TYPE=Debug",
"-DCMAKE_C_COMPILER=/usr/local/clang_8.0.0/bin/clang",
"-DCMAKE_CXX_COMPILER=/usr/local/clang_8.0.0/bin/clang++",
"/opt"
],
"options": {
"cwd": "${workspaceFolder}"
}
}
</code></pre></div></div>
<p>Обратите внимание, что тут явным образом указаны пути до компиляторов, это очень важно.</p>
<p>Далее таска для сборки всего проекта и для сборки конкретного</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
"label": "make full project",
"command": "${workspaceFolder}/scripts/docker_exec.sh",
"args": [
"make", "-j", "8"
],
"options": {
"cwd": "${workspaceFolder}"
}
}
</code></pre></div></div>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
"label": "make (hword) project",
"command": "${workspaceFolder}/scripts/docker_exec.sh",
"args": [
"make", "-j", "8", "hword_r"
],
"options": {
"cwd": "${workspaceFolder}"
}
}
</code></pre></div></div>
<p>И для простого запуска</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
"label": "run (hword) project",
"command": "${workspaceFolder}/scripts/docker_exec.sh",
"args": [
"/opt/build/hword_r"
]
}
</code></pre></div></div>
<p>С отладкой есть несколько проблем. Во-первых, кроме порта на котором слушает lldb-server надо пробросить еще несколько, на которых будет работать собственно соединение (для этого при запуске контейнера несколько опций <code class="highlighter-rouge">-p</code>). Во-вторых, lldb-server’у этот диапазон портов надо явно задать. И в третьих, надо правильно написать launch.json, выглядит он так:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
"name": "debug: hword",
"type": "lldb",
"request": "launch",
"program": "/opt/build/hword_r",
"initCommands": [
"platform select remote-linux",
"platform connect connect://127.0.0.1:7000"
],
"cwd": "/opt",
"sourceMap": {
"/opt" : "${workspaceFolder}"
}
}
</code></pre></div></div>
<p>Жмем F5 и выуля - мы в отладке. Надеюсь это поможет :)</p>
Сравнение Swift vs Kotlin2018-11-28T00:00:00+00:00http://mrdekk.ru/2018/11/28/swift-kotlin-comparison<p>Небольшая табличка сравнения кода на Swift и кода на Kotlin для справки. Этакий cheet sheet.</p>
<h3 id="Переменные-и-константы">Переменные и константы</h3>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">a</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">10</span>
<span class="k">var</span> <span class="nv">b</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">20</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">a</span><span class="p">:</span> <span class="n">Int</span> <span class="p">=</span> <span class="m">10</span>
<span class="kd">var</span> <span class="py">b</span><span class="p">:</span> <span class="n">Int</span> <span class="p">=</span> <span class="m">20</span>
</code></pre></div></div>
<h4 id="optionals">Optionals</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">some</span><span class="p">:</span> <span class="kt">Something</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="k">var</span> <span class="nv">some</span><span class="p">:</span> <span class="kt">Something</span><span class="p">?</span> <span class="o">=</span> <span class="kt">Something</span><span class="p">(</span><span class="nv">text</span><span class="p">:</span> <span class="s">"Hello"</span><span class="p">)</span>
<span class="c1">// optional chaining</span>
<span class="k">let</span> <span class="nv">greeting</span> <span class="o">=</span> <span class="n">some</span><span class="p">?</span><span class="o">.</span><span class="nf">greet</span><span class="p">()</span>
<span class="c1">// elvis</span>
<span class="k">let</span> <span class="nv">value</span> <span class="o">=</span> <span class="n">some</span><span class="p">?</span><span class="o">.</span><span class="n">value</span> <span class="p">??</span> <span class="mi">30</span>
<span class="c1">// optional unwraping</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">some</span> <span class="o">=</span> <span class="n">some</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">some</span><span class="p">)</span> <span class="c1">// some is not optional</span>
<span class="p">}</span>
<span class="c1">// force unwraping</span>
<span class="k">let</span> <span class="nv">forced</span> <span class="o">=</span> <span class="n">some</span><span class="o">!</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">some</span><span class="p">:</span> <span class="n">Something</span><span class="p">?</span> <span class="p">=</span> <span class="n">nil</span>
<span class="kd">var</span> <span class="py">some</span><span class="p">:</span> <span class="n">Something</span><span class="p">?</span> <span class="p">=</span> <span class="n">Something</span><span class="p">(</span><span class="s">"Hello"</span><span class="p">)</span>
<span class="c1">// optional chaining
</span><span class="kd">val</span> <span class="py">greeting</span> <span class="p">=</span> <span class="n">some</span><span class="o">?.</span><span class="n">greet</span><span class="p">()</span>
<span class="c1">// elvis
</span><span class="kd">val</span> <span class="py">value</span> <span class="p">=</span> <span class="n">some</span><span class="o">?.</span><span class="n">value</span> <span class="o">?:</span> <span class="m">30</span>
<span class="c1">// optional unwraping
</span><span class="n">some</span><span class="o">?.</span><span class="n">let</span> <span class="p">{</span> <span class="n">some</span> <span class="p">-></span> <span class="c1">// it if name is not provided
</span> <span class="n">print</span><span class="p">(</span><span class="n">some</span><span class="p">)</span> <span class="c1">// some is not optional
</span><span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">some</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span> <span class="p">{</span>
<span class="n">print</span><span class="p">(</span><span class="n">some</span><span class="p">)</span> <span class="c1">// some is smart casted and not optional
</span><span class="p">}</span>
<span class="c1">// force unwraping
</span><span class="kd">val</span> <span class="py">forced</span> <span class="p">=</span> <span class="n">some</span><span class="o">!!</span>
</code></pre></div></div>
<h3 id="Форматирование-кода">Форматирование кода</h3>
<h4 id="Скобки-круглые-и-фигурные">Скобки (круглые и фигурные)</h4>
<p>Swift</p>
<ul>
<li>Круглые - не обязательны</li>
<li>Фигурные - обязательны</li>
</ul>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">str</span> <span class="o">=</span> <span class="s">"Hello, Swift"</span>
<span class="kd">func</span> <span class="nf">increse</span><span class="p">(</span><span class="n">_</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">value</span> <span class="o">+</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">statements</span><span class="p">()</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">10</span> <span class="p">{</span>
<span class="n">i</span> <span class="o">=</span> <span class="nf">increase</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
<span class="k">if</span> <span class="n">str</span> <span class="o">==</span> <span class="s">"something"</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">str</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Kotlin</p>
<ul>
<li>Круглые - обязательны</li>
<li>Фигурные - не обязательны</li>
</ul>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">str</span> <span class="p">=</span> <span class="s">"Hello, Kotlin"</span>
<span class="k">fun</span> <span class="nf">increase</span><span class="p">(</span><span class="n">value</span><span class="p">:</span> <span class="n">Int</span><span class="p">):</span> <span class="n">Int</span> <span class="p">=</span> <span class="n">value</span> <span class="p">+</span> <span class="m">1</span>
<span class="k">fun</span> <span class="nf">statements</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="py">i</span> <span class="p">=</span> <span class="m">0</span>
<span class="k">while</span> <span class="p">(</span><span class="n">i</span> <span class="p"><</span> <span class="m">10</span><span class="p">)</span> <span class="n">i</span> <span class="p">=</span> <span class="n">increase</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">str</span> <span class="p">==</span> <span class="s">"something"</span><span class="p">)</span> <span class="n">print</span><span class="p">(</span><span class="n">str</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="Управляющие-конструкции">Управляющие конструкции</h3>
<h4 id="pattern-matching">Pattern Matching</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">number</span> <span class="o">=</span> <span class="mi">42</span>
<span class="k">switch</span> <span class="n">number</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">0</span><span class="o">...</span><span class="mi">7</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">9</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"1 digit"</span><span class="p">)</span>
<span class="k">case</span> <span class="mi">10</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"2 digits"</span><span class="p">)</span>
<span class="k">case</span> <span class="mi">11</span><span class="o">...</span><span class="mi">99</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"2 digits"</span><span class="p">)</span>
<span class="k">case</span> <span class="mi">100</span><span class="o">...</span><span class="mi">999</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"3 digits"</span><span class="p">)</span>
<span class="k">default</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"4 or more digits"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">number</span> <span class="p">=</span> <span class="m">42</span>
<span class="k">when</span> <span class="p">(</span><span class="n">number</span><span class="p">)</span> <span class="p">{</span>
<span class="k">in</span> <span class="m">0</span><span class="o">..</span><span class="m">7</span><span class="p">,</span> <span class="m">8</span><span class="p">,</span> <span class="m">9</span> <span class="p">-></span> <span class="n">println</span><span class="p">(</span><span class="s">"1 digit"</span><span class="p">)</span>
<span class="m">10</span> <span class="p">-></span> <span class="n">println</span><span class="p">(</span><span class="s">"2 digits"</span><span class="p">)</span>
<span class="k">in</span> <span class="m">11</span><span class="o">..</span><span class="m">99</span> <span class="p">-></span> <span class="n">println</span><span class="p">(</span><span class="s">"2 digits"</span><span class="p">)</span>
<span class="k">in</span> <span class="m">100</span><span class="o">..</span><span class="m">999</span> <span class="p">-></span> <span class="n">println</span><span class="p">(</span><span class="s">"3 digits"</span><span class="p">)</span>
<span class="k">else</span> <span class="p">-></span> <span class="n">println</span><span class="p">(</span><span class="s">"4 or more digits"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="if-when--expressions-not-statements-">if, when = expressions, not statements (!)</h4>
<p>Swift</p>
<ul>
<li>Не применимо</li>
</ul>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">test</span> <span class="p">=</span> <span class="k">if</span> <span class="p">(</span><span class="k">true</span><span class="p">)</span> <span class="s">"Test"</span> <span class="k">else</span> <span class="s">"False"</span>
<span class="kd">val</span> <span class="py">state</span> <span class="p">=</span> <span class="n">State</span><span class="p">.</span><span class="n">Off</span>
<span class="k">fun</span> <span class="nf">decide</span><span class="p">()</span> <span class="p">=</span> <span class="k">when</span><span class="p">(</span><span class="n">state</span><span class="p">)</span> <span class="p">{</span>
<span class="n">State</span><span class="p">.</span><span class="n">Off</span> <span class="p">-></span> <span class="s">"Off"</span>
<span class="n">State</span><span class="p">.</span><span class="n">On</span> <span class="p">-></span> <span class="s">"On"</span>
<span class="p">}</span>
<span class="kd">val</span> <span class="py">decision</span> <span class="p">=</span> <span class="n">decide</span><span class="p">()</span>
</code></pre></div></div>
<h3 id="Типы">Типы</h3>
<h4 id="Любой-тип">Любой тип</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">a</span><span class="p">:</span> <span class="kt">Any</span> <span class="o">=</span> <span class="s">"Hello"</span>
<span class="k">let</span> <span class="nv">b</span><span class="p">:</span> <span class="kt">AnyObject</span> <span class="o">=</span> <span class="kt">SomeClass</span><span class="p">()</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">a</span><span class="p">:</span> <span class="n">Any</span> <span class="p">=</span> <span class="s">"Hello"</span>
<span class="c1">// no AnyObject yet
</span></code></pre></div></div>
<h4 id="Строковая-интерполяция">Строковая интерполяция</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">str</span> <span class="o">=</span> <span class="s">"Hello </span><span class="se">\(</span><span class="n">nickname</span><span class="se">)</span><span class="s">"</span>
<span class="k">let</span> <span class="nv">str2</span> <span class="o">=</span> <span class="s">"Hello </span><span class="se">\(</span><span class="n">user</span><span class="o">.</span><span class="n">nickname</span><span class="se">)</span><span class="s">"</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">str</span> <span class="p">=</span> <span class="s">"Hello $nickname"</span>
<span class="kd">val</span> <span class="py">str2</span> <span class="p">=</span> <span class="s">"Hello ${user.nickname}"</span>
</code></pre></div></div>
<h4 id="Интервалы-ranges">Интервалы (Ranges)</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">rng1</span> <span class="o">=</span> <span class="mi">0</span><span class="o">...</span><span class="mi">10</span>
<span class="k">let</span> <span class="nv">rng2</span> <span class="o">=</span> <span class="mi">0</span><span class="o">..<</span><span class="mi">10</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">rng1</span> <span class="p">=</span> <span class="m">0</span><span class="o">..</span><span class="m">10</span>
<span class="c1">// no rng2
</span></code></pre></div></div>
<h4 id="Коллекции">Коллекции</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">stringArray</span> <span class="o">=</span> <span class="p">[</span><span class="kt">String</span><span class="p">]()</span>
<span class="c1">// no stringList</span>
<span class="k">let</span> <span class="nv">stringFloatMap</span> <span class="o">=</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Float</span><span class="p">]()</span>
<span class="k">let</span> <span class="nv">stringSet</span> <span class="o">=</span> <span class="kt">Set</span><span class="o"><</span><span class="kt">String</span><span class="o">></span><span class="p">()</span>
<span class="k">var</span> <span class="nv">countries</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="s">"Switzerland"</span><span class="p">,</span> <span class="s">"France"</span><span class="p">,</span> <span class="s">"Germany"</span><span class="p">]</span>
<span class="n">countries</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="s">"Italy"</span><span class="p">)</span>
<span class="n">countries</span><span class="o">.</span><span class="nf">remove</span><span class="p">(</span><span class="s">"France"</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">jobs</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
<span class="s">"Roger"</span><span class="p">:</span> <span class="s">"CEO"</span><span class="p">,</span>
<span class="s">"Martin"</span><span class="p">:</span> <span class="s">"CTO"</span>
<span class="p">]</span>
<span class="n">jobs</span><span class="p">[</span><span class="s">"Adrian"</span><span class="p">]</span> <span class="o">=</span> <span class="s">"Writer"</span>
</code></pre></div></div>
<p>Kotlin</p>
<ul>
<li>Нельзя использовать [] для инициализации массива, используется arrayOf</li>
<li>в arrayOf добавлять и удалять элементы нельзя, можно только их менять</li>
</ul>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">stringArray</span> <span class="p">=</span> <span class="n">arrayOf</span><span class="p"><</span><span class="n">String</span><span class="p">>()</span>
<span class="kd">val</span> <span class="py">stringList</span> <span class="p">=</span> <span class="n">listOf</span><span class="p"><</span><span class="n">String</span><span class="p">>()</span>
<span class="kd">val</span> <span class="py">stringFloatMap</span> <span class="p">=</span> <span class="n">mapOf</span><span class="p"><</span><span class="n">String</span><span class="p">,</span> <span class="n">Float</span><span class="p">>()</span>
<span class="kd">val</span> <span class="py">stringSet</span> <span class="p">=</span> <span class="n">setOf</span><span class="p"><</span><span class="n">String</span><span class="p">>()</span>
<span class="kd">val</span> <span class="py">countries</span> <span class="p">=</span> <span class="n">mutableListOf</span><span class="p"><</span><span class="n">String</span><span class="p">>(</span><span class="s">"Switzerland"</span><span class="p">,</span> <span class="s">"France"</span><span class="p">,</span> <span class="s">"Germany"</span><span class="p">)</span>
<span class="n">countries</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="s">"Italy"</span><span class="p">)</span>
<span class="n">countries</span><span class="p">.</span><span class="n">remove</span><span class="p">(</span><span class="s">"France"</span><span class="p">)</span>
<span class="kd">val</span> <span class="py">jobs</span> <span class="p">=</span> <span class="n">mutableMapOf</span><span class="p">(</span>
<span class="s">"Roger"</span> <span class="n">to</span> <span class="s">"CEO"</span><span class="p">,</span>
<span class="s">"Martin"</span> <span class="n">to</span> <span class="s">"CTO"</span>
<span class="p">)</span>
<span class="n">jobs</span><span class="p">[</span><span class="s">"Adrian"</span><span class="p">]</span> <span class="p">=</span> <span class="s">"Writer"</span>
</code></pre></div></div>
<h4 id="Итерирование-коллекций">Итерирование коллекций</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">str</span> <span class="k">in</span> <span class="n">stringArray</span> <span class="p">{}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">index</span><span class="p">,</span> <span class="n">str</span><span class="p">)</span> <span class="k">in</span> <span class="n">stringArray</span><span class="o">.</span><span class="nf">enumerated</span><span class="p">()</span> <span class="p">{}</span>
<span class="c1">// no for str in stringList</span>
<span class="k">for</span> <span class="p">(</span><span class="n">str</span><span class="p">,</span> <span class="n">num</span><span class="p">)</span> <span class="k">in</span> <span class="n">stringFloatMap</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">str</span> <span class="k">in</span> <span class="n">stringSet</span> <span class="p">{}</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="n">str</span> <span class="k">in</span> <span class="n">stringArray</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">str</span> <span class="k">in</span> <span class="n">stringList</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">for</span> <span class="p">((</span><span class="n">str</span><span class="p">,</span> <span class="n">num</span><span class="p">)</span> <span class="k">in</span> <span class="n">stringFloatMap</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">str</span> <span class="k">in</span> <span class="n">stringSet</span><span class="p">)</span> <span class="p">{}</span>
</code></pre></div></div>
<h3 id="Процедурное-программирование">Процедурное программирование</h3>
<h4 id="Процедуры-и-функции">Процедуры и функции</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">method1</span><span class="p">(</span><span class="nv">input</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">input</span><span class="o">.</span><span class="n">count</span>
<span class="p">}</span>
<span class="c1">// no method2</span>
<span class="kd">func</span> <span class="n">method3</span><span class="o"><</span><span class="kt">T</span><span class="o">></span><span class="p">(</span><span class="nv">input</span><span class="p">:</span> <span class="kt">T</span><span class="p">)</span> <span class="o">-></span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">String</span><span class="p">(</span><span class="nv">describing</span><span class="p">:</span> <span class="n">input</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">method1</span><span class="p">(</span><span class="n">input</span><span class="p">:</span> <span class="n">String</span><span class="p">):</span> <span class="n">Int</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">input</span><span class="p">.</span><span class="n">length</span>
<span class="p">}</span>
<span class="k">fun</span> <span class="nf">method2</span><span class="p">(</span><span class="n">input</span><span class="p">:</span> <span class="n">String</span><span class="p">)</span> <span class="p">=</span> <span class="n">input</span><span class="p">.</span><span class="n">length</span>
<span class="k">fun</span> <span class="err"><</span><span class="nf">T</span><span class="p">></span><span class="n">method3</span><span class="p">(</span><span class="n">input</span><span class="p">:</span> <span class="n">T</span><span class="p">)</span> <span class="p">=</span> <span class="n">input</span><span class="p">.</span><span class="n">toString</span><span class="p">()</span>
</code></pre></div></div>
<h3 id="ООП">ООП</h3>
<h4 id="Интерфейсы">Интерфейсы</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">Person</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="k">set</span> <span class="p">}</span>
<span class="kd">func</span> <span class="nf">greet</span><span class="p">()</span> <span class="o">-></span> <span class="kt">String</span>
<span class="kd">func</span> <span class="nf">showMoreInformation</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">extension</span> <span class="kt">Person</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">greet</span><span class="p">()</span> <span class="o">-></span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">return</span> <span class="s">"Hello! I am </span><span class="se">\(</span><span class="k">self</span><span class="se">)</span><span class="s">"</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">interface</span> <span class="nc">Person</span> <span class="p">{</span>
<span class="kd">var</span> <span class="py">name</span><span class="p">:</span> <span class="n">String</span>
<span class="k">get</span> <span class="k">set</span>
<span class="k">fun</span> <span class="nf">greet</span><span class="p">()</span> <span class="p">=</span> <span class="s">"Hello! I am $this"</span>
<span class="k">fun</span> <span class="nf">showMoreInformation</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="Конструкторы">Конструкторы</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">Manager</span><span class="p">:</span> <span class="kt">Person</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">backingName</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">var</span> <span class="nv">staff</span><span class="p">:</span> <span class="p">[</span><span class="kt">Person</span><span class="p">]</span>
<span class="k">var</span> <span class="nv">state</span><span class="p">:</span> <span class="kt">State</span>
<span class="k">let</span> <span class="nv">isActive</span><span class="p">:</span> <span class="kt">Bool</span>
<span class="nf">init</span><span class="p">(</span><span class="n">backingName</span> <span class="o">=</span> <span class="s">""</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">staff</span><span class="p">:</span> <span class="p">[</span><span class="kt">Person</span><span class="p">]</span> <span class="o">=</span> <span class="p">[],</span> <span class="nv">state</span><span class="p">:</span> <span class="kt">State</span> <span class="o">=</span> <span class="o">.</span><span class="n">off</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">backingName</span> <span class="o">=</span> <span class="n">backingName</span>
<span class="k">self</span><span class="o">.</span><span class="n">staff</span> <span class="o">=</span> <span class="n">staff</span>
<span class="k">self</span><span class="o">.</span><span class="n">state</span> <span class="o">=</span> <span class="n">state</span>
<span class="k">self</span><span class="o">.</span><span class="n">isActive</span> <span class="o">=</span> <span class="kc">true</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nc">Manager</span><span class="p">(</span><span class="k">private</span> <span class="kd">var</span> <span class="py">backingName</span><span class="p">:</span> <span class="n">String</span> <span class="p">=</span> <span class="s">""</span><span class="p">,</span>
<span class="k">private</span> <span class="kd">var</span> <span class="py">staff</span><span class="p">:</span> <span class="n">MutableList</span><span class="p"><</span><span class="n">Person</span><span class="p">></span> <span class="p">=</span> <span class="n">mutableListOf</span><span class="p"><</span><span class="n">Person</span><span class="p">>(),</span>
<span class="kd">var</span> <span class="py">state</span><span class="p">:</span> <span class="n">State</span> <span class="p">=</span> <span class="n">State</span><span class="p">.</span><span class="n">Off</span><span class="p">)</span> <span class="p">:</span> <span class="n">Person</span> <span class="p">{</span>
<span class="k">private</span> <span class="kd">val</span> <span class="py">isActive</span><span class="p">:</span> <span class="n">Boolean</span>
<span class="n">init</span> <span class="p">{</span>
<span class="n">isActive</span> <span class="p">=</span> <span class="k">true</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="data-classes-ponsoposs-vs-pojo">Data Classes (PONSO/POSS vs POJO)</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Employee</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">backingName</span><span class="p">:</span> <span class="kt">String</span>
<span class="k">let</span> <span class="nv">age</span><span class="p">:</span> <span class="kt">Int</span>
<span class="nf">init</span><span class="p">(</span><span class="nv">backingName</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">""</span><span class="p">,</span> <span class="nv">age</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">30</span><span class="p">)</span> <span class="p">{</span>
<span class="k">self</span><span class="o">.</span><span class="n">backingName</span> <span class="o">=</span> <span class="n">backingName</span>
<span class="k">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">data class</span> <span class="nc">Employee</span><span class="p">(</span><span class="k">private</span> <span class="kd">var</span> <span class="py">backingName</span><span class="p">:</span> <span class="n">String</span> <span class="p">=</span> <span class="s">""</span><span class="p">,</span>
<span class="kd">var</span> <span class="py">age</span><span class="p">:</span> <span class="n">Int</span> <span class="p">=</span> <span class="m">30</span><span class="p">)</span> <span class="p">:</span> <span class="n">Person</span> <span class="p">{</span>
</code></pre></div></div>
<h4 id="Инстанцирование-объектов">Инстанцирование объектов</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">person</span> <span class="o">=</span> <span class="kt">Employee</span><span class="p">(</span>
<span class="nv">name</span><span class="p">:</span> <span class="s">"Olivia"</span><span class="p">,</span>
<span class="nv">age</span><span class="p">:</span> <span class="mi">45</span>
<span class="p">)</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">person1</span> <span class="p">=</span> <span class="n">Employee</span><span class="p">(</span><span class="s">"Olivia"</span><span class="p">,</span> <span class="m">45</span><span class="p">)</span>
<span class="kd">val</span> <span class="py">person2</span> <span class="p">=</span> <span class="n">Employee</span><span class="p">().</span><span class="n">apply</span> <span class="p">{</span>
<span class="n">name</span> <span class="p">=</span> <span class="s">"Thomas"</span>
<span class="n">age</span> <span class="p">=</span> <span class="m">56</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="Расширения-классов">Расширения классов</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Double</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">fahrenheit</span><span class="p">:</span> <span class="kt">Double</span> <span class="p">{</span>
<span class="p">(</span><span class="k">self</span> <span class="o">*</span> <span class="mi">9</span> <span class="o">/</span> <span class="mi">5</span><span class="p">)</span> <span class="o">+</span> <span class="mi">32</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">celsius</span><span class="p">:</span> <span class="kt">Double</span> <span class="p">{</span>
<span class="p">(</span><span class="k">self</span> <span class="o">-</span> <span class="mi">32</span><span class="p">)</span> <span class="o">*</span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">9</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">let</span> <span class="nv">temperature</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">32.0</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">fahrenheit</span> <span class="o">=</span> <span class="n">temperature</span><span class="o">.</span><span class="n">fahrenheit</span>
<span class="k">let</span> <span class="nv">celsius</span> <span class="o">=</span> <span class="n">fahrenheit</span><span class="o">.</span><span class="n">celsius</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">temperature</span><span class="se">)</span><span class="s"> degrees Celsius is </span><span class="se">\(</span><span class="n">fahrenheit</span><span class="se">)</span><span class="s"> degrees Fahrenheit"</span><span class="p">)</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">Double</span><span class="p">.</span><span class="n">fahrenheit</span><span class="p">:</span> <span class="n">Double</span> <span class="k">get</span><span class="p">()</span> <span class="p">=</span> <span class="p">(</span><span class="k">this</span> <span class="p">*</span> <span class="m">9</span> <span class="p">/</span> <span class="m">5</span><span class="p">)</span> <span class="p">+</span> <span class="m">32</span>
<span class="kd">val</span> <span class="py">Double</span><span class="p">.</span><span class="n">celsius</span><span class="p">:</span> <span class="n">Double</span> <span class="k">get</span><span class="p">()</span> <span class="p">=</span> <span class="p">(</span><span class="k">this</span> <span class="p">-</span> <span class="m">32</span><span class="p">)</span> <span class="p">*</span> <span class="m">5</span> <span class="p">/</span> <span class="m">9</span>
<span class="kd">val</span> <span class="py">temperature</span><span class="p">:</span> <span class="n">Double</span> <span class="p">=</span> <span class="m">32.0</span>
<span class="kd">val</span> <span class="py">fahrenheit</span> <span class="p">=</span> <span class="n">temperature</span><span class="p">.</span><span class="n">fahrenheit</span>
<span class="kd">val</span> <span class="py">celsius</span> <span class="p">=</span> <span class="n">fahrenheit</span><span class="p">.</span><span class="n">celsius</span>
<span class="n">println</span><span class="p">(</span><span class="s">"$temperature degrees Celsius is $fahrenheit degrees Fahrenheit"</span><span class="p">)</span>
</code></pre></div></div>
<h4 id="Простые-объекты-и-singletonы">Простые объекты и Singleton’ы</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">Constants</span> <span class="p">{</span>
<span class="kd">static</span> <span class="k">let</span> <span class="nv">PI</span> <span class="o">=</span> <span class="mf">3.14</span>
<span class="kd">static</span> <span class="k">let</span> <span class="nv">ANSWER</span> <span class="o">=</span> <span class="mi">42</span>
<span class="kd">static</span> <span class="kd">func</span> <span class="nf">name</span><span class="p">()</span> <span class="o">-></span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">return</span> <span class="s">"Math constants"</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">object</span> <span class="nc">Constants</span> <span class="p">{</span>
<span class="kd">val</span> <span class="py">PI</span> <span class="p">=</span> <span class="m">3.14</span>
<span class="kd">val</span> <span class="py">ANSWER</span> <span class="p">=</span> <span class="m">42</span>
<span class="k">fun</span> <span class="nf">name</span><span class="p">()</span> <span class="p">=</span> <span class="s">"Math contstants"</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="Объекты-компаньоны">Объекты-компаньоны</h4>
<p>Swift</p>
<ul>
<li>Просто используйте static (class) члены</li>
</ul>
<p>Kotlin</p>
<ul>
<li>В Kotlin нет статических членов</li>
</ul>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">companion</span> <span class="k">object</span> <span class="n">OptionalName</span> <span class="p">{</span>
<span class="kd">val</span> <span class="py">MAXIMUM_EMPLOYEE_COUNT</span> <span class="p">=</span> <span class="m">10</span>
<span class="k">fun</span> <span class="nf">managerFactory</span><span class="p">()</span> <span class="p">=</span> <span class="n">Manager</span><span class="p">(</span><span class="s">"Maria Hill"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<h4 id="Перегрузка-операторов">Перегрузка операторов</h4>
<p>Swift</p>
<ul>
<li>Можно делать свои операторы (например квадратный корень)</li>
</ul>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="o">+</span> <span class="p">(</span><span class="nv">lhs</span><span class="p">:</span> <span class="kt">Person</span><span class="p">,</span> <span class="nv">rhs</span><span class="p">:</span> <span class="kt">Person</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Team</span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">Team</span><span class="p">(</span><span class="n">lhs</span><span class="p">,</span> <span class="n">rhs</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">let</span> <span class="nv">team</span> <span class="o">=</span> <span class="n">manager</span> <span class="o">+</span> <span class="n">person</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">operator</span> <span class="k">fun</span> <span class="nf">plus</span><span class="p">(</span><span class="n">person</span><span class="p">:</span> <span class="n">Person</span><span class="p">):</span> <span class="n">Team</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">Team</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="n">person</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">val</span> <span class="py">team</span> <span class="p">=</span> <span class="n">manager</span> <span class="p">+</span> <span class="n">person</span>
</code></pre></div></div>
<h4 id="Инфиксные-методы">Инфиксные методы</h4>
<p>Swift</p>
<ul>
<li>Не применимо</li>
</ul>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">infix</span> <span class="k">fun</span> <span class="nf">addPerson</span><span class="p">(</span><span class="n">person</span><span class="p">:</span> <span class="n">Person</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">staff</span><span class="p">.</span><span class="n">count</span><span class="p">()</span> <span class="p"><</span> <span class="n">MAXIMUM_EMPLOYEE_COUNT</span><span class="p">)</span> <span class="p">{</span>
<span class="n">staff</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="k">throw</span> <span class="n">Exception</span><span class="p">(</span><span class="s">"Cannot add more staff members"</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">manager</span> <span class="n">addPerson</span> <span class="n">person</span>
</code></pre></div></div>
<h4 id="Перечисления">Перечисления</h4>
<p>Swift</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">State</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">on</span>
<span class="k">case</span> <span class="n">off</span>
<span class="k">var</span> <span class="nv">desc</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="kd">func</span> <span class="nf">notify</span><span class="p">()</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Kotlin</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">enum</span> <span class="kd">class</span> <span class="nc">State</span> <span class="p">{</span>
<span class="n">On</span><span class="p">,</span> <span class="n">Off</span>
<span class="kd">var</span> <span class="py">desc</span><span class="p">:</span> <span class="n">String</span> <span class="p">{</span> <span class="o">..</span><span class="p">.</span> <span class="p">}</span>
<span class="n">func</span> <span class="n">notify</span><span class="p">()</span> <span class="p">=</span> <span class="o">..</span><span class="p">.</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="Резюмирующая-табличка">Резюмирующая табличка</h3>
<table>
<thead>
<tr>
<th> </th>
<th>Kotlin 1.2</th>
<th>Objective-C 2.0</th>
<th>Swift 4</th>
</tr>
</thead>
<tbody>
<tr>
<td>Наследование</td>
<td>Одинарное, с интерфейсами и расширениями</td>
<td>Одинарное, с протоколами</td>
<td>Одинарное, с протоколами и расширениями</td>
</tr>
<tr>
<td>Точки с запятой <code class="highlighter-rouge">;</code></td>
<td>Не обязательны</td>
<td>Обязательны</td>
<td>Не обязательны</td>
</tr>
<tr>
<td>Объявление класса</td>
<td><code class="highlighter-rouge">class</code></td>
<td><code class="highlighter-rouge"><span class="k">@interface</span> <span class="err">&</span> <span class="err">@</span><span class="nc">implementation</span></code></td>
<td><code class="highlighter-rouge">class</code></td>
</tr>
<tr>
<td>Интерфейсы</td>
<td>реализует <code class="highlighter-rouge">interface</code></td>
<td>поддерживает <code class="highlighter-rouge"><span class="k">@protocol</span></code></td>
<td>поддерживает <code class="highlighter-rouge">protocol</code></td>
</tr>
<tr>
<td>Подключение кода</td>
<td><code class="highlighter-rouge">import (символы)</code></td>
<td><code class="highlighter-rouge">#import (файлы)</code></td>
<td><code class="highlighter-rouge">import (символы)</code></td>
</tr>
<tr>
<td>Расширение классов</td>
<td>Расширения</td>
<td>Категории</td>
<td>Расширения</td>
</tr>
<tr>
<td>Динамическая типизация</td>
<td><code class="highlighter-rouge">Any</code></td>
<td><code class="highlighter-rouge">id</code></td>
<td><code class="highlighter-rouge">Any</code></td>
</tr>
<tr>
<td>Суффикс приватных полей</td>
<td>Не используется</td>
<td><code class="highlighter-rouge">_</code> (подчеркивание)</td>
<td><code class="highlighter-rouge">_</code> (подчеркивание)</td>
</tr>
<tr>
<td>Управление памятью</td>
<td>Сборка мусора (GС)</td>
<td>Ручное или автоматическое управление памятью (MRC vs ARC)</td>
<td>Автоматическое управление памятью (ARC)</td>
</tr>
<tr>
<td>Дженерики</td>
<td>да (type erasure)</td>
<td>да (type erasure)</td>
<td>да</td>
</tr>
<tr>
<td>Указатели на метод</td>
<td>нет</td>
<td><code class="highlighter-rouge">@selector</code></td>
<td><code class="highlighter-rouge">#selector</code></td>
</tr>
<tr>
<td>Обратный вызов (callbacks)</td>
<td>Лямбды</td>
<td>Делегаты и блоки</td>
<td>замыкания</td>
</tr>
<tr>
<td>Указатели</td>
<td>нет</td>
<td>да</td>
<td>Через библиотечные классы</td>
</tr>
<tr>
<td>Корневые классы</td>
<td><code class="highlighter-rouge">Object</code></td>
<td><code class="highlighter-rouge">NSObject</code> / <code class="highlighter-rouge">NSProxy</code> / …</td>
<td><code class="highlighter-rouge">NSObject</code> / <code class="highlighter-rouge">NSProxy</code> / …</td>
</tr>
<tr>
<td>Управление областью видимости</td>
<td><code class="highlighter-rouge">public</code> / <code class="highlighter-rouge">internal</code> / <code class="highlighter-rouge">protected</code> / <code class="highlighter-rouge">private</code></td>
<td><code class="highlighter-rouge">@public</code> / <code class="highlighter-rouge">@protected</code> / <code class="highlighter-rouge">@private</code> (только поля)</td>
<td><code class="highlighter-rouge">open</code> / <code class="highlighter-rouge">public</code> / <code class="highlighter-rouge">internal</code> / <code class="highlighter-rouge">fileprivate</code> / <code class="highlighter-rouge">private</code></td>
</tr>
<tr>
<td>Обработка ошибок</td>
<td><code class="highlighter-rouge">try</code> / <code class="highlighter-rouge">catch</code> / <code class="highlighter-rouge">finally</code> + <code class="highlighter-rouge">Exception</code></td>
<td><code class="highlighter-rouge">@try</code> / <code class="highlighter-rouge">@catch</code> / <code class="highlighter-rouge">@finally</code> + <code class="highlighter-rouge">NSException</code></td>
<td><code class="highlighter-rouge">do</code> / <code class="highlighter-rouge">try</code> / <code class="highlighter-rouge">catch</code> + <code class="highlighter-rouge">Error</code></td>
</tr>
<tr>
<td>Пространства имен</td>
<td>Пакеты</td>
<td>Через префиксы классов</td>
<td>Неявные, через модули</td>
</tr>
<tr>
<td>Грамматика</td>
<td>kotlinlang.org</td>
<td> </td>
<td>developer.apple.com</td>
</tr>
</tbody>
</table>
Разрабатываем и отлаживаем С++ в Docker с помощью VSCode2018-01-06T00:00:00+00:00http://mrdekk.ru/2018/01/06/vscode-cpp-docker-debugging<p>Продолжаем цикл статей “Как кодить на С++ под OSX для Ubuntu”. После сборки cross-toolchain’ов стояла задача научиться собирать, запускать и отлаживать С++ под OSX для Linux (Ubuntu в данном случае). Как оказалось cross-toolchain был не нужен :(. Но об этом позже.</p>
<p>Для начала нужна была IDE, в которой можно было бы комфортно редактировать код и отлаживаться. Претенденты были такие: Xcode, Eclipse (CDT), CLion, чуть позже к ним добавился VSCode и впоследствии выиграл соревнование.</p>
<p>Постановка задачи была такая: C++, CMake, Linux, при этом редактирование кода и отладка должна работать под OSX.</p>
<p>Xcode поэтому выпал из этой гонки сразу, он есть очень удобная IDE для C++, но в целом в основном для OSX. CMake наверное завести можно, но это будет большое количество сторонних средств.</p>
<p>CLion выпал из этой же гонки примерно по тем же причинам (хотя он вроде умеет в CMake). Плюс я не смог в нем нормально подружиться с Docker.</p>
<p>Остался Eclipse CDT, в нем получилось практически все что требовалось (с cross-toolchain), завелась даже отладка через cross-gdb, однако были странные махинации чтобы нормально работать с CMake и контейнером он управлял относительно сам (не совсем прозрачно), хотя надо отметить что поддержка Docker в Eclipse плюс-минус хорошая.</p>
<p>После этого я пошел посерфить интеренет в поисках каких-нибудь идей, как нормально подружить Eclipse и CMake, и тут я наткнулся на VSCode. Которые не позиционирует себя как IDE, но умеет в дебаг и всякие user-defined task’и. Несмотря на мое некоторое отношение к продуктам Microsoft, я решил дать этому редактору шанс и попробовал его… и надо признать, это очень хороший инструмент.</p>
<p>Теперь же собственно, как завести, чтобы все работало.</p>
<p>Для начала нам понадобится собственно VSCode, настроенный так, как вам наиболее удобно. Можно сразу поставить расширения для работы с C++ (C/C++, Native Debug), CMake (CMake, CMake Tools), Docker.</p>
<p>Далее нам потребуется такая структура проекта (в принципе вы можете выбрать такую структуру, какая вам больше нравится, это скажем так рабочий пример):</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.vscode
tasks.json
launch.json
build
CMakeLists.txt
dbuild.sh
ddebugger.sh
dmaker.sh
Dockerfile
main.cpp
</code></pre></div></div>
<p>Начнем с С++, пример простой, однако чтобы понять что мы действительно работаем для Linux я добавил несколько linux’овых вещей.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include <unistd.h>
#include <limits.h>
</span>
<span class="cp">#include <iostream>
</span>
<span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="kt">char</span> <span class="n">hostname</span><span class="p">[</span><span class="n">HOST_NAME_MAX</span><span class="p">];</span>
<span class="kt">char</span> <span class="n">username</span><span class="p">[</span><span class="n">LOGIN_NAME_MAX</span><span class="p">];</span>
<span class="n">gethostname</span><span class="p">(</span><span class="n">hostname</span><span class="p">,</span> <span class="n">HOST_NAME_MAX</span><span class="p">);</span>
<span class="n">getlogin_r</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="n">LOGIN_NAME_MAX</span><span class="p">);</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="s">"hostname: "</span> <span class="o"><<</span> <span class="n">hostname</span> <span class="o"><<</span> <span class="s">", login: "</span> <span class="o"><<</span> <span class="n">username</span> <span class="o"><<</span> <span class="n">endl</span><span class="p">;</span> <span class="c1">// prints Hello World!
</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Этот код мы будем собирать, запускать и отлаживать.</p>
<p>Далее CMakeLists.txt, который тоже простой и относительно шаблонный:</p>
<div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cmake_minimum_required</span><span class="p">(</span>VERSION 3.5<span class="p">)</span>
<span class="nb">project</span><span class="p">(</span>HWord<span class="p">)</span>
<span class="nb">set</span><span class="p">(</span>HWORD_VERSION_MAJOR 1<span class="p">)</span>
<span class="nb">set</span><span class="p">(</span>HWORD_VERSION_MINOR 0<span class="p">)</span>
<span class="nb">set</span><span class="p">(</span>SOURCE <span class="si">${</span><span class="nv">PROJECT_SOURCE_DIR</span><span class="si">}</span>/main.cpp<span class="p">)</span>
<span class="nb">add_executable</span><span class="p">(</span><span class="si">${</span><span class="nv">PROJECT_NAME</span><span class="si">}</span> <span class="si">${</span><span class="nv">SOURCE</span><span class="si">}</span><span class="p">)</span>
</code></pre></div></div>
<h2 id="Собираем">Собираем</h2>
<p>Как я уже выше писал сначала я пытался собрать все это безобразие под OSX с помощью cross-toolchain. Это плюс-минус работало, но часто приходилось пересобирать toolchain с еще каким-нибудь волшебным флагом, а на моем машине это делается примерно 3 часа. В одну из таких пересборок мне пришла мысль, а почему бы не собирать docker’ом, т.е. gcc который в контейнере и в требуемой системе (linux).</p>
<p>Таким образом собираем такой контейнер:</p>
<p>Dockerfile</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>FROM ubuntu:16.04
RUN apt-get update
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y gcc g++ gdb cmake
WORKDIR /opt/build
VOLUME ["/opt"]
</code></pre></div></div>
<p>Для упрощения работы с командной строкой docker’а заведем пару скриптов:</p>
<p><strong>Скрипт сборки образа:</strong></p>
<p>dbuild.sh</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nv">cwd</span><span class="o">=</span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span>
docker build <span class="se">\</span>
<span class="nt">-t</span> mrdekk/maker <span class="se">\</span>
<span class="nb">.</span>
</code></pre></div></div>
<p>Ничего сверхординарного, но возможно вам лучше поставить свой тег, вместо <code class="highlighter-rouge">mrdekk/maker</code> сделать <code class="highlighter-rouge">%username%/maker</code></p>
<p><strong>Скрипт запуска сборок:</strong></p>
<p>dmaker.sh</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nv">cwd</span><span class="o">=</span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span>
docker stop maker
docker rm maker
docker run <span class="se">\</span>
<span class="nt">-it</span> <span class="se">\</span>
<span class="nt">--name</span> maker <span class="se">\</span>
<span class="nt">-p</span> 6666:6666 <span class="se">\</span>
<span class="nt">-v</span> <span class="k">${</span><span class="nv">cwd</span><span class="k">}</span>:/opt <span class="se">\</span>
<span class="nt">--privileged</span> <span class="se">\</span>
mrdekk/maker <span class="se">\</span>
<span class="s2">"</span><span class="k">${</span><span class="p">@</span><span class="k">}</span><span class="s2">"</span>
</code></pre></div></div>
<p><strong>Скрипт запуска контейнера для отладки:</strong></p>
<p>ddebugger.sh</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="nv">cwd</span><span class="o">=</span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span>
docker stop maker
docker rm maker
docker run <span class="se">\</span>
<span class="nt">-dt</span> <span class="se">\</span>
<span class="nt">--name</span> maker <span class="se">\</span>
<span class="nt">-p</span> 6666:6666 <span class="se">\</span>
<span class="nt">-v</span> <span class="k">${</span><span class="nv">cwd</span><span class="k">}</span>:/opt <span class="se">\</span>
<span class="nt">--privileged</span> <span class="se">\</span>
mrdekk/maker <span class="se">\</span>
<span class="s2">"</span><span class="k">${</span><span class="p">@</span><span class="k">}</span><span class="s2">"</span>
</code></pre></div></div>
<p>Скрипты запуска сборки и дебаггера в общем отличаются только ключами -it и -dt.</p>
<p>Теперь сделаем несколько задач (tasks) для VSCode</p>
<p><strong>Задача сборки контейнера:</strong></p>
<p>Ничего особенного, вызываем скрипт dbuild.sh</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="s2">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Docker: Build Containers"</span><span class="p">,</span><span class="w">
</span><span class="s2">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${workspaceFolder}/dbuild.sh"</span><span class="p">,</span><span class="w">
</span><span class="p">},</span><span class="w">
</span></code></pre></div></div>
<p>И заодно выполняем ее, после этого можете сделать <code class="highlighter-rouge">docker images</code> и проверить, что образ mrdekk/maker появился</p>
<p><strong>Задача для CMake:</strong></p>
<p>Теперь с помощью CMake сгенерим Makefile’ы для сборки проекта. Здесь мы воспользуемся скриптом dmaker.sh, но будем вызывать его с набором ключей:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="s2">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CMake: Initialize"</span><span class="p">,</span><span class="w">
</span><span class="s2">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${workspaceFolder}/dmaker.sh"</span><span class="p">,</span><span class="w">
</span><span class="s2">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"cmake"</span><span class="p">,</span><span class="w"> </span><span class="s2">"-G"</span><span class="p">,</span><span class="w"> </span><span class="s2">"'Unix Makefiles'"</span><span class="p">,</span><span class="w"> </span><span class="s2">"-DCMAKE_BUILD_TYPE=Debug"</span><span class="p">,</span><span class="w"> </span><span class="s2">"/opt"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="s2">"options"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${workspaceFolder}"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">},</span><span class="w">
</span></code></pre></div></div>
<p><code class="highlighter-rouge">/opt</code> это путь до Volume’а в контейнере в который мы будем проецировать текущую папку с исходниками проекта - это в общем часть магии.</p>
<p>После этого в подпапке build вы должны увидеть сгенерированные cmake’ом файлы.</p>
<p><strong>Задача для сборки бинарника:</strong></p>
<p>Здесь снова используем скрипт dmaker.sh но с другими ключами</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="s2">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Make: Build Project"</span><span class="p">,</span><span class="w">
</span><span class="s2">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${workspaceFolder}/dmaker.sh"</span><span class="p">,</span><span class="w">
</span><span class="s2">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"make"</span><span class="p">,</span><span class="w"> </span><span class="s2">"-j"</span><span class="p">,</span><span class="w"> </span><span class="s2">"8"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="s2">"options"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${workspaceFolder}"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="s2">"group"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"kind"</span><span class="p">:</span><span class="w"> </span><span class="s2">"build"</span><span class="p">,</span><span class="w">
</span><span class="s2">"isDefault"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">},</span><span class="w">
</span></code></pre></div></div>
<p>После того, как собереться можете попробовать сделать так <code class="highlighter-rouge">./build/HWord</code> на OSX, и вы должны получить примерно такое:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>exec format error: ./build/HWord
</code></pre></div></div>
<p>Это правильно, наш бинарник собран для linux и на OSX запускаться не должен. Если же вы увидите вывод программы, это значит что что-то пошло не так (вы собрали OSX’овым компилятором).</p>
<h2 id="Запускаем-без-отладки">Запускаем без отладки</h2>
<p>Теперь у нас есть бинарник, надо проверить что он запускается. Для этого снова воспользуемся скриптом dmaker.sh и такой задачей:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="s2">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Docker: Run Containers"</span><span class="p">,</span><span class="w">
</span><span class="s2">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${workspaceFolder}/dmaker.sh"</span><span class="p">,</span><span class="w">
</span><span class="s2">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"/opt/build/HWord"</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">},</span><span class="w">
</span></code></pre></div></div>
<p>После этого вы должны увидеть в <code class="highlighter-rouge">docker logs maker</code> и терминале VSCode примерно такое</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hostname: 03a3a4dee13f, login:
</code></pre></div></div>
<p>Это хорошо, наш бинарник на linux работает хорошо.</p>
<h2 id="Отлаживаемся">Отлаживаемся</h2>
<p>С помощью VSCode поставьте breakpoint куда-нибудь в коде. Нам будет необходим запущенный в фоне контейнер, внутри которого мы через <code class="highlighter-rouge">docker exec</code> будем запускать gdb. Увы, завести VSCode так, чтобы он смог нормально в интерактивном режиме (dmaker.sh и -it) запускать debugger у меня не получилось (видимо механизм запуска дебаггера в VSCode как-то так хитро устроен), поэтому я воспользовался pipeTransport и все заработало.</p>
<p>Таким образом задача для предварительного запуска контейнера:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="s2">"label"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Docker: Run Debugging Container"</span><span class="p">,</span><span class="w">
</span><span class="s2">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${workspaceFolder}/ddebugger.sh"</span><span class="p">,</span><span class="w">
</span><span class="s2">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"bash"</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>а launch.json выглядит так</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="s2">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.2.0"</span><span class="p">,</span><span class="w">
</span><span class="s2">"configurations"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="s2">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Container Debug"</span><span class="p">,</span><span class="w">
</span><span class="s2">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cppdbg"</span><span class="p">,</span><span class="w">
</span><span class="s2">"request"</span><span class="p">:</span><span class="w"> </span><span class="s2">"launch"</span><span class="p">,</span><span class="w">
</span><span class="s2">"program"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/opt/build/HWord"</span><span class="p">,</span><span class="w">
</span><span class="s2">"cwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/opt"</span><span class="p">,</span><span class="w">
</span><span class="s2">"linux"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"MIMode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"gdb"</span><span class="p">,</span><span class="w">
</span><span class="s2">"setupCommands"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="s2">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Enable pretty-printing for gdb"</span><span class="p">,</span><span class="w">
</span><span class="s2">"text"</span><span class="p">:</span><span class="w"> </span><span class="s2">"-enable-pretty-printing"</span><span class="p">,</span><span class="w">
</span><span class="s2">"ignoreFailures"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="s2">"osx"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"MIMode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"gdb"</span><span class="p">,</span><span class="w">
</span><span class="s2">"setupCommands"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="s2">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Enable pretty-printing for gdb"</span><span class="p">,</span><span class="w">
</span><span class="s2">"text"</span><span class="p">:</span><span class="w"> </span><span class="s2">"-enable-pretty-printing"</span><span class="p">,</span><span class="w">
</span><span class="s2">"ignoreFailures"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="s2">"preLaunchTask"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Docker: Run Debugging Container"</span><span class="p">,</span><span class="w">
</span><span class="s2">"pipeTransport"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"pipeProgram"</span><span class="p">:</span><span class="w"> </span><span class="s2">"docker"</span><span class="p">,</span><span class="w">
</span><span class="s2">"pipeCwd"</span><span class="p">:</span><span class="w"> </span><span class="s2">"${workspaceRoot}"</span><span class="p">,</span><span class="w">
</span><span class="s2">"pipeArgs"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"exec"</span><span class="p">,</span><span class="w"> </span><span class="s2">"-i"</span><span class="p">,</span><span class="w"> </span><span class="s2">"maker"</span><span class="p">,</span><span class="w"> </span><span class="s2">"sh"</span><span class="p">,</span><span class="w"> </span><span class="s2">"-c"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="s2">"quoteArgs"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
</span><span class="s2">"debuggerPath"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/usr/bin/gdb"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="s2">"sourceFileMap"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="s2">"/opt"</span><span class="p">:</span><span class="s2">"${workspaceFolder}"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Внимательно ко всем параметрам, они все имеют значение.</p>
<p>После этого жмем F5 (или в меню выбираем Start Debugging) и… отлаживаемся. Дебаггер должен остановить выполнение в установленном вами breakpoint’е.</p>
<p>Примеры кода и исходники к статье лежат тут <a href="https://github.com/mrdekk/viscose-cpp-docker">mrdekk/viscose-cpp-docker</a>.</p>
<p>P.S. Да, я тоже ненавижу автокомплит, когда он что-то исправляет. В названии репозитория должно было быть vscode-cpp-docker, а получилось viscose-cpp-docker. Но, ладно! Так тому и быть.</p>
Собираем toolchain для С++ разработки под Linux (Ubuntu) на OSX2018-01-01T00:00:00+00:00http://mrdekk.ru/2018/01/01/cpp-osx-to-linux<h1 id="crosstool-ng">Crosstool-NG</h1>
<p>Для начала нам необходимо crosstool-ng для сборки toolchain’а для компиляции под Linux на базе OSX.</p>
<h2 id="case-sensitive-volume">Case Sensitive Volume</h2>
<p>Для установки crosstool-ng и для сборки правильного toolchain’а необходим Case-Sensitive Volume. Для этого воспользуемся <a href="https://gist.github.com/scottsb/479bebe8b4b86bf17e2d/">замечательным скриптом</a>. Опционально надо поправить в нем ${HOME} на то, куда вы реально хотите сохранять образ - у меня например второй большой диск (HDD против SSD) для всякого такого, поэтому пришлось поправить это дело.</p>
<p>Заодно сделаем его, воспользовавшись скриптом</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./casesafe.sh create
./casesafe.sh mount
</code></pre></div></div>
<p>После того как станет ненужным, не забудте сделать</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./casesafe.sh unmount
</code></pre></div></div>
<h2 id="Установка-требуемых-зависимостей-через-homebrew">Установка требуемых зависимостей через HomeBrew</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew install autoconf
brew install binutils
brew install gawk
brew install gmp
brew install gnu-sed
brew install help2man
brew install mpfr
brew install openssl
brew install pcre
brew install readline
brew install wget
brew install xz
</code></pre></div></div>
<p>Иногда brew может сказать что зависимость уже стоит, но устарела, тогда опционально можно сделать brew update …</p>
<p>Еще опционально просят поставить dupes/grep ввиду того, что libc результирующего toolchain’а будет неправильно сконфигурирована, ввиду отличия макового BSD grep от GNU grep. –with-default-names необходимо чтобы системный grep заменился новым, так как без этого параметры brew’шный grep поставиться как ggrep.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew install <span class="nb">grep</span> <span class="nt">--with-default-names</span>
</code></pre></div></div>
<h2 id="Установка-собственно-crosstool-ng">Установка собственно crosstool-ng</h2>
<p>Я решил пойти по Hacker’s way и сделать все через исходники, а не через release tarballs. Поэтому первое что делаем - клонируем репу, нужен гит:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/crosstool-ng/crosstool-ng
</code></pre></div></div>
<p>Далее нам требуется выбрать конкретный релиз, релизы обозначены тегами, поэтому выгребем теги и зачекаутим нужный:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git fetch <span class="nt">--all</span> <span class="nt">--tags</span> <span class="nt">--prune</span>
git checkout tags/crosstool-ng-1.23.0 <span class="nt">-b</span> r1.23.0
</code></pre></div></div>
<p>Далее начинаем работу по сборке. Так как мы решили пойти Hacker’s Way, то будем запускать это дело из исходников (поэтому ./configure –enable-local).</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./bootstrap
./configure <span class="nt">--enable-local</span>
make
</code></pre></div></div>
<p>Для проверки успешности сборки в текущей директории надо сделать</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./ct-ng <span class="nb">help</span>
</code></pre></div></div>
<p>Должна вывестись инструкция.</p>
<h2 id="Конфигурируем">Конфигурируем</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./ct-ng menuconfig
</code></pre></div></div>
<p>Говорят, тем кто собирал линуксовые ядра все должно быть понятно, я просмотрел все опции и потыкал то, что мне надо.</p>
<p>А вообще можно сделать так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./ct-ng list-samples
</code></pre></div></div>
<p>И выбрать что-то из уже готовых настроек. И затем посмотреть конфигурацию так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./ct-ng show-x86_64-ubuntu16.04-linux-gnu
</code></pre></div></div>
<p>Выбираем preset</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./ct-ng x86_64-ubuntu16.04-linux-gnu
</code></pre></div></div>
<p>И можно оттюнить с помощью menuconfig</p>
<h2 id="Проблемы">Проблемы</h2>
<p>Может случится так, что вы получите такую ошибку</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/Volumes/OSXElCapitan/Users/mrdekk/casesafe/.build/src/gdb-7.12.1/gdb/doublest.c:258:19: error: use of undeclared identifier 'min'; did you mean 'fmin'?
[ERROR] /Volumes/OSXElCapitan/Users/mrdekk/casesafe/.build/src/gdb-7.12.1/gdb/doublest.c:568:19: error: use of undeclared identifier 'min'; did you mean 'fmin'?
[ERROR] /Volumes/OSXElCapitan/Users/mrdekk/casesafe/.build/src/gdb-7.12.1/gdb/doublest.c:912:25: error: use of undeclared identifier 'min'; did you mean 'fmin'?
[ERROR] make[3]: *** [doublest.o] Error 1
[ERROR] make[3]: *** Waiting for unfinished jobs....
[ERROR] make[2]: *** [all-gdb] Error 2
[ERROR] make[1]: *** [all] Error 2
</code></pre></div></div>
<p>Надо попробовать ветку master в crosstool-ng. Однако bash на OSX слишком старый для bootstrap, поэтому придется поставить bash из brew и поправить shell в bootstrap.</p>
<p>После однако начинаются проблемы с sha512sum, такие</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/Volumes/OSXElCapitan/Users/mrdekk/Documents/Utils/crosstool-ng/scripts/functions: line 786: sha512sum: command not found
</code></pre></div></div>
<p>Для этого делаем так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew install coreutils
ln <span class="nt">-s</span> /usr/local/bin/gsha512sum /usr/local/bin/sha512sum
</code></pre></div></div>
<p>Еще там не собирается glibc с binutils-2.29.1, есть <a href="https://git.busybox.net/buildroot/commit/?id=cf821efbd0b24690b52f379d4a9934a16073762e">патч</a>, который позволяет так собираться</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">char</span> <span class="o">*</span><span class="n">loc1</span> <span class="n">__attribute__</span> <span class="p">((</span><span class="n">nocommon</span><span class="p">));</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">loc2</span> <span class="n">__attribute__</span> <span class="p">((</span><span class="n">nocommon</span><span class="p">));</span>
<span class="n">compat_symbol</span> <span class="p">(</span><span class="n">libc</span><span class="p">,</span> <span class="n">loc1</span><span class="p">,</span> <span class="n">loc1</span><span class="p">,</span> <span class="n">GLIBC_2_0</span><span class="p">);</span>
<span class="n">compat_symbol</span> <span class="p">(</span><span class="n">libc</span><span class="p">,</span> <span class="n">loc2</span><span class="p">,</span> <span class="n">loc2</span><span class="p">,</span> <span class="n">GLIBC_2_0</span><span class="p">);</span>
<span class="cm">/* Although we do not support the use we define this variable as well. */</span>
<span class="kt">char</span> <span class="o">*</span><span class="n">locs</span> <span class="n">__attribute__</span> <span class="p">((</span><span class="n">nocommon</span><span class="p">));</span>
<span class="n">compat_symbol</span> <span class="p">(</span><span class="n">libc</span><span class="p">,</span> <span class="n">locs</span><span class="p">,</span> <span class="n">locs</span><span class="p">,</span> <span class="n">GLIBC_2_0</span><span class="p">);</span>
</code></pre></div></div>
<p>Еще появилась такая проблема</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[ERROR] ../sysdeps/ieee754/dbl-64/e_pow.c:469:13: error: '<<' in boolean context, did you mean '<' ? [-Werror=int-in-bool-context]
[ERROR] ../sysdeps/ieee754/dbl-64/e_pow.c:471:17: error: '<<' in boolean context, did you mean '<' ? [-Werror=int-in-bool-context]
[ERROR] ../sysdeps/ieee754/dbl-64/e_pow.c:477:9: error: '<<' in boolean context, did you mean '<' ? [-Werror=int-in-bool-context]
[ERROR] ../sysdeps/ieee754/dbl-64/e_pow.c:479:13: error: '<<' in boolean context, did you mean '<' ? [-Werror=int-in-bool-context]
</code></pre></div></div>
<p>Для ее решения есть такой <a href="https://patchwork.ozlabs.org/patch/680578/">патч</a></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">diff</span> <span class="o">--</span><span class="n">git</span> <span class="n">a</span><span class="o">/</span><span class="n">sysdeps</span><span class="o">/</span><span class="n">ieee754</span><span class="o">/</span><span class="n">dbl</span><span class="o">-</span><span class="mi">64</span><span class="o">/</span><span class="n">e_pow</span><span class="p">.</span><span class="n">c</span> <span class="n">b</span><span class="o">/</span><span class="n">sysdeps</span><span class="o">/</span><span class="n">ieee754</span><span class="o">/</span><span class="n">dbl</span><span class="o">-</span><span class="mi">64</span><span class="o">/</span><span class="n">e_pow</span><span class="p">.</span><span class="n">c</span>
<span class="n">index</span> <span class="mi">663</span><span class="n">fa39</span><span class="p">..</span><span class="n">bd758b5</span> <span class="mi">100644</span>
<span class="o">---</span> <span class="n">a</span><span class="o">/</span><span class="n">sysdeps</span><span class="o">/</span><span class="n">ieee754</span><span class="o">/</span><span class="n">dbl</span><span class="o">-</span><span class="mi">64</span><span class="o">/</span><span class="n">e_pow</span><span class="p">.</span><span class="n">c</span>
<span class="o">+++</span> <span class="n">b</span><span class="o">/</span><span class="n">sysdeps</span><span class="o">/</span><span class="n">ieee754</span><span class="o">/</span><span class="n">dbl</span><span class="o">-</span><span class="mi">64</span><span class="o">/</span><span class="n">e_pow</span><span class="p">.</span><span class="n">c</span>
<span class="err">@@</span> <span class="o">-</span><span class="mi">466</span><span class="p">,</span><span class="mi">15</span> <span class="o">+</span><span class="mi">466</span><span class="p">,</span><span class="mi">15</span> <span class="err">@@</span> <span class="n">checkint</span> <span class="p">(</span><span class="kt">double</span> <span class="n">x</span><span class="p">)</span>
<span class="k">return</span> <span class="p">(</span><span class="n">n</span> <span class="o">&</span> <span class="mi">1</span><span class="p">)</span> <span class="o">?</span> <span class="o">-</span><span class="mi">1</span> <span class="o">:</span> <span class="mi">1</span><span class="p">;</span> <span class="cm">/* odd or even */</span>
<span class="k">if</span> <span class="p">(</span><span class="n">k</span> <span class="o">></span> <span class="mi">20</span><span class="p">)</span>
<span class="p">{</span>
<span class="o">-</span> <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o"><<</span> <span class="p">(</span><span class="n">k</span> <span class="o">-</span> <span class="mi">20</span><span class="p">))</span>
<span class="o">+</span> <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o"><<</span> <span class="p">(</span><span class="n">k</span> <span class="o">-</span> <span class="mi">20</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="cm">/* if not integer */</span>
<span class="o">-</span> <span class="k">return</span> <span class="p">(</span><span class="n">n</span> <span class="o"><<</span> <span class="p">(</span><span class="n">k</span> <span class="o">-</span> <span class="mi">21</span><span class="p">))</span> <span class="o">?</span> <span class="o">-</span><span class="mi">1</span> <span class="o">:</span> <span class="mi">1</span><span class="p">;</span>
<span class="o">+</span> <span class="k">return</span> <span class="p">(</span><span class="n">n</span> <span class="o"><<</span> <span class="p">(</span><span class="n">k</span> <span class="o">-</span> <span class="mi">21</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="o">?</span> <span class="o">-</span><span class="mi">1</span> <span class="o">:</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="cm">/*if not integer */</span>
<span class="k">if</span> <span class="p">(</span><span class="n">k</span> <span class="o">==</span> <span class="mi">20</span><span class="p">)</span>
<span class="k">return</span> <span class="p">(</span><span class="n">m</span> <span class="o">&</span> <span class="mi">1</span><span class="p">)</span> <span class="o">?</span> <span class="o">-</span><span class="mi">1</span> <span class="o">:</span> <span class="mi">1</span><span class="p">;</span>
<span class="o">-</span> <span class="k">if</span> <span class="p">(</span><span class="n">m</span> <span class="o"><<</span> <span class="p">(</span><span class="n">k</span> <span class="o">+</span> <span class="mi">12</span><span class="p">))</span>
<span class="o">+</span> <span class="k">if</span> <span class="p">(</span><span class="n">m</span> <span class="o"><<</span> <span class="p">(</span><span class="n">k</span> <span class="o">+</span> <span class="mi">12</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="o">-</span> <span class="k">return</span> <span class="p">(</span><span class="n">m</span> <span class="o"><<</span> <span class="p">(</span><span class="n">k</span> <span class="o">+</span> <span class="mi">11</span><span class="p">))</span> <span class="o">?</span> <span class="o">-</span><span class="mi">1</span> <span class="o">:</span> <span class="mi">1</span><span class="p">;</span>
<span class="o">+</span> <span class="k">return</span> <span class="p">(</span><span class="n">m</span> <span class="o"><<</span> <span class="p">(</span><span class="n">k</span> <span class="o">+</span> <span class="mi">11</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="o">?</span> <span class="o">-</span><span class="mi">1</span> <span class="o">:</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Далее опять проблемы</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[ALL ] rpc_parse.c: In function 'get_prog_declaration':
[ERROR] rpc_parse.c:543:23: error: '%d' directive writing between 1 and 10 bytes into a region of size 7 [-Werror=format-overflow=]
[ALL ] sprintf (name, "%s%d", ARGNAME, num); /* default name of argument */
[ALL ] ^~
[ALL ] rpc_parse.c:543:20: note: directive argument in the range [1, 2147483647]
[ALL ] sprintf (name, "%s%d", ARGNAME, num); /* default name of argument */
[ALL ] ^~~~~~
[ALL ] rpc_parse.c:543:5: note: 'sprintf' output between 5 and 14 bytes into a destination of size 10
[ALL ] sprintf (name, "%s%d", ARGNAME, num); /* default name of argument */
[ALL ] ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[ALL ] cc1: all warnings being treated as errors
[ERROR] make[3]: *** [/Volumes/OSXElCapitan/Users/mrdekk/casesafe/.build/x86_64-ubuntu16.04-linux-gnu/build/build-libc-final/multilib/sunrpc/rpc_parse.o] Error 1
[ERROR] make[3]: *** Waiting for unfinished jobs....
[ERROR] make[2]: *** [sunrpc/others] Error 2
[ERROR] make[1]: *** [all] Error 2
</code></pre></div></div>
<p>Решение этой проблемы найти было сложнее, но кажется есть <a href="https://lists.crux.nu/pipermail/crux-commits/2017-October/022800.html">вот</a></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">+---</span> <span class="n">a</span><span class="o">/</span><span class="n">sunrpc</span><span class="o">/</span><span class="n">rpc_parse</span><span class="p">.</span><span class="n">c</span>
<span class="o">++++</span> <span class="n">b</span><span class="o">/</span><span class="n">sunrpc</span><span class="o">/</span><span class="n">rpc_parse</span><span class="p">.</span><span class="n">c</span>
<span class="o">+</span><span class="err">@@</span> <span class="o">-</span><span class="mi">521</span><span class="p">,</span><span class="mi">7</span> <span class="o">+</span><span class="mi">521</span><span class="p">,</span><span class="mi">7</span> <span class="err">@@</span> <span class="k">static</span> <span class="kt">void</span>
<span class="o">+</span> <span class="n">get_prog_declaration</span> <span class="p">(</span><span class="n">declaration</span> <span class="o">*</span> <span class="n">dec</span><span class="p">,</span> <span class="n">defkind</span> <span class="n">dkind</span><span class="p">,</span> <span class="kt">int</span> <span class="n">num</span> <span class="cm">/* arg number */</span> <span class="p">)</span>
<span class="o">+</span> <span class="p">{</span>
<span class="o">+</span> <span class="n">token</span> <span class="n">tok</span><span class="p">;</span>
<span class="o">+-</span> <span class="kt">char</span> <span class="n">name</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="cm">/* argument name */</span>
<span class="o">++</span> <span class="kt">char</span> <span class="n">name</span><span class="p">[</span><span class="n">MAXLINESIZE</span><span class="p">];</span> <span class="cm">/* argument name */</span>
<span class="o">+</span>
<span class="o">+</span> <span class="k">if</span> <span class="p">(</span><span class="n">dkind</span> <span class="o">==</span> <span class="n">DEF_PROGRAM</span><span class="p">)</span>
<span class="o">+</span> <span class="p">{</span>
</code></pre></div></div>
<p>Дальше опять проблемы</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nss_nisplus/nisplus-alias.c:300:12: error: argument 1 null where non-null expected [-Werror=nonnull]
[ERROR] nss_nisplus/nisplus-alias.c:303:39: error: '%s' directive argument is null [-Werror=format-truncation=]
[ERROR] make[3]: *** [/Volumes/OSXElCapitan/Users/mrdekk/casesafe/.build/x86_64-ubuntu16.04-linux-gnu/build/build-libc-final/multilib/nis/nisplus-alias.os] Error 1
[ERROR] make[3]: *** Waiting for unfinished jobs....
[ERROR] make[2]: *** [nis/others] Error 2
[ERROR] make[1]: *** [all] Error 2
</code></pre></div></div>
<p>Лечаться <a href="https://patches.linaro.org/patch/100439/">так</a></p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">diff</span> <span class="o">--</span><span class="n">git</span> <span class="n">a</span><span class="o">/</span><span class="n">nis</span><span class="o">/</span><span class="n">nss_nisplus</span><span class="o">/</span><span class="n">nisplus</span><span class="o">-</span><span class="n">alias</span><span class="p">.</span><span class="n">c</span> <span class="n">b</span><span class="o">/</span><span class="n">nis</span><span class="o">/</span><span class="n">nss_nisplus</span><span class="o">/</span><span class="n">nisplus</span><span class="o">-</span><span class="n">alias</span><span class="p">.</span><span class="n">c</span>
<span class="n">index</span> <span class="mi">7</span><span class="n">f698b4e6d</span><span class="p">..</span><span class="mi">509</span><span class="n">ace1f83</span> <span class="mi">100644</span>
<span class="o">---</span> <span class="n">a</span><span class="o">/</span><span class="n">nis</span><span class="o">/</span><span class="n">nss_nisplus</span><span class="o">/</span><span class="n">nisplus</span><span class="o">-</span><span class="n">alias</span><span class="p">.</span><span class="n">c</span>
<span class="o">+++</span> <span class="n">b</span><span class="o">/</span><span class="n">nis</span><span class="o">/</span><span class="n">nss_nisplus</span><span class="o">/</span><span class="n">nisplus</span><span class="o">-</span><span class="n">alias</span><span class="p">.</span><span class="n">c</span>
<span class="err">@@</span> <span class="o">-</span><span class="mi">297</span><span class="p">,</span><span class="mi">10</span> <span class="o">+</span><span class="mi">297</span><span class="p">,</span><span class="mi">10</span> <span class="err">@@</span> <span class="n">_nss_nisplus_getaliasbyname_r</span> <span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span> <span class="k">struct</span> <span class="n">aliasent</span> <span class="o">*</span><span class="n">alias</span><span class="p">,</span>
<span class="k">return</span> <span class="n">NSS_STATUS_UNAVAIL</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">-</span> <span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="n">strlen</span> <span class="p">(</span><span class="n">name</span><span class="p">)</span> <span class="o">+</span> <span class="mi">9</span> <span class="o">+</span> <span class="n">tablename_len</span><span class="p">];</span>
<span class="o">+</span> <span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="n">tablename_len</span> <span class="o">+</span> <span class="mi">9</span><span class="p">];</span>
<span class="kt">int</span> <span class="n">olderr</span> <span class="o">=</span> <span class="n">errno</span><span class="p">;</span>
<span class="o">-</span> <span class="n">snprintf</span> <span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span> <span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="s">"[name=%s],%s"</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">tablename_val</span><span class="p">);</span>
<span class="o">+</span> <span class="n">snprintf</span> <span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span> <span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="s">"[name=],%s"</span><span class="p">,</span> <span class="n">tablename_val</span><span class="p">);</span>
<span class="n">nis_result</span> <span class="o">*</span><span class="n">result</span> <span class="o">=</span> <span class="n">nis_list</span> <span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">FOLLOW_PATH</span> <span class="o">|</span> <span class="n">FOLLOW_LINKS</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
</code></pre></div></div>
<p>После этого toolchain собрался. После этого я попробовал собрать простой Hello, World с ним и запустить. На macOS не запустилось (zsh: exec format error: ./CrossWorld) - это хорошо. Собираем простейший контейнер на ubuntu 16.04 и запускаем - работает. Отлично!</p>
Об Actor Model от автора2017-12-29T00:00:00+00:00http://mrdekk.ru/2017/12/29/about-actor-model-from-author<p>Хорошее видео про Actor Model от автора концепции. Правда, на английском, но если владеете и вам интересна модель акторов, то очень рекомендую посмотреть.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/7erJ1DV_Tlo?rel=0" frameborder="0" gesture="media" allow="encrypted-media" allowfullscreen=""></iframe>
MySQL в Docker через supervisord2017-12-04T00:00:00+00:00http://mrdekk.ru/2017/12/04/mysql-in-docker<p>Крутится тут у меня небольшой контейнер с LAMP стэком (Apache, PHP, MySQL внутри одного контейнера вместе с supervisor). И в последнее время периодически стал вылетать MySQL (стал наедаться памяти). Помогала только ручная перезагрузка контейнера. Добавить памяти был не вариант (сама машинка ограниченная по ресурсам). Пробовал зашедулить в крон перезагрузку контейнера, но работало это плохо. Поэтому решил сделать что-нибудь чтоб жило само и без перезагрузок. Получилось так:</p>
<p>Проблема, как я уже сказал, ввиду ограниченных ресурсов виртуальной машинки - mysql хочет выделить блок памяти, но не может и поэтому падает совсем. Так как на диске место есть поколдуем так. Создадим специальный swap файлик:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dd <span class="k">if</span><span class="o">=</span>/dev/zero <span class="nv">of</span><span class="o">=</span>/opt/swap.dat <span class="nv">bs</span><span class="o">=</span>1024 <span class="nv">count</span><span class="o">=</span>512k
mkswap /opt/swap.dat
swapon /opt/swap.dat
vim /etc/fstab
</code></pre></div></div>
<p>Файлик /etc/fstab отредактируем так, надо добавить в него такую строчку</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/opt/swap.dat none swap sw 0 0
</code></pre></div></div>
<p>Далее подредактируем конфиг mysql</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vim /etc/mysql/my.cnf
</code></pre></div></div>
<p>Уменьшим размер буфера</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>innodb_buffer_pool_size=64M
</code></pre></div></div>
<p>И далее пересоберем контейнер и перезапустим. 2 недели - полет нормальный. И да - эта инструкция как заставить работать на слабых конфигурациях. Может работать неоптимально с точки зрения производительности.</p>
Шрифт Consolas в Ubuntu2017-11-15T00:00:00+00:00http://mrdekk.ru/2017/11/15/install-consolas-ubuntu<p>Найдено <a href="https://slicks.name/ubuntu/ubuntu-consolas-font-install.html">тут</a>, здесь в кратком изложении.</p>
<p>Задача - установить шрифт consolas (моноширный шрифт для разработки, очень приятный на вид) в Ubuntu, для целей разработки. Он по-умолчанию есть в Windows, но в Ubuntu по понятным причинам его нет.</p>
<p>Делаем так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt-get install font-manager
<span class="nb">sudo </span>apt-get install cabextract
</code></pre></div></div>
<p>Далее создаем скрипт, который скачает и распакует шрифты</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano consolas.sh
</code></pre></div></div>
<p>Содержимое</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="nb">set</span> <span class="nt">-e</span>
<span class="nb">set</span> <span class="nt">-x</span>
mkdir temp
<span class="nb">cd </span>temp
wget http://download.microsoft.com/download/E/6/7/E675FFFC-2A6D-4AB0-B3EB-27C9F8C8F696/PowerPointViewer.exe
cabextract <span class="nt">-L</span> <span class="nt">-F</span> ppviewer.cab PowerPointViewer.exe
cabextract ppviewer.cab
</code></pre></div></div>
<p>Выполняем</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chmod +x consolas.sh
./consolas.sh
</code></pre></div></div>
<p>Устанавливаем шрифт Consolas</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>temp
font-manager
</code></pre></div></div>
<p>Шрифт Consolas содержится в таких файлах</p>
<ul>
<li><strong>CONSOLAB.TTF</strong> - Жирный (bold)</li>
<li><strong>CONSOLAI.TTF</strong> - Курсив (italic)</li>
<li><strong>CONSOLA.TTF</strong> - Обычный (regular)</li>
<li><strong>CONSOLAZ.TTF</strong> - Жирный курсив (bold italic)</li>
</ul>
Полный Debug log сборки в Xcode2017-10-13T00:00:00+00:00http://mrdekk.ru/2017/10/13/full-debug-log-xcode<p>Иногда прям очень надо посмотреть что было не так во время сборки проекта Xcode’ом, оказывается есть полный debug log в DerivedData, достать можно так:</p>
<p>путь: <code class="highlighter-rouge">~/Library/Developer/Xcode/DerivedData/<YOURAPP>/Logs/Debug/</code></p>
<p>Вообще файлы .xcactivitylog - это просто gzip архивы, можно распаковать их так:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> ~/Library/Developer/Xcode/DerivedData/<YOURAPP>/Logs/Debug/
<span class="nv">EXT</span><span class="o">=</span><span class="s2">".xcactivitylog"</span>
<span class="k">for </span>LOG <span class="k">in</span> <span class="k">*</span>.xcactivitylog<span class="p">;</span> <span class="k">do
</span><span class="nv">NAME</span><span class="o">=</span><span class="sb">`</span>basename <span class="nv">$LOG</span> <span class="nv">$EXT</span><span class="sb">`</span>
gunzip <span class="nt">-c</span> <span class="nt">-S</span> <span class="nv">$EXT</span> <span class="s2">"</span><span class="k">${</span><span class="nv">NAME</span><span class="k">}${</span><span class="nv">EXT</span><span class="k">}</span><span class="s2">"</span> <span class="o">></span> <span class="s2">"</span><span class="k">${</span><span class="nv">NAME</span><span class="k">}</span><span class="s2">.log"</span>
<span class="k">done</span>
</code></pre></div></div>
<p>Кроме собственно Debug логов там есть и Build логи</p>
Разбираемся со Spotlight2017-10-04T00:00:00+00:00http://mrdekk.ru/2017/10/04/deal-with-spotlight<p>Начал тут у меня тормозить хакинтош. Пошел разбираться в чем проблема, оказалось что львиную долю ресурсов жрет Spotlight (процессы mds, mds_store, mdworker). Так как Spotlight’ом пользуюсь в основном для того, чтобы открыть Xcode (что можно сделать и другими способами), решено было как-то умерить его аппетиты.</p>
<p>Первое что можно попробовать - это просто выключить у него все индексирование, так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>mdutil <span class="nt">-a</span> <span class="nt">-i</span> off
</code></pre></div></div>
<p>Эта команда отключить индексирование на всех разделах, которые у вас есть. Можно отключать частями так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>mdutil <span class="nt">-i</span> off /Volume/Your_Volume_Name_Here
</code></pre></div></div>
<p>Кроме этого можно удалить имеющийся индекс так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>mdutil <span class="nt">-E</span> /Volume/Your_Volume_Name_Here
</code></pre></div></div>
<p>И снести данные Spotlight</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> /
<span class="nb">sudo </span>rm <span class="nt">-fr</span> .Spotlight-V100
</code></pre></div></div>
SimCtl handbook2017-09-21T00:00:00+00:00http://mrdekk.ru/2017/09/21/simctl-handbook<p>Небольшой сборничек команд управления симулятором iOS.</p>
<p><strong>Важно:</strong> еще пока не проверял на Xcode 9, может что-нибудь поменяться. Поэтому не лишним будет проверить, что используется Xcode 8.3.3:</p>
<p>Смотрим:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcode-select -p</span>
</code></pre></div></div>
<p>Если вы пользуетесь Xcode’ом из AppStore, то у вас наверняка будет только Xcode и вы увидите что-то вроде:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/Applications/Xcode.app/Contents/Developer
</code></pre></div></div>
<p>Тогда надо зайти в этот Xcode и посмотреть какой он версии. Если же вы нумеруете Xcode по версиям, то должны увидеть что-то вроде:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/Applications/Xcode8.3.3.app/Contents/Developer
</code></pre></div></div>
<p>Если там Xcode9 то надо выбрать Xcode8.3.3 примерно так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># sudo xcode-select -s /Applications/Xcode8.3.3.app/Contents/Developer</span>
</code></pre></div></div>
<h3 id="Просмотр-списка-симулятором">Просмотр списка симулятором</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcrun simctl list</span>
</code></pre></div></div>
<p>Выведет большой список, в котором будет примерно такое:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iPhone 5 (D1F67F00-FA3D-42B7-9E2F-FEF23809D4A0) (Booted)
</code></pre></div></div>
<p>Который в данный момент запущен. Вместо “booted” в командах можно использовать UUID.</p>
<h3 id="Снимаем-видео">Снимаем видео</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcrun simctl io booted recordVideo <filename>.<extension></span>
</code></pre></div></div>
<p>Пока скрипт запущен, будет записываться видео всего, что попадаем во framebuffer. Чтобы закончить запись, просто нажмите Ctrl+C. Формат может быть одним из - .mov, .h264, .mp4, .fmp4.</p>
<h3 id="Скриншотим">Скриншотим</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcrun simctl io booted screenshot <filename>.<extension></span>
</code></pre></div></div>
<p>Форматы: .png, .tiff, .bmp, .gif, .jpg</p>
<h3 id="Открыть-url">Открыть url</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcrun simctl openurl booted https://yandex.ru</span>
</code></pre></div></div>
<p>Можно например отправить sms’ку, если вы знаете app url схему:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcrun simctl openurl booted sms: #Open Messages</span>
</code></pre></div></div>
<h3 id="Загружаем-картинку-в-фотогалерею">Загружаем картинку в фотогалерею</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcrun simctl addmedia booted <path to file(s)></span>
</code></pre></div></div>
<h3 id="Выводим-переменные-окружения">Выводим переменные окружения</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcrun simctl getenv booted <variable name></span>
</code></pre></div></div>
<h3 id="Триггерим-синхронизацию-с-icloud">Триггерим синхронизацию с iCloud</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcrun simctl icloud_sync booted</span>
</code></pre></div></div>
<h3 id="Стираем-устройство">Стираем устройство</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcrun simctl erase booted</span>
</code></pre></div></div>
<h3 id="Устанавливаем-приложение">Устанавливаем приложение</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcrun simctl install booted <The path to the app></span>
</code></pre></div></div>
<h3 id="Запускаем-приложение-по-bundle-id">Запускаем приложение (по bundle-id)</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcrun simctl launch booted <bundle-id> <arguments></span>
</code></pre></div></div>
<h3 id="Получить-путь-до-контейнера-приложения-по-bundle-id">Получить путь до контейнера приложения (по bundle-id)</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcrun simctl get_app_container booted <bundle-id></span>
</code></pre></div></div>
<p>Или даже сразу открыть в Finder</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># open `xcrun simctl get_app_container booted <bundle-id>` -a Finder</span>
</code></pre></div></div>
<h3 id="Получить-информацию-о-приложении-по-bundle-id">Получить информацию о приложении (по bundle-id)</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># xcrun simctl appinfo booted <bundle-id></span>
</code></pre></div></div>
Запуск ракеты - спокойно, доверься моему опыту!2017-07-28T00:00:00+00:00http://mrdekk.ru/2017/07/28/rocket-launch<p>Жизненно про разработку и запуск проектов:</p>
<p><img src="/media/images/rocketlaunch.jpg" alt="rocket launch" /></p>
Swift Pattern Matching2017-04-24T00:00:00+00:00http://mrdekk.ru/2017/04/24/swift-pattern-matching<p><em>Статья перевод, оригинал <a href="https://appventure.me/2015/08/20/swift-pattern-matching-in-detail/">тут</a>. Далее повествование идет от лица автора, я лишь в меру своих сил попытался литературно перевести на русский. Также автор судя по всему использует Swift 2, по мере возможности я адаптировал примеры для Swift 3.</em></p>
<p>Среди всех новых конструкций языка Swift по отношению к Objective-C выделяется конструкция switch. Которая, в отличии от языка Objective-C не просто альтернатива набору последовательных операций if-elseif-else, но предоставляет расширенный набор вариантов выбора.</p>
<p>Конструкция switch в swift позволяет делать гораздо больше. И в этой статье я попытаюсь рассказать про эти новые возможности более подробно. Я буду игнорировать те аспекты, которые не несут ничего нового по отношению к Objective-C. Базовые идеи для этой статьи появились в Июле 2014, но большинство вариантов приводило к падению компилятора, поэтому я откладывал написание этой статьи.</p>
<h2 id="Погружаемся">Погружаемся</h2>
<p>Основная функция оператора switch конечно pattern matching (<em>устоявшийся термин, переводить не стал, прим. переводчика</em>), способность классифицировать значения и сопоставлять их вариантам выбора.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Пример наихудшего конвертера binary -> decimal в истории</span>
<span class="k">let</span> <span class="nv">bool1</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">let</span> <span class="nv">bool2</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">switch</span> <span class="p">(</span><span class="n">bool1</span><span class="p">,</span> <span class="n">bool2</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">):</span> <span class="nf">print</span><span class="p">(</span><span class="s">"0"</span><span class="p">)</span>
<span class="k">case</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">):</span> <span class="nf">print</span><span class="p">(</span><span class="s">"1"</span><span class="p">)</span>
<span class="k">case</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">):</span> <span class="nf">print</span><span class="p">(</span><span class="s">"2"</span><span class="p">)</span>
<span class="k">case</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">):</span> <span class="nf">print</span><span class="p">(</span><span class="s">"3"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Pattern matching (PM) давно существует в таких языках программирования, как Haskell, Erlang, Scala или Prolog. Это замечательно, так как позволяет нам увидеть, как PM используется в этих языках для решения практических проблем, и перенять хорошие для решения собственных задач.</p>
<h2 id="Торговый-движок">Торговый движок</h2>
<p>К вам обратились дельцы с Wall Street (ну или хотя бы с ММВБ), им нужна торговая платформа на iOS устройствах. Так как это торгвая платформа, вы определяете перечисление для сделок.</p>
<h3 id="Первый-черновик">Первый черновик</h3>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">Trades</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">buy</span><span class="p">(</span><span class="nv">stock</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">amount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">stockPrice</span><span class="p">:</span> <span class="kt">Float</span><span class="p">)</span>
<span class="k">case</span> <span class="nf">sell</span><span class="p">(</span><span class="nv">stock</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">amount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">stockPrice</span><span class="p">:</span> <span class="kt">Float</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Кроме того, нам доступно следующее API для совершения сделок. <strong>Обратите внимание, что приказы на продажу такие же приказы, только с отрицательным объемом.</strong> Кроме того, сказано, что рыночные цены не так важны, движок возмет внутренню цену в любом случае.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
- parameter stock: The stock name
- parameter amount: The amount, negative number = sell, positive = buy
*/</span>
<span class="kd">func</span> <span class="nf">process</span><span class="p">(</span><span class="nv">stock</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="n">_</span> <span class="nv">amount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">print</span> <span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">amount</span><span class="se">)</span><span class="s"> of </span><span class="se">\(</span><span class="n">stock</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Следующий шаг - обработать приказы. И тут виден потенциал для применения pattern matching, поэтому напишем так:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">trade</span> <span class="o">=</span> <span class="kt">Trades</span><span class="o">.</span><span class="nf">buy</span><span class="p">(</span><span class="nv">stock</span><span class="p">:</span> <span class="s">"APPL"</span><span class="p">,</span> <span class="nv">amount</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span> <span class="nv">stockPrice</span><span class="p">:</span> <span class="mf">115.5</span><span class="p">)</span>
<span class="k">switch</span> <span class="n">trade</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">buy</span><span class="p">(</span><span class="k">let</span> <span class="nv">stock</span><span class="p">,</span> <span class="k">let</span> <span class="nv">amount</span><span class="p">,</span> <span class="n">_</span><span class="p">):</span>
<span class="nf">process</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">)</span>
<span class="k">case</span> <span class="o">.</span><span class="nf">sell</span><span class="p">(</span><span class="k">let</span> <span class="nv">stock</span><span class="p">,</span> <span class="k">let</span> <span class="nv">amount</span><span class="p">,</span> <span class="n">_</span><span class="p">):</span>
<span class="nf">process</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span> <span class="o">*</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// Prints "buy 200 of APPL"</span>
</code></pre></div></div>
<p>Swift позволяет нам извлекать информацию из элементов перечисления в том виде, как нам это необходимо. Поэтому в данном примере, мы используем только идентификатор акции и количество.</p>
<p>Отлично, оправляемся на Wall Street для того, чтобы продемонстрировать нашу замечательную торговую платформу. Однако как обычно, на практике все не так, как в теории. Сделка не так проста, как вам рассказывают.</p>
<ul>
<li>Необходимо рассчитать комиссию, которая отличается в зависимости от типа трейдера.</li>
<li>Чем меньше игрок, тем больше комиссия</li>
<li>Большие компании имеют больший приоритет</li>
</ul>
<p>Поэтому было решено выдать новый API для решения этих проблем:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">processSlow</span><span class="p">(</span><span class="n">_</span> <span class="nv">stock</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="n">_</span> <span class="nv">amount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="n">_</span> <span class="nv">fee</span><span class="p">:</span> <span class="kt">Float</span><span class="p">)</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="s">"slow"</span><span class="p">)</span> <span class="p">}</span>
<span class="kd">func</span> <span class="nf">processFast</span><span class="p">(</span><span class="n">_</span> <span class="nv">stock</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="n">_</span> <span class="nv">amount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="n">_</span> <span class="nv">fee</span><span class="p">:</span> <span class="kt">Float</span><span class="p">)</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="s">"fast"</span><span class="p">)</span> <span class="p">}</span>
</code></pre></div></div>
<h3 id="Типы-трейдоров">Типы трейдоров</h3>
<p>Поэтому возвращаемся и добавляем еще одно перечисление. Тип трейдера будет частью всех сделок.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">TraderType</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">person</span>
<span class="k">case</span> <span class="n">company</span>
<span class="p">}</span>
<span class="kd">enum</span> <span class="kt">Trades</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">buy</span><span class="p">(</span><span class="nv">stock</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">amount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">stockPrice</span><span class="p">:</span> <span class="kt">Float</span><span class="p">,</span> <span class="nv">type</span><span class="p">:</span> <span class="kt">TraderType</span><span class="p">)</span>
<span class="k">case</span> <span class="nf">sell</span><span class="p">(</span><span class="nv">stock</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">amount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">stockPrice</span><span class="p">:</span> <span class="kt">Float</span><span class="p">,</span> <span class="nv">type</span><span class="p">:</span> <span class="kt">TraderType</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Чтож, как лучше всего имплементировать это новое ограничение? Можно просто добавить каскадную конструкцию if-else для .buy и для .sell, но это приведет к каскадному коду, и неминуемо потеряет выразительность, и кто знает, что акулы с Wall Street придумают для нас завтра. Поэтому реализуем эти новые ограничения с применением pattern matching:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">trade</span> <span class="o">=</span> <span class="kt">Trades</span><span class="o">.</span><span class="nf">sell</span><span class="p">(</span><span class="nv">stock</span><span class="p">:</span> <span class="s">"GOOG"</span><span class="p">,</span> <span class="nv">amount</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span> <span class="nv">stockPrice</span><span class="p">:</span> <span class="mf">666.0</span><span class="p">,</span> <span class="nv">type</span><span class="p">:</span> <span class="kt">TraderType</span><span class="o">.</span><span class="n">company</span><span class="p">)</span>
<span class="k">switch</span> <span class="n">trade</span> <span class="p">{</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">buy</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="kt">TraderType</span><span class="o">.</span><span class="n">person</span><span class="p">):</span>
<span class="nf">processSlow</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="mf">5.0</span><span class="p">)</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">sell</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="kt">TraderType</span><span class="o">.</span><span class="n">person</span><span class="p">):</span>
<span class="nf">processSlow</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="o">*</span> <span class="n">amount</span><span class="p">,</span> <span class="mf">5.0</span><span class="p">)</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">buy</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="kt">TraderType</span><span class="o">.</span><span class="n">company</span><span class="p">):</span>
<span class="nf">processFast</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">sell</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="kt">TraderType</span><span class="o">.</span><span class="n">company</span><span class="p">):</span>
<span class="nf">processFast</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="o">*</span> <span class="n">amount</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Достаточно выразительно, не так ли? Кроме того, для краткости мы изменили .buy(let stock, let amount) на let .buy(stock, amount). Суть та же, букв меньше.</p>
<h3 id="Охрана-Охрана">Охрана! Охрана!</h3>
<p><em>Тут видимо имелось ввиду игра слов, синтаксическая конструкция guard также означает и охрану, прим. переводчика</em></p>
<p>Вы снова презентуете ваше творение дельцам с Wall Street, и снова появляется новая хотелка (вам все-таки стоило изначально попросить более детальное ТЗ).</p>
<ul>
<li>Приказы на продажу общим объемом более $ 1 000 000 всегда обрабатываются быстрым процессингом, даже если этот приказ поступил от частного лица</li>
<li>Приказы на покупку объемом менее $ 1 000 всегда обрабатываются медленным процессингом.</li>
</ul>
<p>Это стало бы адом (и лапшой наверное), если бы мы попытались реализовать это через традиционное дерево if’ов. Но у нас есть Swift и конструкция switch. Swift предоставляет дополнительные конструкции для ограничения диапазонов действия переменных.</p>
<p>Поэтому наш код нужно поправить всего чуть-чуть для отражения новых требований.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">trade</span> <span class="o">=</span> <span class="kt">Trades</span><span class="o">.</span><span class="nf">buy</span><span class="p">(</span><span class="nv">stock</span><span class="p">:</span> <span class="s">"GOOG"</span><span class="p">,</span> <span class="nv">amount</span><span class="p">:</span> <span class="mi">1000</span><span class="p">,</span> <span class="nv">stockPrice</span><span class="p">:</span> <span class="mf">666.0</span><span class="p">,</span> <span class="nv">type</span><span class="p">:</span> <span class="kt">TraderType</span><span class="o">.</span><span class="n">person</span><span class="p">)</span>
<span class="k">switch</span> <span class="n">trade</span> <span class="p">{</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">buy</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="kt">TraderType</span><span class="o">.</span><span class="n">person</span><span class="p">):</span>
<span class="nf">processSlow</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="mf">5.0</span><span class="p">)</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">sell</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="n">price</span><span class="p">,</span> <span class="kt">TraderType</span><span class="o">.</span><span class="n">person</span><span class="p">)</span>
<span class="k">where</span> <span class="n">price</span> <span class="o">*</span> <span class="kt">Float</span><span class="p">(</span><span class="n">amount</span><span class="p">)</span> <span class="o">></span> <span class="mi">1000000</span><span class="p">:</span>
<span class="nf">processFast</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="o">*</span> <span class="n">amount</span><span class="p">,</span> <span class="mf">5.0</span><span class="p">)</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">sell</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="kt">TraderType</span><span class="o">.</span><span class="n">person</span><span class="p">):</span>
<span class="nf">processSlow</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="o">*</span> <span class="n">amount</span><span class="p">,</span> <span class="mf">5.0</span><span class="p">)</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">buy</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="n">price</span><span class="p">,</span> <span class="kt">TraderType</span><span class="o">.</span><span class="n">company</span><span class="p">)</span>
<span class="k">where</span> <span class="n">price</span> <span class="o">*</span> <span class="kt">Float</span><span class="p">(</span><span class="n">amount</span><span class="p">)</span> <span class="o"><</span> <span class="mi">1000</span><span class="p">:</span>
<span class="nf">processSlow</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">buy</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="kt">TraderType</span><span class="o">.</span><span class="n">company</span><span class="p">):</span>
<span class="nf">processFast</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">sell</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="n">amount</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="kt">TraderType</span><span class="o">.</span><span class="n">company</span><span class="p">):</span>
<span class="nf">processFast</span><span class="p">(</span><span class="n">stock</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="o">*</span> <span class="n">amount</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Этот код по прежнему остается как достаточно простым для понимания, так и открытым к новым изменениям.</p>
<p>Ура-ура, мы успешно реализовали нашу торговую платформу. Однако, полученное решение все-таки содержит небольшое количество повторяющегося кода. Но может быть есть еще какие-то магические заклинания, которые помогут нам его уменьшить еще? Давайте смотреть дальше.</p>
<h2 id="Продвинутый-pattern-matching">Продвинутый Pattern Matching</h2>
<p>Мы уже попробовали несколько шаблонов в действии. Существуют ли другие варианты? Swift различает 7 типов шаблонов. Давайте посмотрим на все.</p>
<p>Все рассмотренные далее паттерны могут быть использованы не только в конструкции switch, но и с if, guard и даже циклом for.</p>
<h3 id="1-Шаблонный-паттерн">1. Шаблонный паттерн</h3>
<p>Шаблонный паттерн игнорирует значение, к которому сопоставляется. В этом случае допустимо любое значение. Вы его уже встречали, это тоже самое что и, например, <code class="highlighter-rouge">let _ = fnt()</code>, где символ “_” означает, что вам безразлично что будет возвращено из функции. Самое интересное, что будет сопоставлено не только значение, но и его отстутствие (nil). Можно даже сопоставлять Optional, достаточно лишь добавить “?”.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">p</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="k">switch</span> <span class="n">p</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">_</span><span class="p">?:</span> <span class="nf">print</span> <span class="p">(</span><span class="s">"Has String"</span><span class="p">)</span>
<span class="k">case</span> <span class="nv">nil</span><span class="p">:</span> <span class="nf">print</span> <span class="p">(</span><span class="s">"No String"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>И как мы уже успели заметить, когда реализовывали торговую платформу, этот паттерн позволяет нам опускать данные, которые в данном сопоставлении не используются:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">switch</span> <span class="p">(</span><span class="mi">15</span><span class="p">,</span> <span class="s">"example"</span><span class="p">,</span> <span class="mf">3.14</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="k">let</span> <span class="nv">pi</span><span class="p">):</span> <span class="nf">print</span> <span class="p">(</span><span class="s">"pi: </span><span class="se">\(</span><span class="n">pi</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="2-Паттер-по-идентификатору">2. Паттер по идентификатору</h3>
<p>Сопоставляет конкретное значение. Это стандартный механизм того, как работает switch в реализации Objective-C:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">switch</span> <span class="mi">5</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">5</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"5"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="3-Паттерн-с-присвоением-значения">3. Паттерн с присвоением значения</h3>
<p>Аналогичен с присвоением значения переменным через let или var, но только внутри конструкции switch. Мы уже чуть выше встречались с этим видом:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">switch</span> <span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">let</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span> <span class="nf">print</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">x</span><span class="se">)</span><span class="s"> </span><span class="se">\(</span><span class="n">y</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="4-Паттерн-кортеж">4. Паттерн кортеж</h3>
<p>Можно написать отдельную статью про кортежи, здесь ограничимся лишь простым примером:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">age</span> <span class="o">=</span> <span class="mi">23</span>
<span class="k">let</span> <span class="nv">job</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="o">=</span> <span class="s">"Operator"</span>
<span class="k">let</span> <span class="nv">payload</span><span class="p">:</span> <span class="kt">AnyObject</span> <span class="o">=</span> <span class="kt">NSDictionary</span><span class="p">()</span>
<span class="k">switch</span> <span class="p">(</span><span class="n">age</span><span class="p">,</span> <span class="n">job</span><span class="p">,</span> <span class="n">payload</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="p">(</span><span class="k">let</span> <span class="nv">age</span><span class="p">,</span> <span class="n">_</span><span class="p">?,</span> <span class="n">_</span> <span class="k">as</span> <span class="kt">NSDictionary</span><span class="p">):</span>
<span class="nf">print</span><span class="p">(</span><span class="n">age</span><span class="p">)</span>
<span class="k">default</span><span class="p">:</span> <span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>В этом примере мы объединяем три значения в кортеж (например, мы их получили из нескольких разных запросов к API) и сопоставляем их. Важно, что здесь мы выполнили три задачи:</p>
<ol>
<li>Извлекли возраст (age)</li>
<li>Убедились, что есть работа (job), даже несмотря на то, что она нам в общем не нужна</li>
<li>Убедились что привязанные данные (payload) являются наследником класса NSDictionary, даже не смотря на то, что они нам в общем также не нужны.</li>
</ol>
<h3 id="5-Паттерн-выбора-из-перечисления">5. Паттерн выбора из перечисления</h3>
<p>Как уже было замечено выше в примере торговой платформы, pattern matching замечательно работает в паре с перечислениями Swift. Это потому, что перечисления в Swift это закрытые, иммутабельные объекты пригодные для разбора. Также как и с кортежом, вы можете извлекать нужные значения в каждом из вариантов выбора, и только те значения, которые вам необходимы.</p>
<p>Предположим, что мы разрабатываем игру, и у нас имеется несколько видов сущностей. Можно использовать структуры, но так как состояния у нас не много, можно обойтись перечислениями.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">Entities</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">soldier</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
<span class="k">case</span> <span class="nf">tank</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
<span class="k">case</span> <span class="nf">player</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Теперь нам необходимо реализовать цикл отрисовки. Тут нам необходимы только координаты (X и Y).</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">e</span> <span class="k">in</span> <span class="nf">entities</span><span class="p">()</span> <span class="p">{</span>
<span class="k">switch</span> <span class="n">e</span> <span class="p">{</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">soldier</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="nf">drawImage</span><span class="p">(</span><span class="s">"soldier.png"</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">tank</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="nf">drawImage</span><span class="p">(</span><span class="s">"tank.png"</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="nf">player</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="nf">drawImage</span><span class="p">(</span><span class="s">"player.png"</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="6-Паттерн-преобразования-типов">6. Паттерн преобразования типов</h3>
<p>Название говорит само за себя, этот паттерн умеет преобразовывать или просто сопоставлять типы. Имеется два типа выражений:</p>
<ul>
<li><code class="highlighter-rouge">is</code> <strong>type</strong>: Сопоставляет тип (или его потомка) с правой частью выражения. Преобразование типа проводиться, но результат отбрасывается. Поэтому выражение выбора не будет знать про получившейся тип.</li>
<li>выражение <code class="highlighter-rouge">as</code> <strong>type</strong>: Производит тоже сопоставление что и is, но в случае успешного приведения возвращает полученно значение конкретного типа.</li>
</ul>
<p>Далее пример обоих выражений</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">a</span><span class="p">:</span> <span class="kt">Any</span> <span class="o">=</span> <span class="mi">5</span>
<span class="k">switch</span> <span class="n">a</span> <span class="p">{</span>
<span class="c1">// this fails because a is still anyobject</span>
<span class="c1">// error: binary operator '+' cannot be applied to operands of type 'Any' and 'Int'</span>
<span class="k">case</span> <span class="k">is</span> <span class="kt">Int</span><span class="p">:</span> <span class="nf">print</span> <span class="p">(</span><span class="n">a</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
<span class="c1">// This works and returns '6'</span>
<span class="k">case</span> <span class="k">let</span> <span class="nv">n</span> <span class="k">as</span> <span class="kt">Int</span><span class="p">:</span> <span class="nf">print</span> <span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">default</span><span class="p">:</span> <span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Важно, что перед is нет выражения, сопоставляется переменная выбора (а) напрямую.</p>
<h3 id="7-Паттерн---выражение">7. Паттерн - выражение</h3>
<p>Паттер-выражение очень мощный. Он сопоставляет значение в конструкции switch с выражением, реализующим оператор ~=. Для этого оператора существуют реализации по-умолчанию, например, для интервалов, можно сделать так:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">switch</span> <span class="mi">5</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">0</span><span class="o">..<</span><span class="mi">10</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"In range 0-10"</span><span class="p">)</span>
<span class="k">default</span><span class="p">:</span> <span class="k">break</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Интереснее, однако, ситуация, когда этот оператор перегружен для ваших объектов. Предположим, мы решили таки переписать нашу игру про солдатов на структуры.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Soldier</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">hp</span><span class="p">:</span> <span class="kt">Int</span>
<span class="k">let</span> <span class="nv">x</span><span class="p">:</span> <span class="kt">Int</span>
<span class="k">let</span> <span class="nv">y</span><span class="p">:</span> <span class="kt">Int</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Теперь мы хотим сопоставлять все объекты с жизнью в 0. Можно просто переопределить оператор ~= так:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="o">~=</span> <span class="p">(</span><span class="nv">pattern</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Soldier</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Bool</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">pattern</span> <span class="o">==</span> <span class="n">value</span><span class="o">.</span><span class="n">hp</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Теперь можно сопоставлять:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">soldier</span> <span class="o">=</span> <span class="kt">Soldier</span><span class="p">(</span><span class="nv">hp</span><span class="p">:</span> <span class="mi">99</span><span class="p">,</span> <span class="nv">x</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span>
<span class="k">switch</span> <span class="n">soldier</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">0</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"dead soldier"</span><span class="p">)</span>
<span class="k">default</span><span class="p">:</span> <span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Можно сопоставлять кортежи, так:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="o">~=</span> <span class="p">(</span><span class="nv">pattern</span><span class="p">:</span> <span class="p">(</span><span class="nv">hp</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">x</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="kt">Int</span><span class="p">),</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Soldier</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Bool</span> <span class="p">{</span>
<span class="nf">let</span> <span class="p">(</span><span class="n">hp</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="o">=</span> <span class="n">pattern</span>
<span class="k">return</span> <span class="n">hp</span> <span class="o">==</span> <span class="n">value</span><span class="o">.</span><span class="n">hp</span> <span class="o">&&</span> <span class="n">x</span> <span class="o">==</span> <span class="n">value</span><span class="o">.</span><span class="n">x</span> <span class="o">&&</span> <span class="n">y</span> <span class="o">==</span> <span class="n">value</span><span class="o">.</span><span class="n">y</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Оператор ~= может работать с протоколами:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">Entity</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span><span class="k">get</span><span class="p">}</span>
<span class="p">}</span>
<span class="kd">struct</span> <span class="kt">Tank</span><span class="p">:</span> <span class="kt">Entity</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Int</span>
<span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span> <span class="p">}</span>
<span class="p">}</span>
<span class="kd">struct</span> <span class="kt">Peasant</span><span class="p">:</span> <span class="kt">Entity</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Int</span>
<span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span> <span class="p">}</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="o">~=</span><span class="p">(</span><span class="nv">pattern</span><span class="p">:</span> <span class="kt">Entity</span><span class="p">,</span> <span class="nv">x</span><span class="p">:</span> <span class="kt">Entity</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Bool</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">pattern</span><span class="o">.</span><span class="n">value</span> <span class="o">==</span> <span class="n">x</span><span class="o">.</span><span class="n">value</span>
<span class="p">}</span>
<span class="k">switch</span> <span class="kt">Tank</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="kt">Peasant</span><span class="p">(</span><span class="mi">42</span><span class="p">):</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Matched"</span><span class="p">)</span> <span class="c1">// Does match</span>
<span class="k">default</span><span class="p">:</span> <span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Есть еще много интересных вещей, которые можно сделать через паттерн-выражение. Для более детального описания, можете обратится к <a href="http://austinzheng.com/2014/12/17/custom-pattern-matching/">статье Austin Zheng</a>, правда она на английском.</p>
<p>Мы рассмотрели все возможные варианты, однако перед тем, как мы продолжим, есть еще одна важная вещь для рассмотрения.</p>
<h3 id="fallthrough-break-и-метки">Fallthrough, Break, и метки</h3>
<p>Этот раздел напрямую к pattern matching’у не относится, но затрагивает то, как работает конструкция switch, поэтому вкратце рассмотрим. По-умолчанию, и в отличии от C/C++/Objective-C, конструкция switch не обрабатывает последующии конструкции выбора (case), поэтому нет необходимости в каждом из вариантов писать break. Однако, привычное поведение можно вернуть с помощью оператора fallthrough.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">switch</span> <span class="mi">5</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">5</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Is 5"</span><span class="p">)</span>
<span class="k">fallthrough</span>
<span class="k">default</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Is a number"</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// Will print: "Is 5" "Is a number"</span>
</code></pre></div></div>
<p>Если вам необходимо досрочно выйти, то оператором break можно воспользовать. Зачем это надо, если нет обработки следующих конструкций по-умолчанию, спросите вы? Ну, например, внутри конструкции case вы можете определить, что условие не вполне удовлетворяется, и не стоит выполнять следующие операции:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">userType</span> <span class="o">=</span> <span class="s">"system"</span>
<span class="k">let</span> <span class="nv">userID</span> <span class="o">=</span> <span class="mi">10</span>
<span class="k">switch</span> <span class="p">(</span><span class="n">userType</span><span class="p">,</span> <span class="n">userID</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="p">(</span><span class="s">"system"</span><span class="p">,</span> <span class="n">_</span><span class="p">):</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">userData</span> <span class="o">=</span> <span class="nf">getSystemUser</span><span class="p">(</span><span class="n">userID</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">break</span> <span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"user info: </span><span class="se">\(</span><span class="n">userData</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
<span class="nf">insertIntoRemoteDB</span><span class="p">(</span><span class="n">userData</span><span class="p">)</span>
<span class="k">default</span><span class="p">:</span> <span class="p">()</span>
<span class="p">}</span>
<span class="c1">// ... more code that needs to be executed</span>
</code></pre></div></div>
<p>В примере, мы не вызываем функцию insertIntoRemoteData, если функция getSystemUser вернула nil. Конечно, здесь можно использовать конструкцию if let, но если их будет несколько, код станет выглядеть ужасно.</p>
<p>Но что если вы поместили ваш switch внутри, например, цикла while, и вам надо выйти из цикла, а не из switch? Для таких случаев Swift предоставляет возможность определять метки, для которых будут работать операции break и continue:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">gameLoop</span><span class="p">:</span> <span class="k">while</span> <span class="kc">true</span> <span class="p">{</span>
<span class="k">switch</span> <span class="nf">state</span><span class="p">()</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">waiting</span><span class="p">:</span> <span class="k">continue</span> <span class="n">gameLoop</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">done</span><span class="p">:</span> <span class="nf">calculateNextState</span><span class="p">()</span>
<span class="k">case</span> <span class="o">.</span><span class="nv">gameOver</span><span class="p">:</span> <span class="k">break</span> <span class="n">gameLoop</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>На этом мы рассмотрели весь синтаксис и детали реализации конструкции switch и pattern matching’а. Теперь давайте рассмотрим несколько интересных примеров из реальной жизни.</p>
<h2 id="Примеры-из-реальной-жизни">Примеры из реальной жизни</h2>
<h3 id="optionals">Optionals</h3>
<p>Существует огромное количество способов разворачивания Optional, и pattern matching - один из них. Наверняка вы этим часто пользуетесь, но пример все же вот:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">result</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="o">=</span> <span class="nf">secretMethod</span><span class="p">()</span>
<span class="k">switch</span> <span class="n">result</span> <span class="p">{</span>
<span class="k">case</span> <span class="nv">nil</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"is nothing"</span><span class="p">)</span>
<span class="k">case</span> <span class="k">let</span> <span class="nv">a</span><span class="p">?:</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="n">a</span><span class="se">)</span><span class="s"> is a value"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Как можно заметить, result может быть строкой, а может быть и nil - это Optional. Можно поиграться с возвращаемым значением secretMethod() и посмотреть на результат. Кроме того, если в result есть значение, то его можно привязать к переменной (в нашем случае - a). С помощью такого кода - мы явно разделяем два случая - когда там нет значения и когда какое-то есть.</p>
<h2 id="Сопоставление-типов">Сопоставление типов</h2>
<p>Благодаря строгой типизации в Swift, необходимость в runtime проверке типов возникает редко, реже, нежели в Objective-C. Однако, такие случаи иногда встречаются, особенно если вы работаете с наследием Objective-C (особенно, если кодовая база не была обновлена для работы с generic’ами). В таком случае вам нужна проверка типов. Предположим, у нас есть массив NSString’ов и NSNumber’ов:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">u</span> <span class="o">=</span> <span class="kt">NSArray</span><span class="p">(</span><span class="nv">array</span><span class="p">:</span> <span class="p">[</span><span class="kt">NSString</span><span class="p">(</span><span class="nv">string</span><span class="p">:</span> <span class="s">"String1"</span><span class="p">),</span> <span class="kt">NSNumber</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="mi">20</span><span class="p">),</span> <span class="kt">NSNumber</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="mi">40</span><span class="p">)])</span>
</code></pre></div></div>
<p>Когда мы будем итерироваться по этому NSArray, мы не знаем точно, объект какого типа мы получаем. В этом случае нам поможет конструкция switch:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">x</span> <span class="k">in</span> <span class="n">u</span> <span class="p">{</span>
<span class="k">switch</span> <span class="n">x</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">_</span> <span class="k">as</span> <span class="kt">NSString</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"string"</span><span class="p">)</span>
<span class="k">case</span> <span class="n">_</span> <span class="k">as</span> <span class="kt">NSNumber</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"number"</span><span class="p">)</span>
<span class="k">default</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Unknown types"</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="Сопоставляем-баллы-с-оценками">Сопоставляем баллы с оценками</h3>
<p>Предположим вы подрядились написать iOS приложение для учителей в американских школах. Там учитель желает быстро уметь конвертировать некоторое количество баллов (от 0 до 100) в оценку (A - F). И тут pattern matching нам поможет:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">aGrade</span> <span class="o">=</span> <span class="mi">84</span>
<span class="k">switch</span> <span class="n">aGrade</span> <span class="p">{</span>
<span class="k">case</span> <span class="mi">90</span><span class="o">...</span><span class="mi">100</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"A"</span><span class="p">)</span>
<span class="k">case</span> <span class="mi">80</span><span class="o">...</span><span class="mi">90</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"B"</span><span class="p">)</span>
<span class="k">case</span> <span class="mi">70</span><span class="o">...</span><span class="mi">80</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"C"</span><span class="p">)</span>
<span class="k">case</span> <span class="mi">60</span><span class="o">...</span><span class="mi">70</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"D"</span><span class="p">)</span>
<span class="k">case</span> <span class="mi">0</span><span class="o">...</span><span class="mi">60</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"F"</span><span class="p">)</span>
<span class="k">default</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Incorrect Grade"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="Частота-слов">Частота слов</h3>
<p>У нас есть набор пар, каждая из которых представляет собой слово и его частоту в некотором тексте. Наша задача - сделать пороговый фильтр - отфильтровать те слова, частота которых больше некоторого порогового значения и вывести их, без частот.</p>
<p>Вот наши слова:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">wordFreqs</span> <span class="o">=</span> <span class="p">[(</span><span class="s">"k"</span><span class="p">,</span> <span class="mi">5</span><span class="p">),</span> <span class="p">(</span><span class="s">"a"</span><span class="p">,</span> <span class="mi">7</span><span class="p">),</span> <span class="p">(</span><span class="s">"b"</span><span class="p">,</span> <span class="mi">3</span><span class="p">)]</span>
</code></pre></div></div>
<p>Простое решение через map и filter:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">res</span> <span class="o">=</span> <span class="n">wordFreqs</span><span class="o">.</span><span class="nf">filter</span><span class="p">({</span> <span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Bool</span> <span class="k">in</span>
<span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="mi">1</span> <span class="o">></span> <span class="mi">3</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">true</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="p">})</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="mi">0</span> <span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="n">res</span><span class="p">)</span>
</code></pre></div></div>
<p>Однако, с помощью flatmap (map которая возвращает только не nil элементы), можно улучшить и это решение. Первое и самое важное, мы можем избавиться от e.1 и иметь правильное разложение с использованием кортежей. И нам потребуется только один вызов flatmap, вместо filter и map, которые ухудшают производительность.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">res</span> <span class="o">=</span> <span class="n">wordFreqs</span><span class="o">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="o">-></span> <span class="kt">String</span><span class="p">?</span> <span class="k">in</span>
<span class="k">switch</span> <span class="n">e</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">let</span> <span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">t</span><span class="p">)</span> <span class="k">where</span> <span class="n">t</span> <span class="o">></span> <span class="mi">3</span><span class="p">:</span> <span class="k">return</span> <span class="n">s</span>
<span class="k">default</span><span class="p">:</span> <span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="n">res</span><span class="p">)</span>
</code></pre></div></div>
<h3 id="Обход-дерева-каталогов">Обход дерева каталогов</h3>
<p>Предположим, что мы хотим обойти дерево файлов и найти:</p>
<ul>
<li>Все “psd” файлы от customer1 и customer2</li>
<li>Все “blend” файлы от customer2</li>
<li>Все “jpeg” файлы от всех клиентов</li>
</ul>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">guard</span> <span class="k">let</span> <span class="nv">enumerator</span> <span class="o">=</span> <span class="kt">FileManager</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">enumerator</span><span class="p">(</span><span class="nv">atPath</span><span class="p">:</span> <span class="s">"/customers/2014"</span><span class="p">)</span>
<span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
<span class="k">for</span> <span class="k">case</span> <span class="k">let</span> <span class="nv">url</span> <span class="k">as</span> <span class="kt">URL</span> <span class="k">in</span> <span class="n">enumerator</span> <span class="p">{</span>
<span class="k">switch</span> <span class="p">(</span><span class="n">url</span><span class="o">.</span><span class="n">pathComponents</span><span class="p">,</span> <span class="n">url</span><span class="o">.</span><span class="n">pathExtension</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// psd files from customer1, customer2</span>
<span class="k">case</span> <span class="p">(</span><span class="k">let</span> <span class="nv">f</span><span class="p">,</span> <span class="s">"psd"</span><span class="p">)</span>
<span class="k">where</span> <span class="n">f</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="s">"customer1"</span><span class="p">)</span> <span class="o">||</span>
<span class="n">f</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="s">"customer2"</span><span class="p">):</span> <span class="nf">print</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
<span class="c1">// blend files from customer2</span>
<span class="k">case</span> <span class="p">(</span><span class="k">let</span> <span class="nv">f</span><span class="p">,</span> <span class="s">"blend"</span><span class="p">)</span>
<span class="k">where</span> <span class="n">f</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="s">"customer2"</span><span class="p">):</span> <span class="nf">print</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
<span class="c1">// all jpg files</span>
<span class="k">case</span> <span class="p">(</span><span class="n">_</span><span class="p">,</span> <span class="s">"jpg"</span><span class="p">):</span>
<span class="nf">print</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
<span class="k">default</span><span class="p">:</span> <span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="Фибоначчи">Фибоначчи</h3>
<p>Теперь “черная магия”, считаем числа Фибоначчи с помощью pattern matching’а:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">_</span> <span class="nv">i</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">switch</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="k">let</span> <span class="nv">n</span> <span class="k">where</span> <span class="n">n</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">:</span> <span class="k">return</span> <span class="mi">0</span>
<span class="k">case</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">:</span> <span class="k">return</span> <span class="mi">1</span>
<span class="k">case</span> <span class="k">let</span> <span class="nv">n</span><span class="p">:</span> <span class="k">return</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="nf">fibonacci</span><span class="p">(</span><span class="mi">8</span><span class="p">))</span>
</code></pre></div></div>
<p>Не запускайте на больших числах :) - получите переполнение стэка.</p>
<h3 id="Унаследованнное-api-и-извлечение-значений">Унаследованнное API и извлечение значений</h3>
<p>Частно, когда вы получаете данные из внешнего источника (библиотеки, API и т.д.), перед обработкой хорошей практикой считается их проверить на корректность. Необходимо убедиться что все необходимые ключи присутствуют, данные имеют корректный тип или, например, массив нужной длины. Отсутствие такой проверки может вести от некорректного поведения (нет ключа) до падения приложения (индекс за границей массива). Классический вариант такой проверки - вложенные if’ы.</p>
<p>Предположим, что у нас есть API, которое возвращает пользователя. Однако, существуют два типа пользователей: привелигированные пользователи (такие как администратор) и обычные (John B, Bill Gates и т.д.). Учитывая нюансы развития системы, имеем некоторые особенности такого API:</p>
<ul>
<li><code class="highlighter-rouge">system</code> (привелигированные) и <code class="highlighter-rouge">local</code> (обычные) пользователи получаются с помощью одного и того же вызова API</li>
<li>Ключ <code class="highlighter-rouge">department</code> (подразделение) может отсутствовать, так как первые версии базы данных не содержали такого ключа и поэтому для ранних пользователей он необязателен к заполнению.</li>
<li>Массив <code class="highlighter-rouge">name</code> (имя) может содержать либо 4 значения (логин, фамилия, имя, отчество) или 2 значения (логин и полное имя). Количество значений зависит от того, как давно был создан пользователь.</li>
<li>Поле <code class="highlighter-rouge">age</code> (возраст) - целое число указывающее количество полных лет</li>
</ul>
<p>От нашей системы требуется создавать пользовательские аккаунты для всех привелигированных пользователей со следующей информацией: логин (<code class="highlighter-rouge">username</code>), подразделение (<code class="highlighter-rouge">department</code>). Нам необходимы только пользователи с датой рождения после 1980 года. Если подразделение не задано, то по-умолчанию считаем “Corp”.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">legacyAPI</span><span class="p">(</span><span class="nv">id</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-></span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">[</span>
<span class="s">"type"</span><span class="p">:</span> <span class="s">"system"</span><span class="p">,</span>
<span class="s">"department"</span><span class="p">:</span> <span class="s">"Dark Arts"</span><span class="p">,</span>
<span class="s">"age"</span><span class="p">:</span> <span class="mi">57</span><span class="p">,</span>
<span class="s">"name"</span><span class="p">:</span> <span class="p">[</span><span class="s">"voldemort"</span><span class="p">,</span> <span class="s">"Tom"</span><span class="p">,</span> <span class="s">"Marvolo"</span><span class="p">,</span> <span class="s">"Riddle"</span><span class="p">]</span>
<span class="p">]</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Учитывая вышеназванные ограничения, давайте воспользуемся pattern matching’ом:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">item</span> <span class="o">=</span> <span class="nf">legacyAPI</span><span class="p">(</span><span class="nv">id</span><span class="p">:</span> <span class="mi">4</span><span class="p">)</span>
<span class="k">switch</span> <span class="p">(</span><span class="n">item</span><span class="p">[</span><span class="s">"type"</span><span class="p">],</span> <span class="n">item</span><span class="p">[</span><span class="s">"department"</span><span class="p">],</span> <span class="n">item</span><span class="p">[</span><span class="s">"age"</span><span class="p">],</span> <span class="n">item</span><span class="p">[</span><span class="s">"name"</span><span class="p">])</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">let</span> <span class="p">(</span><span class="n">sys</span> <span class="k">as</span> <span class="kt">String</span><span class="p">,</span> <span class="n">dep</span> <span class="k">as</span> <span class="kt">String</span><span class="p">,</span> <span class="n">age</span> <span class="k">as</span> <span class="kt">Int</span><span class="p">,</span> <span class="n">name</span> <span class="k">as</span> <span class="p">[</span><span class="kt">String</span><span class="p">])</span>
<span class="k">where</span> <span class="n">age</span> <span class="o"><</span> <span class="mi">1980</span> <span class="o">&&</span> <span class="n">sys</span> <span class="o">==</span> <span class="s">"system"</span><span class="p">:</span>
<span class="nf">createSystemUser</span><span class="p">(</span><span class="n">name</span><span class="o">.</span><span class="n">count</span> <span class="o">==</span> <span class="mi">2</span> <span class="p">?</span> <span class="n">name</span><span class="o">.</span><span class="n">last</span><span class="o">!</span> <span class="p">:</span> <span class="n">name</span><span class="o">.</span><span class="n">first</span><span class="o">!</span><span class="p">,</span> <span class="nv">dep</span><span class="p">:</span> <span class="n">dep</span><span class="o">.</span><span class="n">characters</span><span class="o">.</span><span class="n">count</span> <span class="o">></span> <span class="mi">0</span> <span class="p">?</span> <span class="nv">dep</span> <span class="p">:</span> <span class="s">"Corp"</span><span class="p">)</span>
<span class="k">default</span><span class="p">:()</span>
<span class="p">}</span>
<span class="c1">// returns ("voldemort", "Dark Arts")</span>
</code></pre></div></div>
<p>Обратите внимание, что приведенный выше код делает одно опасное предположение, которое заключается в том, что если массив <code class="highlighter-rouge">name</code> имеет не 2 значения, то он обязан иметь 4. Если это предположение не выполнится, мы получим крэш.</p>
<p>Во всем остальном - это удачные пример того, как pattern matching может помочь вам писать элегантный код и упростить извлечение значений.</p>
<p>И обратите внимание на то, что мы записали let только перед кортежом, дабы не повторять его для каждой из переменных.</p>
<h2 id="Паттерны-с-другими-конструкциями">Паттерны с другими конструкциями</h2>
<p>Как указывает документация на swift, все обозначенные выше паттерны могут быть использованы также с конструкциями if, for и guard.</p>
<p>Небольшой пример, связывание значений (value binding), кортеж и приведение типов в одном примере для всех трех конструкций:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// This is just a collection of keywords that compiles. This code makes no sense</span>
<span class="kd">func</span> <span class="nf">valueTupleType</span><span class="p">(</span><span class="n">_</span> <span class="nv">a</span><span class="p">:</span> <span class="p">(</span><span class="kt">Int</span><span class="p">,</span> <span class="kt">Any</span><span class="p">))</span> <span class="o">-></span> <span class="kt">Bool</span> <span class="p">{</span>
<span class="c1">// guard case Example</span>
<span class="k">guard</span> <span class="k">case</span> <span class="nf">let</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">_</span> <span class="k">as</span> <span class="kt">String</span><span class="p">)</span> <span class="o">=</span> <span class="n">a</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">false</span><span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="c1">// for case example</span>
<span class="k">for</span> <span class="k">case</span> <span class="nf">let</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">_</span> <span class="k">as</span> <span class="kt">String</span><span class="p">)</span> <span class="k">in</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// if case example</span>
<span class="k">if</span> <span class="k">case</span> <span class="nf">let</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">_</span> <span class="k">as</span> <span class="kt">String</span><span class="p">)</span> <span class="o">=</span> <span class="n">a</span> <span class="p">{</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"if"</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// switch case example</span>
<span class="k">switch</span> <span class="n">a</span> <span class="p">{</span>
<span class="k">case</span> <span class="nf">let</span> <span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">_</span> <span class="k">as</span> <span class="kt">String</span><span class="p">):</span>
<span class="nf">print</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">true</span>
<span class="k">default</span><span class="p">:</span> <span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">let</span> <span class="nv">u</span><span class="p">:</span> <span class="kt">Any</span> <span class="o">=</span> <span class="s">"a"</span>
<span class="k">let</span> <span class="nv">b</span><span class="p">:</span> <span class="kt">Any</span> <span class="o">=</span> <span class="mi">5</span>
<span class="nf">print</span><span class="p">(</span><span class="nf">valueTupleType</span><span class="p">((</span><span class="mi">5</span><span class="p">,</span> <span class="n">u</span><span class="p">)))</span>
<span class="nf">print</span><span class="p">(</span><span class="nf">valueTupleType</span><span class="p">((</span><span class="mi">5</span><span class="p">,</span> <span class="n">b</span><span class="p">)))</span>
<span class="c1">// 5, 5, "if 5", 5, true, false</span>
</code></pre></div></div>
<p>Теперь давайте немножко рассмотрим каждую из конструкций более подробно.</p>
<h3 id="Используем-for-case">Используем for case</h3>
<p>Pattern matching в swift стал настолько важен, что возможности конструкции switch распространили и на другие. Для примера, давайте напишем функцию обработки массива, которая возвращает все существующие (non-nil) элементы:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="n">nonnil</span><span class="o"><</span><span class="kt">T</span><span class="o">></span><span class="p">(</span><span class="n">_</span> <span class="nv">array</span><span class="p">:</span> <span class="p">[</span><span class="kt">T</span><span class="p">?])</span> <span class="o">-></span> <span class="p">[</span><span class="kt">T</span><span class="p">]</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">result</span><span class="p">:</span> <span class="p">[</span><span class="kt">T</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="k">case</span> <span class="k">let</span> <span class="nv">x</span><span class="p">?</span> <span class="k">in</span> <span class="n">array</span> <span class="p">{</span>
<span class="n">result</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">result</span>
<span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="nf">nonnil</span><span class="p">([</span><span class="s">"a"</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="s">"b"</span><span class="p">,</span> <span class="s">"c"</span><span class="p">,</span> <span class="kc">nil</span><span class="p">]))</span>
</code></pre></div></div>
<p>Ключевое слово case может быть использовано в циклах for аналогично конструкции switch. Другой пример, помните игру, которую мы рассматривали ранее? Хорошо, после первого рефакторинга она выглядела так:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">Entity</span> <span class="p">{</span>
<span class="kd">enum</span> <span class="kt">EntityType</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">soldier</span>
<span class="k">case</span> <span class="n">player</span>
<span class="p">}</span>
<span class="k">case</span> <span class="kt">Entry</span><span class="p">(</span><span class="nv">type</span><span class="p">:</span> <span class="kt">EntityType</span><span class="p">,</span> <span class="nv">x</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">hp</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Теперь, мы можем нарисовать все элементы с меньшим количеством кода:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="k">case</span> <span class="k">let</span> <span class="nv">Entity</span><span class="o">.</span><span class="kt">Entry</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="k">in</span> <span class="nf">gameEntities</span><span class="p">()</span>
<span class="k">where</span> <span class="n">x</span> <span class="o">></span> <span class="mi">0</span> <span class="o">&&</span> <span class="n">y</span> <span class="o">></span> <span class="mi">0</span> <span class="p">{</span>
<span class="nf">drawEntity</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Одной строчкой извлекаем все необходимые свойства, убеждаемся что не рисуем ниже и левее 0 и в конце-концов таки вызываем функцию рисования.</p>
<p>Для того, чтобы убедиться что игрок выиграл игру, нам необходимо знать, остался ли в живых хотя бы один солдат (health > 0).</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">gameOver</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Bool</span> <span class="p">{</span>
<span class="k">for</span> <span class="k">case</span> <span class="kt">Entity</span><span class="o">.</span><span class="kt">Entry</span><span class="p">(</span><span class="o">.</span><span class="n">soldier</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="k">let</span> <span class="nv">hp</span><span class="p">)</span> <span class="k">in</span> <span class="nf">gameEntities</span><span class="p">()</span>
<span class="k">where</span> <span class="n">hp</span> <span class="o">></span> <span class="mi">0</span> <span class="p">{</span><span class="k">return</span> <span class="kc">false</span><span class="p">}</span>
<span class="k">return</span> <span class="kc">true</span>
<span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="nf">gameOver</span><span class="p">())</span>
</code></pre></div></div>
<p>Хорошо то, что сопоставление с .soldier является частью запроса. Это больше напоминает SQL нежели императивный цикл. Кроме того, такая запись делает нашу задумку более явной для компилятора и открывает возможности для оптимизаций. И нам нет необходимости раскрывать полное имя типа (Entity.EntityType.soldier).</p>
<h3 id="Используем-guard-case">Используем guard case</h3>
<p>Другое ключевое слово, которое поддерживает pattern matching - это новая конструкция guard. Вы наверняка уже использовали ее для привязки optional’ов к локальному scope без каскадный конструкций так:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">example</span><span class="p">(</span><span class="n">_</span> <span class="nv">a</span><span class="p">:</span> <span class="kt">String</span><span class="p">?)</span> <span class="p">{</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">a</span> <span class="o">=</span> <span class="n">a</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="p">}</span>
<span class="nf">example</span><span class="p">(</span><span class="s">"yes"</span><span class="p">)</span>
</code></pre></div></div>
<p>guard let case позволяет использовать всю мощь pattern matching’а. Давайте снова взглянем на наших солдатиков. Нам необходимо посчитать требуемую HP до того, как игрок будет полностью здоров. Солдаты не могут восстанавливать HP, поэтому нам необходимо для них всегда возвращать 0.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">MAX_HP</span> <span class="o">=</span> <span class="mi">100</span>
<span class="kd">func</span> <span class="nf">healthHP</span><span class="p">(</span><span class="n">_</span> <span class="nv">entity</span><span class="p">:</span> <span class="kt">Entity</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">guard</span> <span class="k">case</span> <span class="k">let</span> <span class="nv">Entity</span><span class="o">.</span><span class="kt">Entry</span><span class="p">(</span><span class="o">.</span><span class="n">player</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">hp</span><span class="p">)</span> <span class="o">=</span> <span class="n">entity</span><span class="p">,</span> <span class="n">hp</span> <span class="o"><</span> <span class="kt">MAX_HP</span>
<span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="mi">0</span> <span class="p">}</span>
<span class="k">return</span> <span class="kt">MAX_HP</span> <span class="o">-</span> <span class="n">hp</span>
<span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Soldier"</span><span class="p">,</span> <span class="nf">healthHP</span><span class="p">(</span><span class="kt">Entity</span><span class="o">.</span><span class="kt">Entry</span><span class="p">(</span><span class="nv">type</span><span class="p">:</span> <span class="o">.</span><span class="n">soldier</span><span class="p">,</span> <span class="nv">x</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">hp</span><span class="p">:</span> <span class="mi">79</span><span class="p">)))</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Player"</span><span class="p">,</span> <span class="nf">healthHP</span><span class="p">(</span><span class="kt">Entity</span><span class="o">.</span><span class="kt">Entry</span><span class="p">(</span><span class="nv">type</span><span class="p">:</span> <span class="o">.</span><span class="n">player</span><span class="p">,</span> <span class="nv">x</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">hp</span><span class="p">:</span> <span class="mi">57</span><span class="p">)))</span>
<span class="c1">// Prints:</span>
<span class="s">"Soldier 0"</span>
<span class="s">"Player 43"</span>
</code></pre></div></div>
<p>Замечательный пример рассмотренных нами возможностей.</p>
<ul>
<li>Он просто и понятен, без каскадных конструкций</li>
<li>Логика и инициализация состояния обрабатывается в начале функции, что повышает читаемость</li>
<li>Лаконичный</li>
</ul>
<p>Можно совмещать с конструкций switch для совмещения сложных логических конструкций в легкую для прочтения форму. Конечно, сама логика от этого не станет легче в восприятии, но как минимум, она будет лаконичнее и короче. Особенно при использовании перечислений.</p>
<h3 id="Используем-if-case">Используем if case</h3>
<p>if case можно испоьзовать в противовес guard case. Это замечательный способ извлечения и сопоставления данных в конкретной ветви условия. Продолжаем с нашими солдатиками - нам необходима функция перемещения. Так как наши сущности - это перечисления, нам необходимо вернуть измененную сущность.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">move</span><span class="p">(</span><span class="n">_</span> <span class="nv">entity</span><span class="p">:</span> <span class="kt">Entity</span><span class="p">,</span> <span class="nv">xd</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">yd</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Entity</span> <span class="p">{</span>
<span class="k">if</span> <span class="k">case</span> <span class="kt">Entity</span><span class="o">.</span><span class="kt">Entry</span><span class="p">(</span><span class="k">let</span> <span class="nv">t</span><span class="p">,</span> <span class="k">let</span> <span class="nv">x</span><span class="p">,</span> <span class="k">let</span> <span class="nv">y</span><span class="p">,</span> <span class="k">let</span> <span class="nv">hp</span><span class="p">)</span> <span class="o">=</span> <span class="n">entity</span><span class="p">,</span>
<span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">xd</span><span class="p">)</span> <span class="o"><</span> <span class="mi">1000</span> <span class="o">&&</span> <span class="p">(</span><span class="n">y</span> <span class="o">+</span> <span class="n">yd</span><span class="p">)</span> <span class="o"><</span> <span class="mi">1000</span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">Entity</span><span class="o">.</span><span class="kt">Entry</span><span class="p">(</span><span class="nv">type</span><span class="p">:</span> <span class="n">t</span><span class="p">,</span> <span class="nv">x</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">xd</span><span class="p">),</span> <span class="nv">y</span><span class="p">:</span> <span class="p">(</span><span class="n">y</span> <span class="o">+</span> <span class="n">yd</span><span class="p">),</span> <span class="nv">hp</span><span class="p">:</span> <span class="n">hp</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">entity</span>
<span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="nf">move</span><span class="p">(</span><span class="kt">Entity</span><span class="o">.</span><span class="kt">Entry</span><span class="p">(</span><span class="nv">type</span><span class="p">:</span> <span class="o">.</span><span class="n">soldier</span><span class="p">,</span> <span class="nv">x</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">hp</span><span class="p">:</span> <span class="mi">79</span><span class="p">),</span> <span class="nv">xd</span><span class="p">:</span> <span class="mi">30</span><span class="p">,</span> <span class="nv">yd</span><span class="p">:</span> <span class="mi">500</span><span class="p">))</span>
<span class="c1">// prints: Entry(main.Entity.EntityType.soldier, 40, 510, 79)</span>
</code></pre></div></div>
Неожиданные проблемы при написании iOS приложений на Swift2017-04-08T00:00:00+00:00http://mrdekk.ru/2017/04/08/swift-oops<p>Как оказалось, не все так просто в ObjC-Swift Interoperability. Иногда возникают проблемы, которые очень сложно диагностировать, и поэтому сложно починить, но которые при этом оказываются достаточно простыми. В этой статье такие проблемы опишу, список пополняется и вы можете в этом поучаствовать.</p>
<h3 id="swift-и-cabasicanimation">Swift и CABasicAnimation</h3>
<p>Если коротко - CABasicAnimation не работает на Swift’овых классах, например, если хочется сделать примерно такое</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">SomeLayer</span><span class="p">:</span> <span class="kt">CALayer</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">rotate</span><span class="p">:</span> <span class="kt">CGFloat</span>
<span class="kd">func</span> <span class="nf">setupRotation</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">anim</span> <span class="o">=</span> <span class="kt">CABasicAnimation</span><span class="p">(</span><span class="nv">keyPath</span><span class="p">:</span> <span class="kd">#keyPath(</span><span class="nf">rotate</span><span class="kd">)</span><span class="p">)</span>
<span class="n">anim</span><span class="o">.</span><span class="n">fromValue</span> <span class="o">=</span> <span class="kt">NS</span>
<span class="n">anim</span><span class="o">.</span><span class="n">fromValue</span> <span class="o">=</span> <span class="kt">NSNumber</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">)</span>
<span class="n">anim</span><span class="o">.</span><span class="n">toValue</span> <span class="o">=</span> <span class="kt">NSNumber</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="mf">2.0</span> <span class="o">*</span> <span class="kt">M_PI</span><span class="p">)</span>
<span class="n">anim</span><span class="o">.</span><span class="n">duration</span> <span class="o">=</span> <span class="mf">0.3</span>
<span class="n">anim</span><span class="o">.</span><span class="n">fillMode</span> <span class="o">=</span> <span class="n">kCAFillModeBoth</span>
<span class="nf">removeAnimation</span><span class="p">(</span><span class="nv">forKey</span><span class="p">:</span> <span class="kd">#keyPath(</span><span class="nf">rotate</span><span class="kd">)</span><span class="p">)</span>
<span class="nf">add</span><span class="p">(</span><span class="n">anim</span><span class="p">,</span> <span class="nv">forKey</span><span class="p">:</span> <span class="kd">#keyPath(</span><span class="nf">rotate</span><span class="kd">)</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>работать не будет, даже если вы не забудете про</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+ (BOOL)needsDisplayForKey:(NSString*)key
</code></pre></div></div>
<p>Потому что Swift dynamic свойства это не то же самое что ObjC @dynamic.</p>
<p>Единственный выход, который я нашел на данный момент, - в таких случаях писать такие классы на ObjC.</p>
Установка rmagick на Mac OS X (macOS)2017-03-02T00:00:00+00:00http://mrdekk.ru/2017/03/02/rmagick-install-osx<p>Если у вас по каким-то причинам не устанавливается rmagick, например:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>current directory: /home/kb10uy/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rmagick-42aa8ce34f61/ext/RMagick
/home/kb10uy/.rbenv/versions/2.3.1/bin/ruby -r ./siteconf20160812-28804-12a4s7p.rb extconf.rb
checking for gcc... yes
checking for Magick-config... no
checking for pkg-config... yes
checking for outdated ImageMagick version (<= 6.4.9)... no
checking for presence of MagickWand API (ImageMagick version >= 6.9.0)... no
checking for Ruby version >= 1.8.5... yes
checking for stdint.h... yes
checking for sys/types.h... yes
checking for wand/MagickWand.h... no
Can't install RMagick 2.15.4. Can't find MagickWand.h.
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers. Check the mkmf.log file for more details. You may
need configuration options.
Provided configuration options:
--with-opt-dir
--without-opt-dir
--with-opt-include
--without-opt-include=${opt-dir}/include
--with-opt-lib
--without-opt-lib=${opt-dir}/lib
--with-make-prog
--without-make-prog
--srcdir=.
--curdir
--ruby=/home/kb10uy/.rbenv/versions/2.3.1/bin/$(RUBY_BASE_NAME)
To see why this extension failed to compile, please check the mkmf.log which can be found here:
/home/kb10uy/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/extensions/armv6l-linux/2.3.0-static/rmagick-42aa8ce34f61/mkmf.log
extconf failed, exit code 1
</code></pre></div></div>
<p>Или например так:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jjdevenuta(opal)$ gem install rmagick
Fetching: rmagick-2.13.1.gem (100%)
Building native extensions. This could take a while...
ERROR: Error installing rmagick:
ERROR: Failed to build gem native extension.
/Users/jjdevenuta/.rvm/rubies/ruby-1.9.2-head/bin/ruby extconf.rb
checking for Ruby version >= 1.8.5... yes
checking for gcc... yes
checking for Magick-config... no
Can't install RMagick 2.13.1. Can't find Magick-config in /Users/jjdevenuta/.rvm/gems/ruby-1.9.2-head@rails3/bin:/Users/jjdevenuta/.rvm/gems/ruby-1.9.2-head@global/bin:/Users/jjdevenuta/.rvm/rubies/ruby-1.9.2-head/bin:/Users/jjdevenuta/.rvm/bin:/usr/local/bin:/usr/local/sbin:/usr/local/mysql/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/git/bin:/usr/X11/bin
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers. Check the mkmf.log file for more
details. You may need configuration options.
Provided configuration options:
--with-opt-dir
--without-opt-dir
--with-opt-include
--without-opt-include=${opt-dir}/include
--with-opt-lib
--without-opt-lib=${opt-dir}/lib
--with-make-prog
--without-make-prog
--srcdir=.
--curdir
--ruby=/Users/jjdevenuta/.rvm/rubies/ruby-1.9.2-head/bin/ruby
</code></pre></div></div>
<p>То это значит что у вас в системе стоит imagemagick 7 который не поддерживается rmagick (там переименовали и переместили важные заголовочные файлы). Чтобы поставить rmagick вам надо imagemagick 6, сделать это можно так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew update
brew rm imagemagick
brew install imagemagick@6
brew link imagemagick@6 <span class="nt">--force</span>
bundle
</code></pre></div></div>
<p>Взято <a href="https://github.com/rmagick/rmagick/issues/256">отсюдова</a></p>
Детектируем недоступные API на данном minimal deployment target2016-12-05T00:00:00+00:00http://mrdekk.ru/2016/12/05/detect-unavailable-api-usage<p>Как правило iOS приложения (да и в общем macOS тоже) пишутся с использованием самых новых инструментов разработки (читай последних SDK), но при этом поддерживаются предыдущие версии iOS (macOS). Бывают ситуации, когда по недосмотру используются API из новых версий, которые недоступны на данном minimal deployment target. Если такую ошибку пропустить в прод (зарелизить приложение), то вызов такого метода на версии iOS которая его не поддерживает неминуемо приведет к крэшу. Дабы облегчить себе жизнь можно сделать вот что:</p>
<ol>
<li>Идем в Build Settings нужного проекта в xcode</li>
<li>Находим раздел “Apple LLVM 8.0 - Custom Compiler Flags”</li>
<li>Находим там пункт “Other Warning Flags”</li>
<li>В него для требуемой настройки сборки (например Debug) добавляем такой флаг “-Wpartial-availability”</li>
</ol>
<p>После этого Xcode начинает отображать такие места как #warning</p>
<p>Для того, чтобы Xcode не писал #warning’и для тех мест где вы делаете это намерянно, можно переопределить (где-нибудь в pre-compile заголовках) такие макро:</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define START_IGNORE_PARTIAL _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wpartial-availability\"")
#define END_IGNORE_PARTIAL _Pragma("clang diagnostic pop")
</span></code></pre></div></div>
Поваренная книга ARC2016-11-24T00:00:00+00:00http://mrdekk.ru/2016/11/24/arc-cookbook<ul>
<li>Про ARC можно почитать тут: <a href="http://developer.apple.com/library/ios/#releasenotes/ObjectiveC/RN-TransitioningToARC/_index.html">Transitioning to ARC Release Notes</a></li>
<li>Майк Эш (Mike Ash) написал хорошую статью в его Friday <a href="http://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html">Q&As</a></li>
<li>Детальная техническая документация доступна на <a href="http://clang.llvm.org/docs/AutomaticReferenceCounting.html">сайте CLANG’а</a> проекта LLVM.</li>
</ul>
<p>Описанные в этой статье рецепты подразумевают использование iOS 5 и выше. В iOS 4 не доступны weak ссылки, которые являются очень важной вещью. Я понимаю, что в конце 2016 года уже даже iOS 7 официально не поддерживается, но вдруг найдутся такие читатели, которые вынуждены поддерживать очень старый код.</p>
<p>В статье в примерах используется Objective-C. Примеры на Swift’е не привожу - так как там это во-первых все несколько проще, во-вторых, кажется не составит труда отобразить эти правила на Swift, если это необходимо.</p>
<h2 id="Общее">Общее</h2>
<ul>
<li>скалярные ivar свойства должны использовать <strong>assign</strong></li>
</ul>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">assign</span><span class="p">)</span> <span class="kt">int</span> <span class="n">scalarInt</span><span class="p">;</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">assign</span><span class="p">)</span> <span class="n">CGFloat</span> <span class="n">scalarFloat</span><span class="p">;</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">assign</span><span class="p">)</span> <span class="n">CGPoint</span> <span class="n">scalarStruct</span><span class="p">;</span>
</code></pre></div></div>
<ul>
<li>ссылочные ivar свойства, на которые необходимо иметь сильную ссылку или это дочерние свойства (“вниз” по иерархии) должны использовать <strong>strong</strong></li>
</ul>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">strong</span><span class="p">)</span> <span class="n">id</span> <span class="n">childObject</span><span class="p">;</span>
</code></pre></div></div>
<ul>
<li>ссылочные ivar свойства на родителя (“вверх” по иерархии) должны использовать <strong>weak</strong>. Кроме того, при ссылках “вне” иерархии (например, делегаты) лучше использовать <strong>weak</strong> как наиболее безопасный.</li>
</ul>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">weak</span><span class="p">)</span> <span class="n">id</span> <span class="n">parentObject</span><span class="p">;</span>
<span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">weak</span><span class="p">)</span> <span class="n">NSObject</span> <span class="o"><</span><span class="n">SomeDelegate</span><span class="o">></span> <span class="o">*</span><span class="n">delegate</span><span class="p">;</span>
</code></pre></div></div>
<ul>
<li>в блоках лучше использовать <strong>copy</strong></li>
</ul>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@property</span> <span class="p">(</span><span class="n">nonatomic</span><span class="p">,</span> <span class="n">copy</span><span class="p">)</span> <span class="n">SomeBlockType</span> <span class="n">someBlock</span><span class="p">;</span>
</code></pre></div></div>
<ul>
<li>В dealloc
<ul>
<li>удаляем обсерверы (observers)</li>
<li>отписываемся от уведомлений</li>
<li>для всех не weak свойств устанавливаем nil</li>
<li>инвалидируем все таймеры</li>
</ul>
</li>
<li>IBOutlet’ы должны быть <strong>weak</strong> за исключением корневых, которые должны быть <strong>strong</strong>.</li>
</ul>
<h2 id="bridging">Bridging</h2>
<p>Из документации</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">id</span> <span class="n">myId</span><span class="p">;</span>
<span class="n">CFStringRef</span> <span class="n">myCFRef</span><span class="p">;</span>
<span class="n">NSString</span> <span class="o">*</span><span class="n">a</span> <span class="o">=</span> <span class="p">(</span><span class="n">__bridge</span> <span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="n">myCFRef</span><span class="p">;</span> <span class="c1">// тривиальное преобразование (noop)
</span><span class="n">CFStringRef</span> <span class="n">b</span> <span class="o">=</span> <span class="p">(</span><span class="n">__bridge</span> <span class="n">CFStringRef</span><span class="p">)</span><span class="n">myId</span><span class="p">;</span> <span class="c1">// тривиальное преобразование (noop)
</span><span class="n">NSString</span> <span class="o">*</span><span class="n">c</span> <span class="o">=</span> <span class="p">(</span><span class="n">__bridge_transfer</span> <span class="n">NSString</span> <span class="o">*</span><span class="p">)</span><span class="n">myCFRef</span><span class="p">;</span> <span class="c1">// -1 на CFRef
</span><span class="n">CFStringRef</span> <span class="n">d</span> <span class="o">=</span> <span class="p">(</span><span class="n">__bridge_retained</span> <span class="n">CFStringRef</span><span class="p">)</span><span class="n">myId</span><span class="p">;</span> <span class="c1">// возвращает CFRef +1
</span></code></pre></div></div>
<p>Если переводить с непонятного на русский, то:</p>
<ul>
<li><strong>__bridge</strong> - тривиальное преобразование (по отношению к управлению памятью) (noop)</li>
<li><strong>__bridge_transfer</strong> - необходимо для преобразования CF ссылок в объекты Objective-C. ARC уменьшит счетчик ссылок объекта CF, поэтому убедитесь, что CFRef имеет +1.</li>
<li><strong>__bridge_retained</strong> - необходимо для преобразования объектов Objective-C в CF ссылки. Эта операция даст вым CF ссылку с +1. Далее вы будете ответственны за вызов CFRelease у полученной CFRef ссылки где-нибудь в будущем.</li>
</ul>
<h2 id="nserror">NSError</h2>
<p>Вездесущий NSError с точки зрения ARC непростой. Типовое соглашение Cocoa гласит, что ошибки обычно передаются как out-параметры (косвенные указатели).</p>
<p>В ARC out-параметры по-умолчанию <strong>__autoreleasing</strong> и должны реализовываться так:</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">-</span> <span class="p">(</span><span class="n">BOOL</span><span class="p">)</span><span class="nf">performWithError</span><span class="p">:(</span><span class="n">__autoreleasing</span> <span class="n">NSError</span> <span class="o">**</span><span class="p">)</span><span class="nv">error</span>
<span class="p">{</span>
<span class="c1">// ... случилась ошибка ...
</span> <span class="k">if</span> <span class="p">(</span><span class="n">error</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// записываем ее в out-параметр. ARC автоматически autorelease'ит
</span> <span class="o">*</span><span class="n">error</span> <span class="o">=</span> <span class="p">[[</span><span class="n">NSError</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">initWithDomain</span><span class="p">:</span><span class="s">@""</span>
<span class="nf">code</span><span class="p">:</span><span class="o">-</span><span class="mi">1</span>
<span class="nl">userInfo:</span><span class="nb">nil</span><span class="p">];</span>
<span class="k">return</span> <span class="nb">NO</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nb">YES</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>При использовании out-параметров, вы как правило будет использовать __autoreleasing на ваших *error объектах примерно таким образом:</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">NSError</span> <span class="n">__autoreleasing</span> <span class="o">*</span><span class="n">error</span> <span class="o">=</span> <span class="n">error</span><span class="p">;</span>
<span class="n">BOOL</span> <span class="n">OK</span> <span class="o">=</span> <span class="p">[</span><span class="n">myObject</span> <span class="nf">performOperationWithError</span><span class="p">:</span><span class="o">&</span><span class="n">error</span><span class="p">];</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">OK</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// обрабатываем ошибку.
</span><span class="p">}</span>
</code></pre></div></div>
<p>Если __autoreleasing не указать, компилятор автоматически добавит для вас временный autoreleasing объект. Такое решение необходимо как компромисс для обеспечения обратной совместимости. В далекие времена iOS 5 существовали такие настройки компилятора, которые не добавляли автоматически __autoreleasing.</p>
<h2 id="autoreleasepool">@autoreleasepool</h2>
<p>Используйте @autoreleasepool внутри циклов, когда:</p>
<ul>
<li>в нем очень много итераций</li>
<li>в одной итерации создается большое количество временных объектов</li>
</ul>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// если someArray большой
</span><span class="k">for</span> <span class="p">(</span><span class="n">id</span> <span class="n">obj</span> <span class="k">in</span> <span class="n">someArray</span><span class="p">)</span>
<span class="p">{</span>
<span class="err">@autoreleasepool</span>
<span class="p">{</span>
<span class="c1">// или вы создаете много
</span> <span class="c1">// временных объектов тут
</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Создание и уничтожение autorelease пулов через @autoreleasepool дешевле чем даром. Не волнуйтесь использовать их внутри цикла. Если же этих заверений недостаточно, можете воспользоваться профайлером.</p>
<h2 id="blocks">Blocks</h2>
<p>В общем, блоки просто работают, однако есть несколько исключений.</p>
<p>Перед тем как добавлять блоки в коллекцию, необходимо предварительно их скопировать (copy).</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">someBlockType</span> <span class="n">someBlock</span> <span class="o">=</span> <span class="o">^</span><span class="p">{</span><span class="n">NSLog</span><span class="p">(</span><span class="s">@"hi"</span><span class="p">);};</span>
<span class="p">[</span><span class="n">someArray</span> <span class="nf">addObject</span><span class="p">:[</span><span class="n">someBlock</span> <span class="nf">copy</span><span class="p">]];</span>
</code></pre></div></div>
<p>retain-циклы особенно опасны в блоках. Вы могли видеть такой warning:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>warning: capturing 'self' strongly in this
block is likely to lead to a retain cycle
[-Warc-retain-cycles,4]
</code></pre></div></div>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SomeBlockType</span> <span class="n">someBlock</span> <span class="o">=</span> <span class="o">^</span><span class="p">{</span>
<span class="p">[</span><span class="n">self</span> <span class="nf">someMethod</span><span class="p">];</span>
<span class="p">};</span>
</code></pre></div></div>
<p>Идея здесь такая. self имеет сильную ссылку на someBlock, someBlock “захватывает” и держит сильную ссылку на self. В следующем примере мы имеем дело с тем же retain циклом, но он уже менее очевиден:</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// блок захватит self сильной ссылкой
</span><span class="n">SomeBlockType</span> <span class="n">someBlock</span> <span class="o">=</span> <span class="o">^</span><span class="p">{</span>
<span class="n">BOOL</span> <span class="n">isDone</span> <span class="o">=</span> <span class="n">_isDone</span><span class="p">;</span> <span class="c1">// _isDone - это ivar в self
</span><span class="p">};</span>
</code></pre></div></div>
<p>Более безопасно (правда малость многословно) это можно сделать с использованием weakSelf:</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">__weak</span> <span class="n">SomeObjectClass</span> <span class="o">*</span><span class="n">weakSelf</span> <span class="o">=</span> <span class="n">self</span><span class="p">;</span>
<span class="n">SomeBlockType</span> <span class="n">someBlock</span> <span class="o">=</span> <span class="o">^</span><span class="p">{</span>
<span class="n">SomeObjectClass</span> <span class="o">*</span><span class="n">strongSelf</span> <span class="o">=</span> <span class="n">weakSelf</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">strongSelf</span> <span class="o">==</span> <span class="nb">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// Оригинальный self здесь не существует.
</span> <span class="c1">// Игнорируйте, уведомите о или каким-то еще способ обработайте этот случай.
</span> <span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="p">[</span><span class="n">strongSelf</span> <span class="nf">someMethod</span><span class="p">];</span>
<span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>
<p>Иногда, необходимо отдельно позаботиться о конкретных объектах, чтобы избежать retain-циклов: если someObject будет захвачен сильной ссылок в блоке, который использует someObject, вам потребуется weakSomeObject для решения проблемы.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SomeObjectClass *someObject = ...
__weak SomeObjectClass *weakSomeObject = someObject;
someObject.completionHandler = ^{
SomeObjectClass *strongSomeObject = weakSomeObject;
if (strongSomeObject == nil)
{
// Оригинальный someObject здесь не существует.
// Игнорируйте, уведомите о или каким-то еще способ обработайте этот случай.
}
else
{
// отлично, ТЕПЕРЬ мы можем что-нибудь сделать с someObject
[strongSomeObject someMethod];
}
};
</code></pre></div></div>
<h2 id="Использование-cg-из-ns-или-ui">Использование CG из NS или UI</h2>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">UIColor</span> <span class="o">*</span><span class="n">redColor</span> <span class="o">=</span> <span class="p">[</span><span class="n">UIColor</span> <span class="nf">redColor</span><span class="p">];</span>
<span class="n">CGColorRef</span> <span class="n">redRef</span> <span class="o">=</span> <span class="n">redColor</span><span class="p">.</span><span class="n">CGColor</span><span class="p">;</span>
<span class="c1">// делаем что-нибудь с redRef.
</span></code></pre></div></div>
<p>Этот примерн имеет несколько малозаметных проблем. Когда вы создаете redRef, если redColor более нигде не используется, то он удаляется сразу после комментария.</p>
<p>Проблема здесь в том, что redColor “владеет” redRef, и когда redRef используется, redColor’а уже может не быть. Кроме того, такие проблемы редко проявляют себя в симуляторе. Более вероятно, что эта проблема “выстрелит” где-нибудь на устройстве, у которого осталось мало свободной памяти.</p>
<p>Существует несколько workaround’ов. Обычно, необходимо лишь убедиться, что redColor существует все время, пока вы используете redRef.</p>
<p>Самый простой способ добиться этого - использовать __autoreleasing.</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">UIColor</span> <span class="o">*</span> <span class="n">__autoreleasing</span> <span class="n">redColor</span> <span class="o">=</span> <span class="p">[</span><span class="n">UIColor</span> <span class="nf">redColor</span><span class="p">];</span>
<span class="n">CGColorRef</span> <span class="n">redRef</span> <span class="o">=</span> <span class="n">redColor</span><span class="p">.</span><span class="n">CGColor</span><span class="p">;</span>
</code></pre></div></div>
<p>Теперь, redColor не будет уничтожен до тех пор в будущем, пока метод не вернет управление. Поэтому мы можем спокойно использовать redRef в рамках нашего метода.</p>
<p>Другой путь - получить +1 на redRef:</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">UIColor</span> <span class="o">*</span><span class="n">redColor</span> <span class="o">=</span> <span class="p">[</span><span class="n">UIColor</span> <span class="nf">redColor</span><span class="p">];</span>
<span class="n">CGColorRef</span> <span class="n">redRef</span> <span class="o">=</span> <span class="n">CFRetain</span><span class="p">(</span><span class="n">redColor</span><span class="p">.</span><span class="n">CGColor</span><span class="p">);</span>
<span class="c1">// используем redRef и когда закончим - удаляем его:
</span><span class="n">CFRelease</span><span class="p">(</span><span class="n">redRef</span><span class="p">)</span>
</code></pre></div></div>
<p>Важно, что необходимо вызывать CFRetain() в той же строке, в которой вы получаете redColor.CGColor. redColor будет уничтожен сразу после последнего использования, следующий код работать <strong>НЕ</strong> будет:</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">UIColor</span> <span class="o">*</span><span class="n">redColor</span> <span class="o">=</span> <span class="p">[</span><span class="n">UIColor</span> <span class="nf">redColor</span><span class="p">];</span>
<span class="n">CGColorRef</span> <span class="n">redRef</span> <span class="o">=</span> <span class="n">redColor</span><span class="p">.</span><span class="n">CGColor</span><span class="p">;</span> <span class="c1">// redColor будет удален сразу после этого ...
</span><span class="n">CFRetain</span><span class="p">(</span><span class="n">redRef</span><span class="p">);</span> <span class="c1">// Тут будет крэш ...
</span><span class="p">...</span>
</code></pre></div></div>
<p>Есть еще один интересный момент относительно строки “Тут будет крэш”. Как правило, крэш в этой строке в симуляторе не случится, но обязательно произойдет на реальном устройстве.</p>
<h2 id="singletons">Singletons</h2>
<p>Связаны с ARC только косвенно. Существует огромное количество реализаций singleton’ов “на коленке” (некоторые из них даже переопределяют retain и release).</p>
<p>Вот правильная реализация singleton’а которую стоит использовать:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+ (MyClass *)singleton
{
static MyClass *sharedMyClass = nil;
static dispatch_once_t once = 0;
dispatch_once(&once, ^{
sharedMyClass = [[self alloc] init];
});
return sharedMyClass;
}
</code></pre></div></div>
<p>Теперь нам необходимо иметь возможность уничтожить такой singleton. Кроме того, если это UnitTest’ы, то лучше не использовать singleton’ы.</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// определяем статическую переменную вне метода singleton'а
</span><span class="k">static</span> <span class="n">MyClass</span> <span class="o">*</span><span class="n">__sharedMyClass</span> <span class="o">=</span> <span class="nb">nil</span><span class="p">;</span>
<span class="k">+</span> <span class="p">(</span><span class="n">MyClass</span> <span class="o">*</span><span class="p">)</span><span class="n">singleton</span>
<span class="p">{</span>
<span class="k">static</span> <span class="n">dispatch_once_t</span> <span class="n">once</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">dispatch_once</span><span class="p">(</span><span class="o">&</span><span class="n">once</span><span class="p">,</span> <span class="o">^</span><span class="p">{</span>
<span class="n">__sharedMyClass</span> <span class="o">=</span> <span class="p">[[</span><span class="n">self</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">init</span><span class="p">];</span>
<span class="p">});</span>
<span class="k">return</span> <span class="n">__sharedMyClass</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Только для использования библиотекой тестирования!!!
</span><span class="o">-</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">destroyAndRecreateSingleton</span>
<span class="p">{</span>
<span class="n">__sharedMyClass</span> <span class="o">=</span> <span class="p">[[</span><span class="n">self</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">init</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>
Заметки об IoC и DI2016-11-16T00:00:00+00:00http://mrdekk.ru/2016/11/16/note-about-ioc-and-di<p>IoC - паттерн инверсии управления (Inversion of Control), почитать можно <a href="https://ru.wikipedia.org/wiki/Инверсия_управления">тут</a>
DI - паттерн внедрения зависимостей (Dependency Injection), почитать можно <a href="https://ru.wikipedia.org/wiki/Внедрение_зависимости">тут</a></p>
<p>В последнее время все чаще участвую в разного рода дискуссиях касательно архитектуры приложений, причем не важно каких - web, десктопных или мобильных. Во всех случаях всплывают примерно одни и те же проблемы и примерно одни и те же грабли. Дабы не “мозолить” себе язык и не объяснять свою точку зрения постоянно, решил написать эту статью. Тут рассмотрим некоторые основные подходы и паттерны и затем возможные их применения при разработке приложений.</p>
<p>Но для начала предлагаю рассмотреть небольшой примерчик для понимания того, откуда проблема берется.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">Inbox</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">allLetters</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">Mail</span><span class="p">]</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">EmailInbox</span> <span class="p">:</span> <span class="kt">Inbox</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="kd">class</span> <span class="kt">MailService</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">inbox</span><span class="p">:</span> <span class="kt">Inbox</span> <span class="o">=</span> <span class="kt">EmailInbox</span><span class="p">(</span><span class="s">"null@example.com"</span><span class="p">)</span>
<span class="kd">func</span> <span class="nf">mail</span><span class="p">(</span><span class="n">with</span> <span class="nv">recipient</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-></span> <span class="p">[</span><span class="kt">Mail</span><span class="p">]</span>
<span class="p">{</span>
<span class="k">let</span> <span class="nv">letters</span> <span class="o">=</span> <span class="n">inbox</span><span class="o">.</span><span class="nf">allLetters</span><span class="p">()</span>
<span class="k">return</span> <span class="n">letters</span><span class="o">.</span><span class="n">filter</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">recipient</span> <span class="o">==</span> <span class="n">recipient</span> <span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Примерчик простой, однако даже в нем уже есть проблема - класс MailService крепко-накрепко связан с классом EmailInbox, несмотря даже на то, что мы честно выделили протокол Inbox. Теперь (например, в другом проекте) потребовалось использовать MailService, но при этом Inbox там реализован по другому (например через файлы). Это приведет к тому, что придется код переписывать, не получится просто взять и использовать.</p>
<p>Ситуацию можно немного ухудшить, написав примерно так.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">MailService</span>
<span class="p">{</span>
<span class="kd">func</span> <span class="nf">mail</span><span class="p">(</span><span class="n">with</span> <span class="nv">recipient</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-></span> <span class="p">[</span><span class="kt">Mail</span><span class="p">]</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kt">EmailInbox</span><span class="o">.</span><span class="n">shared</span><span class="o">.</span><span class="nf">allLetters</span><span class="p">()</span><span class="o">.</span><span class="n">filter</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">recipient</span> <span class="o">==</span> <span class="n">recipient</span> <span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Теперь внешнему наблюдателю сразу даже будет непонятно, что внутри класса есть зависимость. Да при детальном анализе кода будет ясно, но если класс не из 10 строк, а из 1000? По этой же причине опасны singleton’ы.</p>
<p>Для обеспечения переносимости кода, для возможности организовать хорошее тестирование (например заmock’ать какие-нибудь зависимости) и т.д. с этим надо что-то делать. И вот тут-то и появляются разного рода умные слова, такие как Dependency Injection, Inversion of Control и иногда Service Locator. Как правило, считается что они значат примерно одно и то же, хотя если разбираться детальнее разница между ними есть и большая.</p>
<p>Самый первый и самый с одной стороны простой термин - Dependency Injection или по-русски “внедрение зависимостей”. Вообще, это комплекс мероприятий в результате которого зависимости оказываются в вашем объекте. Даже наш простой примерчик в общем делает dependency injection в самом простом его виде - зависимость просто создается. Внедрять зависимости можно спрашивая их у кого-то (см. Service Locator) или кто-то их нам установит автоматически (см. Inversion of Control).</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">mailService</span> <span class="o">=</span> <span class="kt">MailService</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">inbox</span> <span class="o">=</span> <span class="kt">EmailInbox</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="n">mailServie</span><span class="o">.</span><span class="n">inbox</span> <span class="o">=</span> <span class="n">inbox</span> <span class="c1">// dependency injection</span>
</code></pre></div></div>
<p>Service Locator - это специальный объект (или фасад) у которого вы всегда сможете спросить нужную зависимость. Как правило, он либо знает где ее взять (на то он и locator), либо он сам хранит их у себя (см. паттерн реестр). Когда кому-то нужна какая-то зависимость, этот кто-то через механизмы singleton’а (или еще как) идет к Service Locator’у и спрашивает нужную зависимость.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">MailService</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">inbox</span><span class="p">:</span> <span class="kt">Inbox</span> <span class="o">=</span> <span class="kt">ServiceLocator</span><span class="o">.</span><span class="n">shared</span><span class="o">.</span><span class="n">inbox</span> <span class="c1">// service locator</span>
<span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Третий термин (Inversion of Control) рассмотрим подробнее.</p>
<p>Но сначала давайте разберемся с тем, что за “control” инвертируется. Когда-то давно, когда приложения были консольными, control-flow приложения выглядел примерно так: мы запрашиваем с устройства ввода у пользователя данные, обрабатываем их и выводим на устройство вывода. Потоком управления управляло (пардон за тавталогию) само приложение. Когда появились графические UI приложение вместо того, чтобы управлять циклом приложения стало обрабатывать событие, отдав управление операционной системе. Таким образом получилась инверсия control-flow приложения. Таким же образом обстоит дело с инверсией при внедрении зависимостей при использовании паттерна Inversion of Control - управление временем жизни объекта и внедрением зависимостей делегируется специальному объекту, в задачу которого входит создание всех необходимых объектов и установка ссылок между ними. Этот специальный объект может называться сборщиком (assembler), контейнером (inversion of control container, см. Spring Framework) или еще как-нибудь, сути его это не меняеет.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">IoCContainer</span> <span class="c1">// Inversion of Control container (assembler)</span>
<span class="p">{</span>
<span class="k">let</span> <span class="nv">mailService</span><span class="p">:</span> <span class="kt">MailService</span><span class="o">!</span>
<span class="k">let</span> <span class="nv">inbox</span><span class="p">:</span> <span class="kt">Inbox</span>
<span class="kd">func</span> <span class="nf">build</span><span class="p">()</span>
<span class="p">{</span>
<span class="nf">create</span><span class="p">()</span>
<span class="nf">internlink</span><span class="p">()</span>
<span class="nf">post</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">create</span><span class="p">()</span> <span class="p">{</span>
<span class="n">mailService</span> <span class="o">=</span> <span class="kt">MailService</span><span class="p">()</span>
<span class="n">inbox</span> <span class="o">=</span> <span class="kt">EmailInbox</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">interlink</span><span class="p">()</span> <span class="p">{</span>
<span class="n">mailService</span><span class="o">.</span><span class="n">inbox</span> <span class="o">=</span> <span class="n">inbox</span>
<span class="p">}</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">post</span><span class="p">()</span> <span class="p">{</span>
<span class="n">mailService</span><span class="o">.</span><span class="nf">postCreate</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Главное отличие контейнера от сервис локатора в том, что контейнер конкретный класс как правило не видит - зависимости для него появляются автомагически (automagically), а service locator видит и даже может быть хранит на него ссылку, а если еще и сильную… Кроме того, при использовании контейнера вы можете например иметь циклическую ссылку (с учетом конечно всех требований ARC и weak), или выполнить что-то после того, как весь граф (не дерево! и это важно) зависимостей будет собран (см. IoCContainer.post)</p>
TODO в swift в Xcode2016-11-09T00:00:00+00:00http://mrdekk.ru/2016/11/09/todos-in-swift-xcode<p>Очень часто в процессе разработки бывает, когда вместо некоторого кода пишется временная заглушка или некий временный код, или даже просто участок кода к которому необходимо вернуться чуть позже, например:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// TODO: we need to convert to float math for better precision</span>
<span class="k">let</span> <span class="nv">sum</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span>
<span class="k">let</span> <span class="nv">avg</span> <span class="o">=</span> <span class="n">sum</span> <span class="o">/</span> <span class="mi">2</span>
</code></pre></div></div>
<p>И хотелось бы, чтобы Xcode каким-либо образом это подсвечивал, например как это было в старом добром Objective-C</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#warning we need to convert to float math for better precision
</span><span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">2</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">avg</span> <span class="o">=</span> <span class="n">sum</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
</code></pre></div></div>
<p>Но в Xcode “из коробки” увы нет такой поддержки для Swift. Быть может она появится в будущем, сейчас же предлагаю применить черную магию и написать скрипт. Для этого идем в Build Phases и создаем “Run Script”, поместить его можно в конце. Содержание скрипта такое:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">TAGS</span><span class="o">=</span><span class="s2">"TODO:|FIXME:"</span>
find <span class="s2">"</span><span class="k">${</span><span class="nv">SRCROOT</span><span class="k">}</span><span class="s2">"</span> <span class="se">\(</span> <span class="nt">-name</span> <span class="s2">"*.h"</span> <span class="nt">-or</span> <span class="nt">-name</span> <span class="s2">"*.m"</span> <span class="nt">-or</span> <span class="nt">-name</span> <span class="s2">"*.swift"</span> <span class="se">\)</span> <span class="nt">-print0</span> | xargs <span class="nt">-0</span> egrep <span class="nt">--with-filename</span> <span class="nt">--line-number</span> <span class="nt">--only-matching</span> <span class="s2">"(</span><span class="nv">$TAGS</span><span class="s2">).*</span><span class="se">\$</span><span class="s2">"</span> | perl <span class="nt">-p</span> <span class="nt">-e</span> <span class="s2">"s/(</span><span class="nv">$TAGS</span><span class="s2">)/ warning: </span><span class="se">\$</span><span class="s2">1/"</span>
</code></pre></div></div>
<p>Xcode после сборки начнет подсвечивать ваши TODO’шки так</p>
<p><img src="/media/images/xcode_todos_1.png" alt="highlight todos" /></p>
<p>Если вы хотите подсвечивать еще например // ERROR: как ошибки, можно скрипт сделать таким</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">TAGS</span><span class="o">=</span><span class="s2">"TODO:|FIXME:"</span>
<span class="nv">ERRORTAG</span><span class="o">=</span><span class="s2">"ERROR:"</span>
find <span class="s2">"</span><span class="k">${</span><span class="nv">SRCROOT</span><span class="k">}</span><span class="s2">"</span> <span class="se">\(</span> <span class="nt">-name</span> <span class="s2">"*.h"</span> <span class="nt">-or</span> <span class="nt">-name</span> <span class="s2">"*.m"</span> <span class="nt">-or</span> <span class="nt">-name</span> <span class="s2">"*.swift"</span> <span class="se">\)</span> <span class="nt">-print0</span> | xargs <span class="nt">-0</span> egrep <span class="nt">--with-filename</span> <span class="nt">--line-number</span> <span class="nt">--only-matching</span> <span class="s2">"(</span><span class="nv">$TAGS</span><span class="s2">).*</span><span class="se">\$</span><span class="s2">|(</span><span class="nv">$ERRORTAG</span><span class="s2">).*</span><span class="se">\$</span><span class="s2">"</span> | perl <span class="nt">-p</span> <span class="nt">-e</span> <span class="s2">"s/(</span><span class="nv">$TAGS</span><span class="s2">)/ warning: </span><span class="se">\$</span><span class="s2">1/"</span> | perl <span class="nt">-p</span> <span class="nt">-e</span> <span class="s2">"s/(</span><span class="nv">$ERRORTAG</span><span class="s2">)/ error: </span><span class="se">\$</span><span class="s2">1/"</span>
</code></pre></div></div>
<p>Xcode сделает так</p>
<p><img src="/media/images/xcode_todos_2.png" alt="highlight errors" /></p>
<p>Придумал не сам, нашел <a href="http://krakendev.io/blog/generating-warnings-in-xcode">тут</a> и перевел.</p>
Yandex Mobile Contest2016-10-05T00:00:00+00:00http://mrdekk.ru/2016/10/05/yandex-mobile-contest<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">import</span> <span class="kt">Foundation</span>
<span class="k">let</span> <span class="nv">ymc</span> <span class="o">=</span> <span class="kt">Event</span><span class="p">(</span><span class="s">"Yandex Mobile Contest"</span><span class="p">,</span> <span class="nv">at</span><span class="p">:</span><span class="s">"2016-10-17"</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">attendee</span> <span class="o">=</span> <span class="kt">AnyPerson</span><span class="p">()</span>
<span class="k">if</span> <span class="n">attendee</span><span class="o">.</span><span class="nf">haveInterest</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="s">"Мобильная разработка"</span><span class="p">)</span> <span class="p">{</span>
<span class="n">attendee</span><span class="o">.</span><span class="nf">takePart</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="n">ymc</span><span class="p">)</span> <span class="o">&&</span> <span class="n">attendee</span><span class="o">.</span><span class="nf">winPrize</span><span class="p">(</span><span class="nv">of</span><span class="p">:</span> <span class="n">ymc</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Проводим в Яндексе соревнование мобильных разработчиков, приглашаем всех желающих.
<a href="https://yandex.ru/ymc?utm_source=mrdekk_blog">Регистрируйтесь тут</a></p>
Почему не надо использовать React Native2016-10-01T00:00:00+00:00http://mrdekk.ru/2016/10/01/why-not-use-react-native<p>Хорошая статья почему не надо использовать React Native, правда на английском.</p>
<p>https://arielelkin.github.io/articles/why-im-not-a-react-native-developer</p>
GIT cookbook2016-09-27T00:00:00+00:00http://mrdekk.ru/2016/09/27/git-cookbook<p>Разное полезное для гита</p>
<h2 id="Забрать-к-себе-все-ветки-с-remote">Забрать к себе все ветки с remote</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git branch <span class="nt">-r</span> | <span class="nb">grep</span> <span class="nt">-v</span> <span class="s1">'\->'</span> | <span class="k">while </span><span class="nb">read </span>remote<span class="p">;</span> <span class="k">do </span>git branch <span class="nt">--track</span> <span class="s2">"</span><span class="k">${</span><span class="nv">remote</span><span class="p">#origin/</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$remote</span><span class="s2">"</span><span class="p">;</span> <span class="k">done
</span>git fetch <span class="nt">--all</span>
git pull <span class="nt">--all</span>
</code></pre></div></div>
<h2 id="Отправить-все-локальные-бранчи-в-новый-remote">Отправить все локальные бранчи в новый remote</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git push REMOTE <span class="nt">--all</span>
<span class="c"># or git push REMOTE '*:*'</span>
git push REMOTE <span class="nt">--tags</span>
</code></pre></div></div>
<h2 id="Поудалять-все-теги-по-маске">Поудалять все теги по маске</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git tag <span class="nt">-l</span> | <span class="nb">grep </span>_clog | <span class="k">while </span><span class="nb">read </span>remote<span class="p">;</span> <span class="k">do </span>git tag <span class="nt">-d</span> <span class="nv">$remote</span><span class="p">;</span> git push origin :refs/tags/<span class="nv">$remote</span><span class="p">;</span> <span class="k">done</span>
</code></pre></div></div>
Как отлаживать приложение для iOS 10 в xCode 7.3.12016-09-05T00:00:00+00:00http://mrdekk.ru/2016/09/05/run-on-ios10-device-from-xcode7<p>Случилась такая задача - отладить приложение собираемое с iOS 9 SDK на телефоне с iOS 10. Для этого собирать его надо естественно в xCode 7, но xCode 7 не видит устройства с iOS 10. Проблема относительно просто решается вот такой вот магией:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ln <span class="nt">-s</span> /Applications/Xcode8beta4.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/10.0<span class="se">\ \(</span>14A5322e<span class="se">\)</span> /Applications/Xcode7.3.1.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/
</code></pre></div></div>
<p>После этого можно отлаживаться в обычном режиме.</p>
<p>Аналогичным образом можно подцепить к Xcode 8 девайс с iOS 7.1 например</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ln <span class="nt">-s</span> /Applications/Xcode7.3.1.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/7.1 /Applications/Xcode8GM.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport
</code></pre></div></div>
<p>Но это уже на ваш страх и риск - никто работоспособность такого хака не гарантирует</p>
Poor Man's package management2016-09-04T00:00:00+00:00http://mrdekk.ru/2016/09/04/poor-mans-package-management<p>Вы наверняка пользовались пакетными менеджерами а-ля CocoaPods и Carthage. Однако иногда возникают ситуации когда вы хотите подцепить себе в проект сторонний проект, но при этом по какой-то причине не хотите пользоваться этими менеджерами пакетов. В таких случаях может пригодиться техника, которую я ласково называю “Poor Man’s package management” (пакетный менеджер для бедных, уж простят меня оные).</p>
<p>Суть техники в следующем - зависимости мы цепляем непосредственно как часть нашего проекта с помощью git submodules. Да, техника работает для пользователей git (хотя лично я знаю как ее применить и для SVN, думается, что уж для популярных DCVS систем такую технику уж точно придумать можно).</p>
<p>Для того, чтобы репозиторий зависимости стал “подпапкой” вашего проекта достаточно сделать так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git submodule add http://github.com/cooluser/coolproject ./deps/coolproject
</code></pre></div></div>
<p>и далее ручками через xcode добавляем нужные файлы к себе в проект. Не забываем закоммитить .gitmodules себе в проект, это нужно чтобы ваши коллеги могли себе собрать тоже и также.</p>
<p>Чтобы другой человек (ваш коллега, например) получил полный экземпляр вашей работы кроме обычных действий при работе с подмодулями ему надо сделать</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git submodule init
git submodule update
</code></pre></div></div>
Проверяем версию iOS2016-08-10T00:00:00+00:00http://mrdekk.ru/2016/08/10/objc-check-ios-version<p>Как проверить версию iOS? Очень просто:</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">NSString</span><span class="o">*</span> <span class="n">requiredVersion</span> <span class="o">=</span> <span class="s">@"9.3.1"</span><span class="p">;</span>
<span class="n">NSString</span><span class="o">*</span> <span class="n">currentSystemVersion</span> <span class="o">=</span> <span class="p">[[</span><span class="n">UIDevice</span> <span class="nf">currentDevice</span><span class="p">]</span> <span class="nf">systemVersion</span><span class="p">];</span>
<span class="k">if</span> <span class="p">([</span><span class="n">currSystemVersion</span> <span class="nf">compare</span><span class="p">:</span><span class="n">requiredVersion</span> <span class="nf">options</span><span class="p">:</span><span class="n">NSNumericSearch</span><span class="p">]</span> <span class="o">!=</span> <span class="n">NSOrderedAscending</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// code that is suitable for iOS >= 9.3.1
</span><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// code for versions older that 9.3.1
</span><span class="p">}</span>
</code></pre></div></div>
<p>В Swift’е это сделать несколько проще:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="kd">#available(iOS 9.3.1, *)</span> <span class="p">{</span>
<span class="c1">// code that is suitable for iOS >= 9.3.1</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// code for versions older than 9.3.1</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Важное отступление. Наверняка возникает вопрос, почему нельзя сделать так:</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="n">UIDevice</span><span class="p">.</span><span class="n">currentDevice</span><span class="p">.</span><span class="n">systemVersion</span><span class="p">.</span><span class="n">floatValue</span> <span class="o">>=</span> <span class="mi">9</span><span class="p">.</span><span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// do something for iOS greater or equal 9.3
</span><span class="p">}</span>
</code></pre></div></div>
<p>Отвечаю. Пункт первый - а что если вам надо >= 9.3.1 именно с 1 обновления, а не для всей 9.3? Float сравнение уже не позволит написать такое условие, в то же время приведенный мной код строкового сравнения позволяет сделать это. Пункт второй, более важный - сравнение чисел с плавающей точкой. Пример, для вот такого кода</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">NSString</span><span class="o">*</span> <span class="n">systemVersion</span> <span class="o">=</span> <span class="s">@"9.3.2"</span><span class="p">;</span>
<span class="kt">float</span> <span class="n">sysVer</span> <span class="o">=</span> <span class="n">systemVersion</span><span class="p">.</span><span class="n">floatValue</span><span class="p">;</span>
</code></pre></div></div>
<p>в sysVer будет 9.30000019 и простое сравнение sysVer == 9.3 не сработает. В таком случае надо бы делать fabs(sysVer - 9.3) <= 0.01 что уже как проигрывает по выразительности, так и ставит вопросы - а каков эпсилон версий операционки?</p>
<p>Со swift’ом в общем таких проблем нет.</p>
Собираем свою версию bootstrap'а с кастомизацией2016-08-05T00:00:00+00:00http://mrdekk.ru/2016/08/05/customize-bootstrap<p>В этой статье опишу рецепт, как сделать свою кастомизацию базовых стилей фреймворка twitter bootstrap. Для начала нам понадобяться собственно исходники бутстрапа:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/twbs/bootstrap.git
</code></pre></div></div>
<p>И опционально переключаемся на нужный тэг:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout tags/v3.3.7
</code></pre></div></div>
<p>Далее нам потребуется LESS для сборки css файлов bootstrap’а</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>npm i less <span class="nt">-g</span>
</code></pre></div></div>
<p>теперь попытаемся собрать CSS:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lessc bootstrap/less/bootstrap.less <span class="o">></span> boostrap.css
</code></pre></div></div>
<p>Или минифицированную версию</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lessc <span class="nt">--compress</span> bootstrap/less/bootstrap.less <span class="o">></span> boostrap.css
</code></pre></div></div>
<p>Теперь для создания своей конфигурации boostrap вы можете править файл bootstrap.less или его зависимости. Однако здесь таится одна небольшая проблема. Мы выше забрали исходники через гит. При выходе очередной версии вы наверняка захотите обновиться. Но в этом вам будут мешать сделанные изменения, которые надо будет куда-то деть. Да, конечно можно форкнуть репозиторий, сделать свою ветку и потом просто rebase’иться. Однако есть вариант лучше. Наши измения мы просто вынесем в отдельную папку, и они просто будут ссылаться на исходники boostrap’а. В таком случае обновление будет сводиться просто к <code class="highlighter-rouge">git checkout tags/...</code> и изредка правкой вашей кастомизации.</p>
<p>Чтож, приступим.</p>
<ol>
<li>
<p>Создаем папку</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir customization
</code></pre></div> </div>
</li>
<li>
<p>Создаем свои файлы в которые будем помещать кастомизацию</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cp bootstrap/less/variables.less customization/custom-variables.less
<span class="nb">echo</span> <span class="s2">""</span> <span class="o">></span> customization/custom-other.less
</code></pre></div> </div>
</li>
<li>
<p>Далее создаем наш кастомизированный центральный файл customization/custom-boostrap.less со следующим содержимым:</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@import</span> <span class="s1">"../bootstrap/less/bootstrap.less"</span><span class="p">;</span>
<span class="k">@import</span> <span class="s1">"custom-variables.less"</span><span class="p">;</span>
<span class="k">@import</span> <span class="s1">"custom-other.less"</span><span class="p">;</span>
<span class="k">@import</span> <span class="s1">"../bootstrap/less/utilities.less"</span><span class="p">;</span>
</code></pre></div> </div>
</li>
<li>
<p>Сделаем пробную сборку кастомизированного (на самом деле пока еще нет) bootstrap’а</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lessc customization/custom-bootstrap.less <span class="o">></span> bootstrap.css
</code></pre></div> </div>
</li>
</ol>
<p>После этого можно править custom-variables.less, перегенрировать и радоваться. Однако надо где-то смотреть. Есть вот такой хороший проект где все компоненты на одной странице https://github.com/mrdekk/bootstrap-tldr</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/mrdekk/bootstrap-tldr
npm install
<span class="nb">sudo </span>npm i bower <span class="nt">-g</span>
bower install
<span class="nb">sudo </span>npm i grunt <span class="nt">-g</span>
lessc customization/custom-bootstrap.less bootstrap-tldr/app/styles/bootstrap.css
grunt serve
</code></pre></div></div>
<p>После этого http://localhost:9000 вы сможете лицезреть страничку со всеми элементами bootstrap</p>
Крик души о мапперах API в модель2016-08-01T00:00:00+00:00http://mrdekk.ru/2016/08/01/stop-doing-mappers<p>В последнее время очень часто приходится сталкиваться вот с каким вопросом. Мобильные приложения должны работать с бэкендом. Как правило, модель данных API бэкенда не совсем сходится с моделью данных на самом приложении, и даже если сходится 1 в 1, библиотеки сетевых запросов возвращают объекты класса словарь (как бы он не назывался - Map<?, ?>, NSDictionary, …) и возникает необходимость конвертации вида</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">map</span><span class="p">(</span><span class="nv">json</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">])</span> <span class="o">-></span> <span class="kt">SomethingModel</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">model</span> <span class="o">=</span> <span class="kt">SomethingModel</span><span class="p">()</span>
<span class="n">model</span><span class="o">.</span><span class="n">some</span> <span class="o">=</span> <span class="n">json</span><span class="p">[</span><span class="s">"some"</span><span class="p">]</span>
<span class="n">model</span><span class="o">.</span><span class="n">another</span> <span class="o">=</span> <span class="n">json</span><span class="p">[</span><span class="s">"another"</span><span class="p">]</span>
<span class="n">model</span><span class="o">.</span><span class="n">rest</span> <span class="o">=</span> <span class="n">json</span><span class="p">[</span><span class="s">"rest"</span><span class="p">]</span>
<span class="o">...</span>
<span class="k">return</span> <span class="n">model</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">reverse</span><span class="p">(</span><span class="nv">obj</span><span class="p">:</span> <span class="kt">SomethingModel</span><span class="p">)</span> <span class="o">-></span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">res</span> <span class="o">=</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]()</span>
<span class="n">json</span><span class="p">[</span><span class="s">"some"</span><span class="p">]</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">some</span>
<span class="n">json</span><span class="p">[</span><span class="s">"another"</span><span class="p">]</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">another</span>
<span class="n">json</span><span class="p">[</span><span class="s">"rest"</span><span class="p">]</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">rest</span>
<span class="o">...</span>
<span class="k">return</span> <span class="n">model</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Как видно, это большей частью простые императивные преобразования. Иногда конечно возникает необходимость дополнительной проверки (например на nil) или кастомной конвертации (NSDate -> String). Однако для этого можно написать простые хелперы и код будет выглядить как-нибудь так:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model</span><span class="o">.</span><span class="n">nillable</span> <span class="o">=</span> <span class="kt">Conv</span><span class="o">.</span><span class="nf">nillable</span><span class="p">(</span><span class="n">json</span><span class="p">,</span> <span class="s">"nillable"</span><span class="p">)</span>
<span class="n">model</span><span class="o">.</span><span class="n">date</span> <span class="o">=</span> <span class="kt">Conv</span><span class="o">.</span><span class="nf">date</span><span class="p">(</span><span class="n">json</span><span class="p">,</span> <span class="s">"date"</span><span class="p">)</span>
</code></pre></div></div>
<p>Что же есть на практике. А на практике каждый уважающий себя разработчик стремится для <code class="highlighter-rouge">model.some = json["some"]</code> написать кастомную обертку, и что особенно важно, декларативно. То есть вместо упомянутой строчки мы получаем нечто вроде:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kd">class</span> <span class="kt">SomethingModel</span> <span class="p">{</span>
<span class="kd">static</span> <span class="kd">func</span> <span class="nf">jsonMap</span><span class="p">()</span> <span class="o">-></span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">res</span> <span class="o">=</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]()</span>
<span class="n">res</span><span class="p">[</span><span class="s">"some"</span><span class="p">]</span> <span class="o">=</span> <span class="kt">StringMapping</span><span class="p">(</span><span class="nv">path</span><span class="p">:</span> <span class="s">"some"</span><span class="p">)</span>
<span class="n">res</span><span class="p">[</span><span class="s">"another"</span><span class="p">]</span> <span class="o">=</span> <span class="kt">NumberMapping</span><span class="p">(</span><span class="nv">path</span><span class="p">:</span> <span class="s">"another"</span><span class="p">,</span> <span class="k">default</span><span class="p">:</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">res</span><span class="p">[</span><span class="s">"rest"</span><span class="p">]</span> <span class="o">=</span> <span class="kt">DateMapping</span><span class="p">(</span><span class="nv">path</span><span class="p">:</span> <span class="s">"rest"</span><span class="p">,</span> <span class="kt">NSDate</span><span class="p">())</span>
<span class="o">...</span>
<span class="k">return</span> <span class="n">res</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="kt">DeclarativeMapper</span><span class="o"><</span><span class="kt">ModelClass</span><span class="o">></span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">fromJson</span><span class="p">(</span><span class="nv">json</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">])</span> <span class="o">-></span> <span class="kt">ModelClass</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">model</span> <span class="o">=</span> <span class="kt">ModelClass</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">mapping</span> <span class="o">=</span> <span class="kt">ModelClass</span><span class="o">.</span><span class="nf">jsonMap</span><span class="p">()</span>
<span class="k">for</span> <span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="n">mapper</span><span class="p">)</span> <span class="k">in</span> <span class="n">mapping</span><span class="o">.</span><span class="nf">enumerate</span><span class="p">()</span> <span class="p">{</span>
<span class="n">model</span><span class="o">.</span><span class="nf">setValue</span><span class="p">(</span><span class="nv">field</span><span class="p">:</span> <span class="n">field</span><span class="p">,</span> <span class="n">mapper</span><span class="o">.</span><span class="nf">map</span><span class="p">(</span><span class="n">json</span><span class="p">,</span> <span class="n">mapper</span><span class="o">.</span><span class="n">path</span><span class="p">))</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">model</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">let</span> <span class="nv">json</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]</span> <span class="o">=</span> <span class="o">...</span>
<span class="k">let</span> <span class="nv">declarativeMapper</span> <span class="o">=</span> <span class="kt">DeclarativeMapper</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">model</span> <span class="o">=</span> <span class="n">declarativeMapper</span><span class="o">.</span><span class="nf">fromJson</span><span class="p">(</span><span class="n">json</span><span class="p">)</span>
</code></pre></div></div>
<p>У меня есть главный вопрос - а зачем все это делается? Ради декларативности - императивный код проще и сосредоточен в одном месте - в коде запроса, в точке наиболее близкой к точке изменения контракта. Декларативный код размазывает маппинги по нескольким местам и вносит дополнительный код (мапперы), которые могут сломаться. Кроме того, декларативный фреймворки делают некоторые предположения относительно того, как работает бэкенд и если вдруг бэкенд работает не совсем так - у фреймворка возникают проблемы.</p>
<p>Может ли кто-нибудь привести примеры, в которой вся эта декларактивная машинерия оправдана?</p>
Конжак 20162016-07-03T00:00:00+00:00http://mrdekk.ru/2016/07/03/konzhak-2016<p>Решили мы тут попробовать свои силы и таки взобраться на Конжак. “Конжаком” в народе называют гору Конжаковский камень - самую высокую гору Свердловской области (не путать с Уралом, на Серверном Урале есть <a href="https://ru.wikipedia.org/wiki/%D0%9D%D0%B0%D1%80%D0%BE%D0%B4%D0%BD%D0%B0%D1%8F_(%D0%B3%D0%BE%D1%80%D0%B0)">горы</a> и повыше). Каждый год здесь проводится горный марафон “Конжак”, где участникам предлагается на время бегом взобраться на вершину, там отметиться и вернуться на старт. Ну а раз проводиться марафон, то по маршруту марафона организованы контрольные пункты, и на трассе присутствует огромное количество людей - не так страшно в безлюдных горах. Марафон, как правило, проводят с 10 часов (дают старт забегу) и до 8 вечера. Вообще на трассе находятся два вида участников: “спортсмены” - те, кто официально участвует в марафоне, и “туристы” - те, <del>кто путаются под ногами</del> забираются в гору самостоятельно и не участвуют в марафоне.</p>
<p>Итак, как <a href="https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BD%D0%B6%D0%B0%D0%BA%D0%BE%D0%B2%D1%81%D0%BA%D0%B8%D0%B9_%D0%9A%D0%B0%D0%BC%D0%B5%D0%BD%D1%8C">сообщает нам википедия</a>:</p>
<blockquote>
<p>Конжаковский Камень — гора в южной части Северного Урала, на территории Свердловской области (Россия). Одна из высочайших вершин Уральских гор (1569 м). Сложена пироксенитами, дунитами и габбро. В нижней части склоны покрыты хвойными лесами, а с высоты 900—1000 м — горной тундрой и каменными россыпями[1]. Названа по имени охотника-вогула Конжакова, юрта которого некогда стояла у основания горы[2].</p>
</blockquote>
<iframe src="https://api-maps.yandex.ru/frame/v1/-/CVXW56JA" width="100%" height="400" frameborder="0"></iframe>
<p>Вдоль склонов горы проложена трасса марафона, на 4 километре которой есть даже вот такая его карта</p>
<p><img src="/media/images/konzhak2016/01-maraphon-map.jpg" alt="карта марафона" /></p>
<p>Рассчитывая свои силы мы решили пойти пораньше, так как мы совсем не марафонцы. Начало не предвещало того, что потом открылось нам:</p>
<p><img src="/media/images/konzhak2016/02-beginning.jpg" alt="это только начало" /></p>
<p>По дороге нам встретился вполне горный горный перевал, а потом дорога пошла вниз (мы удивились - в гору же идем) и привела нас к плещущейся горной реке и наведенной через нее переправе:</p>
<p><img src="/media/images/konzhak2016/03-mount-river.jpg" alt="горная речка - деревянный мост" /></p>
<p>Чуть выше по маршруту на нее можно посмотреть с высоты:</p>
<p><img src="/media/images/konzhak2016/04-mount-river-2.jpg" alt="горная речка - смотровая площадка" /></p>
<p>Дальше начался подъем вверх, сначала пологий, но потихоньку он становился все круче и круче. После небольшого подъема (примерно 2 км) встречается КП “Родник”, где “спортсменов” кормят бутербродами и поют чаем, а “туристы” могут пополнить запасы воды в роднике. Вода, кстати, вкусная. Далее после небольшого горизонтального участка трасса круто забирает вверх. На этом подъеме нас догнали первые “спортсмены”, которые в отличии от нас стартовали не в 8, а в 10. Далее, после еще нескольких километров крутого подъема, встречается второй контрольный пункт - “Поляна Художников”. Это отметка 14 км от начала трассы и, как правило, многие “туристы” заканчивают здесь свое восхождение (высота около 1000 м над уровнем моря). Но мы хотели взобраться совсем наверх, поэтому пошли дальше.</p>
<p><img src="/media/images/konzhak2016/05-artist-valley.jpg" alt="поляна художников" />
(спойлер - обратите внимание на муху в центре кадра)</p>
<p>С этой поляны открываются впечатляющие виды</p>
<p><img src="/media/images/konzhak2016/06-artist-v-pano.jpg" alt="панорама с поляны художников" /></p>
<p>Следующим впечатлящим пунктом стал ледник, который мы запреметили еще издалека</p>
<p><img src="/media/images/konzhak2016/07-ice.jpg" alt="ледник" /></p>
<p>Ледник уже сам по себе находится достаточно близко к первым вершинам (еще не сам Конжак):</p>
<p><img src="/media/images/konzhak2016/08-ice2.jpg" alt="ледник 2" /></p>
<p>А вот с него уже деревья не мешают обзору, и вид вообще шикарный:</p>
<p><img src="/media/images/konzhak2016/09-ice-pano.jpg" alt="виды с ледника" /></p>
<p>Дальше, после длительного карабканья по камням, мы попадаем на Иовское плато, где настолько высоко, что уже немного тяжело дышать, и ветер дует постоянно. А еще здесь как на болоте - почва утопает под ногами, и пройти в кросовках, не промочив ноги, практически невозможно.</p>
<p><img src="/media/images/konzhak2016/10-iov-plato.jpg" alt="Иовское плато" /></p>
<p>Здесь начинаются обещанные альпийские луга (где, кстати, очень много растений из Красной книги)</p>
<p><img src="/media/images/konzhak2016/11-alps-grass.jpg" alt="Альпийские луга" /></p>
<p>Виды с Иовского плато</p>
<p><img src="/media/images/konzhak2016/12-iov-plato2.jpg" alt="Виды с Иовского плато" /></p>
<p>Здесь, посмотрев на почти отвесный подъем по камням на саму вершину:</p>
<p><img src="/media/images/konzhak2016/13-oops.jpg" alt="Упс" /></p>
<p>Мы решили на этом закруглиться. Итого 1246 метров из 1569, не дошли чуть больше 200 метров в высоту и 2,5 км по трассе в длину.</p>
<p><img src="/media/images/konzhak2016/14-proof.jpg" alt="Пруф" />
<img src="/media/images/konzhak2016/15-proof2.jpg" alt="Пруф 2" /></p>
<p>Вид с конечной точки нашего восхождения</p>
<p><img src="/media/images/konzhak2016/16-last-view.jpg" alt="Конечная точка" /></p>
<p>“Дохлости”</p>
<p><img src="/media/images/konzhak2016/17-deads.jpg" alt="Дохлости" /></p>
<p>Далее мы пошли вниз. В итоге - 39,46 километров мы прошли (марафон был 42) сделав 36 тысяч шагов.</p>
<p><img src="/media/images/konzhak2016/18-proof3.jpg" alt="Пруф 3" /></p>
Полезные флаги xCode2016-06-07T00:00:00+00:00http://mrdekk.ru/2016/06/07/usefull-xcode-flags<p>Некоторые полезные в работе флаги</p>
<ul>
<li><code class="highlighter-rouge">-UIViewShowAlignmentRects YES</code> - показывает желтыми линиями frame rect’ы, которые удобно использовать для отладки</li>
<li><code class="highlighter-rouge">-com.apple.CoreData.ConcurrencyDebug 1</code> - “стреляет” assert’ами, когда NSManagedObjectContext или другие примитивы CoreData используются в неправильном потоке</li>
<li><code class="highlighter-rouge">-com.apple.CoreData.SQLDebug 1</code> - пишет в лог все sqlite запросы, которые делает CoreData в процессе своей работы</li>
<li><code class="highlighter-rouge">-Name:OS_ACTIVITY_MODE disable</code> - отключает вывод системного лога в debug окно Xcode 8</li>
<li><code class="highlighter-rouge">DYLD_PRINT_STATISTICS 1</code> - выводит данные о загрузке внешних библиотек (и не только)</li>
<li><code class="highlighter-rouge">SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT 1-3</code> - выводит лог использования @objc inference</li>
</ul>
<p>Некоторые полезные флаги компилятора</p>
<ul>
<li><code class="highlighter-rouge">-Wglobal-constructors</code> - показывает использование нетривиальных конструкторов С++ при инициализации глобальных объектов</li>
</ul>
<p>Разные полезные штуки при работе с LLDB</p>
<ul>
<li>
<p>Показать историю аллокаций по адресу в памяти, надо в консоли отладчика сделать так</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> (lldb) command script import lldb.macosx.heap
(lldb) malloc_info --stack-history 0x10010d680
</code></pre></div> </div>
</li>
</ul>
GitHub - синхронизируем forked репозиторий с upstream2016-05-23T00:00:00+00:00http://mrdekk.ru/2016/05/23/github-sync-forked-repo-with-upstream<p>Задача: синхронизировать форкнутый репозиторий с тем, от которого он был форкнут и забрать оттуда новые изменения.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="c"># Добавляем новый удаленный репозиторий</span>
<span class="nv">$ </span>git remote add upstream https://github.com/whoever/whatever.git
<span class="c"># Загружаем все ветки для отслеживания</span>
<span class="nv">$ </span>git fetch upstream
<span class="c"># Убеждаемся, что мы на своем master'е</span>
<span class="nv">$ </span>git checkout master
<span class="c"># Перезаписываем нашу ветку master чтобы те наши коммиты, которые не входят в удаленный мастер</span>
<span class="c"># оказали на нем:</span>
<span class="nv">$ </span>git rebase upstream/master
<span class="c"># Force push необходим для перезаписи истории ветки на вашем репозитории</span>
<span class="nv">$ </span>git push <span class="nt">-f</span> origin master
</code></pre></div></div>
Поваренная книга GCD2016-05-19T00:00:00+00:00http://mrdekk.ru/2016/05/19/gcd-handbook<p><em>GCD - Grand Central Dispatch - библиотека от Apple для iOS и OS X для работы с примитивами многозадачности. На просторах интернета наткнулся на <a href="http://khanlou.com/2016/04/the-GCD-handbook/">статью</a> с некоторыми рецептами по работе с GCD. Статья на английском и очень интересная. Здесь хочу предложить вам ее вольный перевод. Далее повествование ведется от лица автора.</em></p>
<p>Grand Central Dispatch или GCD - очень мощная штуковина. Она предоставляет в ваше распоряжение низкоуровневые конструкции, такие как очереди и семафоры, которые вы можете комбинировать различными путями для получения многотопоточных эффектов. Однако, основанное на языке С API, на первый взгляд кажется книгой заклинаний и не сразу понятно, как собрать эти низкоуровневые кубики в нечто полезное высокоуровневое. В этой статье я постараюсь описать некоторые полезные конструкции, которые вы сможете использовать в своих приложениях.</p>
<h2 id="Выполнение-задач-в-фоне">Выполнение задач в фоне</h2>
<p>Пожалуй самое просто что может потребоваться - это выполнить некоторую работу в фоновом потоке, а затем вернуть результат в основной поток для отображения, так как такие компоненты как UIKit могут работать исключительно из главного потока.</p>
<p>В этой статье для отображения чего-то, что занимает много времени для выполнения будут использоваться функции вида <code class="highlighter-rouge">doSomeExpensiveWork()</code></p>
<p>И так, чтобы выполнить что-то в фоновом потоке, а затем вернуться в основной надо сделать примерно так:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="nv">defaultPriority</span> <span class="o">=</span> <span class="kt">DISPATCH_QUEUE_PRIORITY_DEFAULT</span>
<span class="k">let</span> <span class="nv">backgroundQueue</span> <span class="o">=</span> <span class="nf">dispatch_get_global_queue</span><span class="p">(</span><span class="n">defaultPriority</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="nf">dispatch_async</span><span class="p">(</span><span class="n">backgroundQueue</span><span class="p">)</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="nf">doSomeExpensiveWork</span><span class="p">()</span>
<span class="nf">dispatch_async</span><span class="p">(</span><span class="nf">dispatch_get_main_queue</span><span class="p">())</span> <span class="p">{</span>
<span class="c1">// используем как-нибудь `result`</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>На практике, очень редко используются другие приоритеты, нежели <code class="highlighter-rouge">DISPATCH_QUEUE_PRIORITY_DEFAULT</code>. Функция <code class="highlighter-rouge">dispatch_get_global_queue</code> возвращает очередь, задания из которой могут выполняться на сотнях разных потоков. Если вам требуется, чтобы какие-то тяжелые задачи всегда выполнялись на какой-то определенной фоновой очереди, вы можете создать свою с помощью <code class="highlighter-rouge">dispatch_queue_create</code>. Она принимает имя очереди и флаг того, должна быть очередь последовательной (serial) или конкурентной (concurrent).</p>
<p>Важно, чтобы каждый вызов использовал dispatch_<strong>a</strong>sync, а не dispatch_sync. dispatch_async возвращает управление до того момента, как блок будет выополнен. Напротив, dispatch_sync дожидается выполнения блока перед тем как вернуть управление. Внутренний вызов в dispatch_sync может использовать dispatch_sync (так как в общем все равно когда вернется управление), но внешний вызов должен быть dispatch_async, потому что иначе главный поток будет заблокирован.</p>
<h2 id="singleton-паттерн-одиночка">Singleton (паттерн одиночка)</h2>
<p>С помощью dispatch_once вы можете создавать объекты паттерна “одиночка”. В swift это уже не так важно, так как есть более простые способы, однако для информации все же паттерн приводится ниже (ну и для Objective-C это по прежнему важно).</p>
<div class="language-objc highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">+</span> <span class="p">(</span><span class="n">instancetype</span><span class="p">)</span> <span class="n">sharedInstance</span> <span class="p">{</span>
<span class="k">static</span> <span class="n">dispatch_once_t</span> <span class="n">onceToken</span><span class="p">;</span>
<span class="k">static</span> <span class="n">id</span> <span class="n">sharedInstance</span><span class="p">;</span>
<span class="n">dispatch_once</span><span class="p">(</span><span class="o">&</span><span class="n">onceToken</span><span class="p">,</span> <span class="o">^</span><span class="p">{</span>
<span class="n">sharedInstance</span> <span class="o">=</span> <span class="p">[[</span><span class="n">self</span> <span class="nf">alloc</span><span class="p">]</span> <span class="nf">init</span><span class="p">];</span>
<span class="p">});</span>
<span class="k">return</span> <span class="n">sharedInstance</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="Сглаживаем-О_О-блок-обратного-вызова">Сглаживаем О_О блок обратного вызова</h2>
<p><em>Сглаживание конечно не совсем верный перевод для термина flatten, который еще можно перевести как выравнивание, но за неимением лучшей альтернативы пока так, можете предложить лучше, если знаете. (прим. переводчика)</em></p>
<p>А вот тут уже становится интересно. С помощью семафора, мы можем заблокировать поток на некоторое количество времени, пока не получим сигнал от другого потока. Семафоры, как и другие примитивы GCD, потокобезопасные, поэтому их можно вызывать где угодно.</p>
<p>Семафоры можно использовать тогда, когда вы имеете асинхронное API но хотите сделать синхронный вызов, но API менять по какой-то причине не можете.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// на фоновой очереди</span>
<span class="n">dispatch_semaphore_t</span> <span class="n">semaphore</span> <span class="o">=</span> <span class="nf">dispatch_semaphore_create</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="nf">doSomeExpensiveWorkAsynchronously</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">dispatch_semaphore_signal</span><span class="p">(</span><span class="n">semaphore</span><span class="p">)</span>
<span class="p">}</span>
<span class="nf">dispatch_semaphore_wait</span><span class="p">(</span><span class="n">semaphore</span><span class="p">,</span> <span class="kt">DISPATCH_TIME_FOREVER</span><span class="p">)</span>
<span class="c1">// тяжелая асинхронная обработка закончена</span>
</code></pre></div></div>
<p>С помощью вызова <code class="highlighter-rouge">dispatch_semaphore_wait</code> вы блокируете поток до тех пор, пока где-либо не будет вызван сигнал <code class="highlighter-rouge">dispatch_semaphore_signal</code>. Это означает, что сигнал должен быть вызван из другого потока, так как текущий заблокирован. И более, <strong>вы никогда не должны вызывать wait из главного потока, только из фонового</strong>.</p>
<p>Вы можете выбрать любой промежуток времени при вызове <code class="highlighter-rouge">dispatch_semaphore_wait</code> (это будет своего рода таймаут), но обычно передается <code class="highlighter-rouge">DISPATCH_TIME_FOREVER</code>.</p>
<p>Может быть не совсем понятно зачем вам делать синхронный вызов из функции, которая <strong>уже</strong> содержит блок обратного вызова, но если вам это потребуется, вы знаете как это сделать. Однако, есть один случай, когда вам это может понадобиться - вам необходимо выполнить ряд асинхронных операций <strong>последовательно</strong>. Для того, чтобы облегчить использование, можно написать простой класс для абстракции - AsyncSerialWorker:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">typealias</span> <span class="kt">DoneBlock</span> <span class="o">=</span> <span class="p">()</span> <span class="o">-></span> <span class="p">()</span>
<span class="kd">typealias</span> <span class="kt">WorkBlock</span> <span class="o">=</span> <span class="p">(</span><span class="kt">DoneBlock</span><span class="p">)</span> <span class="o">-></span> <span class="p">()</span>
<span class="kd">class</span> <span class="kt">AsyncSerialWorker</span> <span class="p">{</span>
<span class="kd">private</span> <span class="k">let</span> <span class="nv">serialQueue</span> <span class="o">=</span> <span class="nf">dispatch_queue_create</span><span class="p">(</span><span class="s">"com.khanlou.serial.queue"</span><span class="p">,</span> <span class="kt">DISPATCH_QUEUE_SERIAL</span><span class="p">)</span>
<span class="kd">func</span> <span class="nf">enqueueWork</span><span class="p">(</span><span class="nv">work</span><span class="p">:</span> <span class="kt">WorkBlock</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">dispatch_async</span><span class="p">(</span><span class="n">serialQueue</span><span class="p">)</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">semaphore</span> <span class="o">=</span> <span class="nf">dispatch_semaphore_create</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="nf">work</span><span class="p">({</span>
<span class="nf">dispatch_semaphore_signal</span><span class="p">(</span><span class="n">semaphore</span><span class="p">)</span>
<span class="p">})</span>
<span class="nf">dispatch_semaphore_wait</span><span class="p">(</span><span class="n">semaphore</span><span class="p">,</span> <span class="kt">DISPATCH_TIME_FOREVER</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Этот небольшой класс создает последовательную очередь и позволяет вам передать в нее блок на обработку. Объект WorkBlock дает вам DoneBlock для вызова, когда работа будет закончена, который в свою очередь отправит сигнал семафору и очередь отправит на обработку следующий блок.</p>
<h2 id="Ограничиваем-количество-одновременно-выполняемых-блоков">Ограничиваем количество одновременно выполняемых блоков</h2>
<p>В предыдущем примере, семафор использовался как обычный флаг, но он может быть использован как счетчик для ограниченных ресурсов. Например, вы хотите открыть определенное количество подключений к определенному ресурсу. Вам поможет в этом следующий класс:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">LimitedWorker</span> <span class="p">{</span>
<span class="kd">private</span> <span class="k">let</span> <span class="nv">concurrentQueue</span> <span class="o">=</span> <span class="nf">dispatch_queue_create</span><span class="p">(</span><span class="s">"com.khanlou.concurrent.queue"</span><span class="p">,</span> <span class="kt">DISPATCH_QUEUE_CONCURRENT</span><span class="p">)</span>
<span class="kd">private</span> <span class="k">let</span> <span class="nv">semaphore</span><span class="p">:</span> <span class="n">dispatch_semaphore_t</span>
<span class="nf">init</span><span class="p">(</span><span class="nv">limit</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span>
<span class="n">semaphore</span> <span class="o">=</span> <span class="nf">dispatch_semaphore_create</span><span class="p">(</span><span class="n">limit</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">enqueueWork</span><span class="p">(</span><span class="nv">work</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="p">())</span> <span class="p">{</span>
<span class="nf">dispatch_async</span><span class="p">(</span><span class="n">concurrentQueue</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">dispatch_semaphore_wait</span><span class="p">(</span><span class="n">semaphore</span><span class="p">,</span> <span class="kt">DISPATCH_TIME_FOREVER</span><span class="p">)</span>
<span class="nf">work</span><span class="p">()</span>
<span class="nf">dispatch_semaphore_signal</span><span class="p">(</span><span class="n">semaphore</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Этот пример взят из <a href="https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102-SW24">Apple Concurrency Programming Guide</a>. Они могут объяснить что происходит лучше чем я:</p>
<blockquote>
<p>Когда вы создаете семафор, вы можете задать количество доступных ресурсов. Это значение становится первичным значением счетчика для семафора. Каждый раз когда вы вызываете wait на семафоре, <code class="highlighter-rouge">dispatch_semaphore_wait</code> уменьшает счетчик на единицу. Если полученное значение отрицательно, функция блокирует ваш поток. С другой стороны, вызов функции <code class="highlighter-rouge">dispatch_semaphore_signal</code> увеличивает счетчик на единицу для уведомления о том, что ресурс освобожден. Если есть заблокированные задачи, ожидающие доступ к ресурсу, одна из них разблокируется и начнет выполнение.</p>
</blockquote>
<p>Эффект аналогичен установке значения <code class="highlighter-rouge">maxConcurrentOperationCount</code> объекта <code class="highlighter-rouge">NSOperationQueue</code>. Если вы используете сырой GCD вместо NSOperationQueue, вы можете использовать семафоры для ограничения количества одновременно выполняемых блоков.</p>
<p><strong>ВАЖНО!</strong> Здесь присутствует один важный момент. Каждый раз когда вы вызываете enqueueWork и вы достигли ограничения семафора - создается новый поток. Если у вас небольшой лимит и много задач для размещения в очередь, вы таким образом создадите огромное количество заблокированных потоков. Используйте профайлер и избегайте узких мест.</p>
<h2 id="Ожидаем-единого-завершения-нескольких-асинхронных-операций">Ожидаем единого завершения нескольких асинхронных операций</h2>
<p>Если у вас есть несколько асинхронно выполняемых блоков и вы хотите дождаться их общего завершения, вы можете использовать группу. <code class="highlighter-rouge">dispatch_group_async</code> позволяет вам добавить задачу в очередь (задача в блоке однако должна быть синхронной), и ведет учет количество добавленных задач. Важно, что одна и та же группу может добавлять задачи в несколько разных очередь и учитывать все из них. Когда все задачи в группе будут выполнены, вызывается блок, переданный в <code class="highlighter-rouge">dispatch_group_notify</code> - это своего рода блок обратного вызова на всю группу.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dispatch_group_t</span> <span class="n">group</span> <span class="o">=</span> <span class="nf">dispatch_group_create</span><span class="p">()</span>
<span class="k">for</span> <span class="n">item</span> <span class="k">in</span> <span class="n">someArray</span> <span class="p">{</span>
<span class="nf">dispatch_group_async</span><span class="p">(</span><span class="n">group</span><span class="p">,</span> <span class="n">backgroundQueue</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">performExpensiveWork</span><span class="p">(</span><span class="nv">item</span><span class="p">:</span> <span class="n">item</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nf">dispatch_group_notify</span><span class="p">(</span><span class="n">group</span><span class="p">,</span> <span class="nf">dispatch_get_main_queue</span><span class="p">())</span> <span class="p">{</span>
<span class="c1">// все задачи выполнены</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Это хороший пример сглаживания функции, которая имеет блок обратного вызова. Существует другой, более ручной способ использования групп, особенно, если часть ваших фоновых вычислений уже асинхронная.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// этот код должен выполняться на фоновом потоке</span>
<span class="n">dispatch_group_t</span> <span class="n">group</span> <span class="o">=</span> <span class="nf">dispatch_group_create</span><span class="p">()</span>
<span class="k">for</span> <span class="n">item</span> <span class="k">in</span> <span class="n">someArray</span> <span class="p">{</span>
<span class="nf">dispatch_group_enter</span><span class="p">(</span><span class="n">group</span><span class="p">)</span>
<span class="nf">performExpensiveAsyncWork</span><span class="p">(</span><span class="nv">item</span><span class="p">:</span> <span class="n">item</span><span class="p">,</span> <span class="nv">completionBlock</span><span class="p">:</span> <span class="p">{</span>
<span class="nf">dispatch_group_leave</span><span class="p">(</span><span class="n">group</span><span class="p">)</span>
<span class="p">})</span>
<span class="p">}</span>
<span class="nf">dispatch_group_wait</span><span class="p">(</span><span class="n">group</span><span class="p">,</span> <span class="kt">DISPATCH_TIME_FOREVER</span><span class="p">)</span>
<span class="c1">// все фоновые задачи в группе выполнены</span>
</code></pre></div></div>
<p>Этот пример сложнее, но если пройтись по нему строка за строкой то все станет понятно. Как и семафор, группа содержит потокобезопасный внутренний счетчик, которым можно манипулировать. С помощью него вы можете гарантировать, что блок обратного вызова будет вызван после завершения всех долготекущий операций. Вызов “enter” увеличивает счетчик, вызов “leave” - уменьшает. <code class="highlighter-rouge">dispatch_group_async</code> скрывает для вас все детали, поэтому предпочтительнее.</p>
<p>Последнее в этом примере - это вызов wait, который блокирует поток и ожидает момента когда счетчик достигнет 0. Важно! Вы моежет использовать <code class="highlighter-rouge">dispatch_group_notify</code> даже если вы используете enter/leave. Обратное также верно - вы можете использовать <code class="highlighter-rouge">dispatch_group_wait</code> даже при использовании <code class="highlighter-rouge">dispatch_group_async</code>.</p>
<p>Функция <code class="highlighter-rouge">dispatch_group_wait</code> также как и <code class="highlighter-rouge">dispatch_semaphore_wait</code> принимает в качестве аргумента таймаут. Опять же, редко возникает нужда в чем-то отличном от <code class="highlighter-rouge">DISPATCH_TIME_FOREVER</code>. И так же как и в случае <code class="highlighter-rouge">dispatch_semaphore_wait</code> никогда не вызывайте <code class="highlighter-rouge">dispatch_group_wait</code> на главном потоке.</p>
<p>Самое главное отличие между этими двумя подходами в том, что notify можно вызывать непосредственно на главном потоке, а wait необходимо использовать на фоновом (как минимум wait, так как этот вызов блокирует текущую очередь).</p>
<h2 id="Очереди-изоляции">Очереди изоляции</h2>
<p>Словарь (и массив) в Swift - типы значений. Когда они изменяются, их ссылка полностью заменяется новой копией структуры. Однако, ввиду того что обновление полей экземпляра (ivar) объектов Swift не является атомарной операцией, она не является потокобезопасной. Два потока могут обновить словарь (например, добавить значение) в одно и то же время, и оба попытаются записать один и тот же блок памяти, что приведет к ее повреждению. Для того, чтобы обеспечить потокобезопасность можно использовать очереди изоляции.</p>
<p>Давайте сформируем коллекцию объектов, которая будет представлять собой словарь, где ключом будет ID объекта, а значением - сам объект.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">IdentityMap</span><span class="o"><</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Identifiable</span><span class="o">></span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">dictionary</span> <span class="o">=</span> <span class="kt">Dictionary</span><span class="o"><</span><span class="kt">String</span><span class="p">,</span> <span class="kt">T</span><span class="o">></span><span class="p">()</span>
<span class="kd">func</span> <span class="nf">object</span><span class="p">(</span><span class="n">forID</span> <span class="kt">ID</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-></span> <span class="kt">T</span><span class="p">?</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">dictionary</span><span class="p">[</span><span class="kt">ID</span><span class="p">]</span> <span class="k">as</span> <span class="kt">T</span><span class="p">?</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">addObject</span><span class="p">(</span><span class="nv">object</span><span class="p">:</span> <span class="kt">T</span><span class="p">)</span> <span class="p">{</span>
<span class="n">dictionary</span><span class="p">[</span><span class="n">object</span><span class="o">.</span><span class="kt">ID</span><span class="p">]</span> <span class="o">=</span> <span class="n">object</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Этот объект фактически является оберткой над словарем. Если наша функция addObject будет вызвана из нескольких потоков в одно и то же время, память будет нарушена, так как потоки будут использовать одну и ту же ссылку. Эта известная <a href="https://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B0_%D0%BE_%D1%87%D0%B8%D1%82%D0%B0%D1%82%D0%B5%D0%BB%D1%8F%D1%85-%D0%BF%D0%B8%D1%81%D0%B0%D1%82%D0%B5%D0%BB%D1%8F%D1%85">задача о читателях-писателях</a>. В кратце, мы можем иметь несколько читателей в одно и то же время, но только одного писателя в каждый конкретный момент времени.</p>
<p>К счастью, GCD дает нам замечательные инструменты для этого случая. Нам доступны следующие инструменты для решения этой задачи:</p>
<ul>
<li>dispatch_sync</li>
<li>dispatch_async</li>
<li>dispatch_barrier_sync</li>
<li>dispatch_barrier_async</li>
</ul>
<p>Идеальным случаем будет:</p>
<ul>
<li>чтения случаются синхронно и конкурентно</li>
<li>записи должны быть асинхронными и должны быть единственной задачей, которая работает с ссылкой</li>
</ul>
<p>Барьеры GCD делают одну интересную вещь - они ожидают момента, когда очередь будет полностью пуста, перед тем как выполнить блок. С помощью барьеров для наших чтений мы ограничим доступ к словарю и обеспечим гарантию того, что никакая запись не будет проводиться одновременно с чтением или другой записью.</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">IdentityMap</span><span class="o"><</span><span class="kt">T</span><span class="p">:</span> <span class="kt">Identifiable</span><span class="o">></span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">dictionary</span> <span class="o">=</span> <span class="kt">Dictionary</span><span class="o"><</span><span class="kt">String</span><span class="p">,</span> <span class="kt">T</span><span class="o">></span><span class="p">()</span>
<span class="k">let</span> <span class="nv">accessQueue</span> <span class="o">=</span> <span class="nf">dispatch_queue_create</span><span class="p">(</span><span class="s">"com.khanlou.isolation.queue"</span><span class="p">,</span> <span class="kt">DISPATCH_QUEUE_CONCURRENT</span><span class="p">)</span>
<span class="kd">func</span> <span class="nf">object</span><span class="p">(</span><span class="n">withID</span> <span class="kt">ID</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-></span> <span class="kt">T</span><span class="p">?</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">result</span><span class="p">:</span> <span class="kt">T</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="nf">dispatch_sync</span><span class="p">(</span><span class="n">accessQueue</span><span class="p">)</span> <span class="p">{</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">dictionary</span><span class="p">[</span><span class="kt">ID</span><span class="p">]</span> <span class="k">as</span> <span class="kt">T</span><span class="p">?</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">result</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">addObject</span><span class="p">(</span><span class="nv">object</span><span class="p">:</span> <span class="kt">T</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">dispatch_barrier_async</span><span class="p">(</span><span class="n">accessQueue</span><span class="p">)</span> <span class="p">{</span>
<span class="n">dictionary</span><span class="p">[</span><span class="n">object</span><span class="o">.</span><span class="kt">ID</span><span class="p">]</span> <span class="o">=</span> <span class="n">object</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Функция <code class="highlighter-rouge">dispatch_sync</code> отправит блок в нашу очередь изоляции и будет дожидаться окончания перед тем как вернуть выполнение. После этого, мы будем иметь результат нашего чтения. Если не делать вызов синхронным, тогда потребуется введение блока обратного вызова. Благодаря тому, что очередь конкурентная, такие синхронные чтения могут выполняться по несколько штук параллельно.</p>
<p>Функция <code class="highlighter-rouge">dispatch_barrier_async</code> отправит блок в очередь изоляции. Async означает что управление будет возвращено до того, как блок фактически выполниться (т.е. выполниться запись). Это хорошо для проиводительности, но имеет и обратную сторону медали - чтение сразу после записи может вернуть старые данные.</p>
<p>Барьерная часть <code class="highlighter-rouge">dispatch_barrier_async</code> означает, что блок не будет выполнен до тех пор, пока каждый блок в очереди не закончит свое выполнение. Другие блоки будут размещены после барьерного и выполняться после того, как выполнится барьерный.</p>
<h2 id="Таймер-на-gcd">Таймер на GCD</h2>
<p>Иногда требуется периодическое выполнение какой-то функциональности и как правило для этого используют NSTimer. Однако таймер можно сделать и на GCD примерно вот так:</p>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="nv">timer</span><span class="p">:</span> <span class="n">dispatch_source_t</span><span class="p">?</span>
<span class="kd">func</span> <span class="nf">createTimer</span><span class="p">()</span> <span class="p">{</span>
<span class="n">dispatch_queue_t</span> <span class="n">queue</span> <span class="o">=</span> <span class="nf">dispatch_get_global_queue</span><span class="p">(</span><span class="kt">DISPATCH_QUEUE_PRIORITY_DEFAULT</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="n">double</span> <span class="n">secondsToFire</span> <span class="o">=</span> <span class="mf">1.000</span><span class="n">f</span><span class="p">;</span>
<span class="k">self</span><span class="o">.</span><span class="n">timer</span> <span class="o">=</span> <span class="nf">dispatch_source_create</span><span class="p">(</span><span class="kt">DISPATCH_SOURCE_TYPE_TIMER</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">queue</span><span class="p">)</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">timer</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">timer</span> <span class="p">{</span>
<span class="nf">dispatch_source_set_timer</span><span class="p">(</span><span class="n">timer</span><span class="p">,</span> <span class="kt">DISPATCH_TIME_NOW</span><span class="p">,</span> <span class="n">secondsToFire</span> <span class="o">*</span> <span class="kt">NSEC_PER_SEC</span><span class="p">,</span> <span class="p">(</span><span class="mi">1</span><span class="n">ull</span> <span class="o">*</span> <span class="kt">NSEC_PER_SEC</span><span class="p">)</span> <span class="o">/</span> <span class="mi">10</span><span class="p">)</span>
<span class="nf">dispatch_source_set_event_handler</span><span class="p">(</span><span class="n">timer</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// do something useful</span>
<span class="p">}</span>
<span class="nf">dispatch_resume</span><span class="p">(</span><span class="n">timer</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">releaseTimer</span><span class="p">()</span> <span class="p">{</span>
<span class="k">guard</span> <span class="k">let</span> <span class="nv">timer</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">timer</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
<span class="nf">dispatch_source_cancel</span><span class="p">(</span><span class="n">timer</span><span class="p">)</span>
<span class="n">timer</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="post-scriptum">Post Scriptum</h2>
<p>Фреймворк GCD содержит много низкоуровневых примитивов. С их помощью мы смогли построить высокоуровневые конструкции. Если вам известны какие-то другие высокоуровневые конструкции не упомянутые здесь, будут рад их услышать <em>(вы можете сделать PR тут или отправить напрямую автору. прим. переводчика)</em></p>
Полезное для jekyll2016-05-15T00:00:00+00:00http://mrdekk.ru/2016/05/15/jekyll-usefull-stuff<ul>
<li><a href="https://github.com/jneen/rouge/wiki/List-of-supported-languages-and-lexers">список доступных подсветок синтаксиса для rouge</a></li>
<li><a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet">github flavored markdown cheatsheet</a></li>
</ul>
Символификация crash-дампа iOS2016-05-11T00:00:00+00:00http://mrdekk.ru/2016/05/11/symbolicate-crash-log<p>Допустим вам надо понять что случилось с приложением и у Вас есть крэш-дамп, однако на телефоне стоит iOS 6.1 которую xCode 7.3.1
принципиально знать не хочет и крэш-дампы с этого устройства не забирает. А смотреть надо. Для этого включаем iTunes и синхронизируем устройство,
тогда по пути</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">~/Library/Logs/CrashReporter/MobileDevice/</code></pre></figure>
<p>вы сможете найти крэш-дампы. После этого идем по пути</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">/Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources</code></pre></figure>
<p>Для xCode 7.3 путь меняется</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">/Applications/Xcode7.3.1.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources</code></pre></figure>
<p>где лежит symbolicatecrash. Берем его и копируем вместе с *.crash файлом и *.ipa файлом (предварительно выключив опцию Strip Debug Symbols During Copy) после этого выполняем нехитрый скрипт</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">export </span><span class="nv">DEVELOPER_DIR</span><span class="o">=</span>/Applications/xCode6.4/Xcode6.4.app/Contents/Developer
<span class="nv">$ </span>./symbolicatecrash <span class="nt">-v</span> superapp_2016-05-11-211803_superphone.crash superapp.ipa</code></pre></figure>
Боремся с The ‘Pods...’ target has transitive dependencies that include static binaries2016-04-30T00:00:00+00:00http://mrdekk.ru/2016/04/30/cocoapods-static-transitive-binary-dependencies<p>Суть проблемы следующаяя</p>
<ol>
<li>Используем CocoaPods (про Carthage знаю, по некоторым причинам не хочу использовать)</li>
<li>Проект на Swift</li>
<li>Имеет swift’овые зависимости, поэтому use_frameworks!</li>
<li>Имеет старые Objective-C’шные зависимости, в которых есть транзитивные зависимости в которых есть статические либы (something.a, можно в vendored_libraries)</li>
</ol>
<p>При попытке сделать pod install получаем</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>The ‘Pods-...’ target has transitive dependencies that include static binaries
</code></pre></div></div>
<p>Сие подробно дискутируется <a href="https://github.com/CocoaPods/CocoaPods/issues/2926">тут</a> и <a href="https://github.com/CocoaPods/CocoaPods/issues/3289">тут</a></p>
<p>Внятного решения даже разработчики cocoapods судя по всему пока придумать не могут, но зато предлагают такой вот хак</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">pre_install</span> <span class="k">do</span> <span class="o">|</span><span class="n">installer</span><span class="o">|</span>
<span class="k">def</span> <span class="nc">installer</span><span class="o">.</span><span class="nf">verify_no_static_framework_transitive_dependencies</span><span class="p">;</span> <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Который просто напросто отключает проверку.</p>
<p>P.S. Правда сий хак не всегда помогает :( и вы можете получить unresolved symbols</p>
<p>P.P.S. Если же вы все-таки хотите transitive static binaries и use_frameworks! то решение существует - необходимо этот transitive static binary обернуть руками в framework а в под зашить vendored_frameworks примерно следующим образом</p>
<p>Нам нужен скрипт создания фреймворка (будем показывать на примере OpenSSL for iPhone)</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>create-openssl-framework.sh
</code></pre></div></div>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="nv">FWNAME</span><span class="o">=</span>openssl
<span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-d</span> lib <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">echo</span> <span class="s2">"Please run build-libssl.sh first!"</span>
<span class="nb">exit </span>1
<span class="k">fi
if</span> <span class="o">[</span> <span class="nt">-d</span> <span class="nv">$FWNAME</span>.framework <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">echo</span> <span class="s2">"Removing previous </span><span class="nv">$FWNAME</span><span class="s2">.framework copy"</span>
rm <span class="nt">-rf</span> <span class="nv">$FWNAME</span>.framework
<span class="k">fi
if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"dynamic"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nv">LIBTOOL_FLAGS</span><span class="o">=</span><span class="s2">"-dynamic -undefined dynamic_lookup"</span>
<span class="k">else
</span><span class="nv">LIBTOOL_FLAGS</span><span class="o">=</span><span class="s2">"-static"</span>
<span class="k">fi
</span><span class="nb">echo</span> <span class="s2">"Creating </span><span class="nv">$FWNAME</span><span class="s2">.framework"</span>
mkdir <span class="nt">-p</span> <span class="nv">$FWNAME</span>.framework/Headers
libtool <span class="nt">-no_warning_for_no_symbols</span> <span class="nv">$LIBTOOL_FLAGS</span> <span class="nt">-o</span> <span class="nv">$FWNAME</span>.framework/<span class="nv">$FWNAME</span> <span class="nt">-install_name</span> @rpath/<span class="nv">$FWNAME</span>.framework/<span class="nv">$FWNAME</span> lib/libcrypto.a lib/libssl.a
cp <span class="nt">-r</span> include/<span class="nv">$FWNAME</span>/<span class="k">*</span> <span class="nv">$FWNAME</span>.framework/Headers/
<span class="nb">echo</span> <span class="s2">"Created </span><span class="nv">$FWNAME</span><span class="s2">.framework"</span>
</code></pre></div></div>
<p><strong>ВАЖНО:</strong> чтобы имелся параметр -install_name … так как без него у вас все соберется, но при запуске будет Library not loaded: Image not found</p>
<p>Сам же podspec файл будет выглядеть примерно следующим образом</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="s2">"name"</span><span class="p">:</span> <span class="s2">"OpenSSL-for-iOS"</span><span class="p">,</span>
<span class="p">...</span>
<span class="s2">"prepare_command"</span><span class="p">:</span> <span class="s2">"./build-libssl.sh</span><span class="err">\</span><span class="s2">n./create-openssl-framework.sh</span><span class="err">\</span><span class="s2">n"</span><span class="p">,</span>
<span class="s2">"ios"</span><span class="p">:</span> <span class="p">{</span>
<span class="s2">"vendored_frameworks"</span><span class="p">:</span> <span class="s2">"openssl.framework"</span>
<span class="p">},</span>
<span class="p">...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>И далее цепляете под себе в Podfile обычным образом</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">pod</span> <span class="s1">'OpenSSL-for-iOS'</span>
</code></pre></div></div>
<p>С включенным use_frameworks!</p>
Путь до сборочной директории xCode2016-03-24T00:00:00+00:00http://mrdekk.ru/2016/03/24/xcode-build-dir<p>Тут:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~/Library/Developer/Xcode/DerivedData
</code></pre></div></div>
GIT для пользователей SVN2016-02-07T00:00:00+00:00http://mrdekk.ru/2016/02/07/git-for-svn-users<p>Записал слайдкаст с краткими замечаниями по GIT’у для пользователей SVN</p>
<iframe src="http://files.slidesnack.com/iframe/embed.html?hash=b7umykjt&wmode=transparent&bgcolor=EEEEEE&t=1454798716&type=slidecast" width="854" height="480" frameborder="0" allowtransparency="true"></iframe>
Создание новых веток в SVN при работе с GIT-SVN2015-12-16T00:00:00+00:00http://mrdekk.ru/2015/12/16/new-svn-branch-on-gitsvn<p>Если вам требуется создать ветку в SVN, но при этом вы работаете в GIT, то ситуация сначала может показаться странной, ведь если вы создали ветку от svn/trunk то svn dcommit будет отправлять изменения в svn/trunk а не в вашу ветку. Проблема решается достаточно просто, вот рецепт:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git svn branch foo
<span class="nv">$ </span>git checkout <span class="nt">-b</span> foo <span class="nt">-t</span> svn/foo
<span class="nv">$ </span>...
<span class="nv">$ </span>git commit
<span class="nv">$ </span>git svn dcommit
</code></pre></div></div>
Передача веток и коммитов между двумя GIT-репозиториями2015-12-10T00:00:00+00:00http://mrdekk.ru/2015/12/10/branches-pass-between-isolated-repos<p>Случилась такая задача. Есть два git-репозитория (один из которых ко всему прочему соединен с SVN через git-svn). Между ними необходимо передавать ветки и коммиты. Если бы они имели прямую связь через файловую систему или например через http, особых проблем бы не было. Однако, никакой связи кроме человека с флэшкой между этими репозиториями нет. Поэтому пришлось изобретать некий workflow который позволил бы эффективно эту проблему решить.</p>
<p>Вот картинка того, что требуется сделать:</p>
<p><img src="/media/images/branches-pass/git.bundle.exchange.01.png" alt="branches pass between isolated repos" /></p>
<p>Решить эту проблему можно тремя способами:</p>
<ul>
<li>Скопировать весь локальный репозиторий одной стороны (например, Repo A) и перенести его на флэшке рядом с репозиторием (Repo B) чтобы между ними можно было установить прямую связь через локальную файловую систему</li>
<li>Использовать механизм патчей (git am)</li>
<li>Использовать механизм пакетов git’а (git bundles)</li>
</ul>
<p>Первый способ нам не подошел. Хотя бы потому, что репозиторий весит около 1.5 ГБ в сжатом виде. А заливать его приходилось в том числе через RDP соединение. Хотя если у вас есть такая возможность – это самый правильный и самый лучший вариант. Нам увы не подошел, поэтому идем дальше.</p>
<p>Второй способ мы даже активно использовали. Workflow там примерно следующий. С какого-то определенного коммита мы делаем набор патчей для каждого коммита выбранной ветки. На другом репозитории мы переключаемся (или создаем если такой ветки еще нет) на нужную ветку и делаем Apply Patch Serial. В принципе это все работает, но есть проблемы. Проблема первая – фактически в двух репозиториях мы имеем две разные ветки, хоть они одинаково и называются. И содержат разные коммиты, хоть и они содержат одно и то же. Кроме того, возникают нетривиальные вещи связанные с разрешением коллизий. Вообщем, достаточно громоздко и сложно, хотя и работает.</p>
<p>Третий способ – git bundles. Вот его и рассмотрим.</p>
<p>Начнем с того, что git bundle – это такой специальный файл. В который во-первых упакованы нужные ветки и нужные коммиты (вы их указываете сами). Во-вторых, он может представляться как удаленный git-репозиторий, который можно добавить в remotes и работать с ним как с полноценным удаленным репозиторием. Скажем так – этот подход – лайт версия первого подхода, когда вы тащите за собой весь репозиторий. Только здесь вы тащите один файл с тем, что надо. Места он правда может занять тоже весьма нехило, но в общем гораздо меньше (при правильном подходе), чем весь репозиторий.</p>
<p>Теперь давайте рассмотрим как это дело провернуть. Предположим у вас есть репозиторий A из которого необходимо перетащить ветку в репозиторий B (который пуст).</p>
<p>Имеем несколько файлов в репозитории A:</p>
<p><img src="/media/images/branches-pass/RepoA.jpg" alt="repo a" /></p>
<p>И ветку master:</p>
<p><img src="/media/images/branches-pass/RepoAHistory.jpg" alt="master branch" /></p>
<h3 id="Создание-бандлов">Создание бандлов</h3>
<p>Теперь нам нужно создать (пересоздать) бандл. Тут два пути – если он уже был создан когда-то, или же его еще не было.</p>
<p><strong>Случай, когда мы создаем новый бандл</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Создаем новый бандл для ветки мастер (веток может быть несколько)</span>
<span class="nv">$ </span>git bundle create ../master.bundle master
<span class="c"># Помечаем последний коммит на ветке мастер который мы себе забрали (чтобы в следующий раз не тащить все)</span>
<span class="c"># lastR2bundle - просто некоторое имя</span>
<span class="nv">$ </span>git tag <span class="nt">-f</span> lastR2bundle master
</code></pre></div></div>
<p><strong>Случай, если бандл уже создавали (и появились новые изменения)</strong></p>
<p><img src="/media/images/branches-pass/RepoAHistoryUpdated.jpg" alt="bundle been created" /></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Создаем бандл, но уже с указанием места, с которого его создавали в предыдущий раз</span>
<span class="nv">$ </span>git bundle create ../master2.bundle lastR2bundle..master
<span class="c"># Обновляем указатель последнего коммита бандла</span>
<span class="nv">$ </span>git tag <span class="nt">-f</span> lastR2bundle master
</code></pre></div></div>
<h3 id="Разворачивание-бандлов">Разворачивание бандлов</h3>
<p>Теперь бандл у нас есть, необходимо развернуть (обновить) его на другом репозитории RepoB.</p>
<p><strong>В случае если бандл мы принесли первый раз и репозитория нет.</strong></p>
<p>Тогда можно просто склонировать репозиторий прямо с бандла:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Клонируем репозиторий с бандла в текущую папку</span>
<span class="nv">$ </span>git clone <span class="nt">-b</span> master C:/Temp/gittest/master.bundle <span class="nb">.</span>
</code></pre></div></div>
<p>Ветка автоматически разворачивается до текущего состояния (флаг -b master указывает нужную ветку)</p>
<p><img src="/media/images/branches-pass/RepoB_ClonedFromBundle.jpg" alt="branch updated" /></p>
<p><strong>Случай, когда бандл принесли первый раз, но репозиторий уже есть</strong></p>
<p>Тут несколько сложнее, необходимо зайти в папку .git в корне репозитория и отредактировать там файл config</p>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># создаем новый remote с именем RepoA
</span>[<span class="n">remote</span> <span class="s2">"RepoA"</span>]
<span class="c"># указываем путь до бандла
</span><span class="n">url</span> = <span class="n">C</span>:/<span class="n">Temp</span>/<span class="n">gittest</span>/<span class="n">master</span>.<span class="n">bundle</span>
<span class="c"># указываем способ получения веток из бандла
</span><span class="n">fetch</span> = +<span class="n">refs</span>/<span class="n">heads</span>/*:<span class="n">refs</span>/<span class="n">remotes</span>/<span class="n">RepoA</span>/*
</code></pre></div></div>
<p>После этого делаем</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git pull RepoA master
</code></pre></div></div>
<p>И получаем выгруженную историю из бандла.</p>
<p><strong>Случай, когда бандл уже приносили</strong></p>
<p>В этом случае достаточно заменить старую версию бандла новым, и сделать git pull. Бандлы, сделанные не с самого «начала времен» а с определенного места занимают не так много места.</p>
<p>Надеюсь всем было все понятно и это поможет вам вести удобную разработку с использованием git’а.</p>
<p>P.S. Механизм бандлов работает в обе стороны, изменения в нашей схеме можно переносить не только из RepoA в RepoB, но и наоборот.</p>
Зачекаутить все submodules в проекте git2015-10-12T00:00:00+00:00http://mrdekk.ru/2015/10/12/checkout-all-git-submodules<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git submodule foreach git pull origin master
</code></pre></div></div>
<p>Работает начиная с GIT 1.6.1</p>
docker в Ubuntu через прокси2015-10-02T00:00:00+00:00http://mrdekk.ru/2015/10/02/docker-through-proxy<p>Чтобы в свою очередь docker мог в ubuntu работать через proxy редактируем файл /etc/default/docker в самый конец добавляем</p>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">export</span> <span class="n">http_proxy</span>=<span class="s2">"http://login:password@host:port"</span>
<span class="n">export</span> <span class="n">https_proxy</span>=<span class="s2">"http://login:password@host:port"</span>
</code></pre></div></div>
<p>и перезапускаем службу docker</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>service docker restart
</code></pre></div></div>
<p>После этого можно проверить hello-world</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span> <span class="nb">sudo </span>docker run hello-world
</code></pre></div></div>
Работа apt-get в Ubuntu через прокси2015-10-02T00:00:00+00:00http://mrdekk.ru/2015/10/02/aptitude-through-proxy<p>Чтобы apt-get в Ubuntu заработал через прокси, необходимо создать или добавить в файл</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/etc/apt/apt.conf.d/proxy
</code></pre></div></div>
<p>Следующие строчки:</p>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Acquire</span>::<span class="n">http</span>::<span class="n">Proxy</span> <span class="s2">"http://login:password@host:port"</span>;
<span class="n">Acquire</span>::<span class="n">ftp</span>::<span class="n">Proxy</span> <span class="s2">"http://login:password@host:port"</span>;
<span class="n">Acquire</span>::::<span class="n">Proxy</span> <span class="s2">"true"</span>;
</code></pre></div></div>
<p>И все заработает</p>
Решение проблемы с PowerShell2015-09-29T00:00:00+00:00http://mrdekk.ru/2015/09/29/powershell-problem<p>Возникла проблема PowerShell «не удается загрузить файл так как выполнение скриптов запрещено для данной системы»</p>
<p>Решается выключением нафиг этой защиты</p>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Set</span>-<span class="n">ExecutionPolicy</span> <span class="n">Unrestricted</span>
</code></pre></div></div>
О фреймворках JavaScript’овых2015-09-23T00:00:00+00:00http://mrdekk.ru/2015/09/23/about-javascript-frameworks<p>В последнее время наблюдаю ситуацию, когда на JavaScript’е пытаются сделать все, что только можно. Тенденция на самом деле пугающая. Популярность понятна – низкий порог вхождения; отсутствие строгих требований к пониманию того, как работают программные системы и распространенность инструментарий (фактически для начала нужен хром и блокнот). Однако, «программисты» на JavaScript’е большей частью люди, далекие от серьезной разработки (существуют конечно и уважаемые гуру, но их как правило гораздо меньше) пишут свой код бездумно и в больших количествах. И очень часто этот код попадает в рабочие проекты. И после этого, когда дело доходит до того, что «система не работает» с этим приходится разбираться. Для того, чтобы немного систематизировать информацию и, дай бог, уменьшить количество такого кода и предназначена эта статья.</p>
<p>Но для начала немного терминологии, которую будем использовать в этой статье. Разделим все Web-страницы, Web-сайты и Web-приложения на два «лагеря» – backend-ориентированные и frontend-ориентированные.</p>
<p>Backend-ориентированные в вопросах обработки бизнес-информации и бизнес-логики опираются на бэкенды, которые написаны как правило на таких языках как Java, PHP, Ruby, Python и даже C++ (надеюсь никого не обидел). Представление в таких приложения как правило не требует каких-то более серьезных вещей кроме как динамика представления данных, обработка событий пользователя и передача их на бэкенд.</p>
<p>Frontend-ориентированные приложения как правило оставляют бэкенду только вопросы взаимодействия с базой данных или другими приложениями и реже вопросы формирования самих представлений. Основная часть работы в таких приложениях ложится на непосредственно frontend, где необходимо кроме собственно динамики представления обеспечивать такие вещи как формирование представлений (сиречь HTML код), обработку переходов и взаимодействия между несколькими представлениями, получение и кэширование данных. Да что там говорить, вообщем-то весь MVC переезжает во Frontend.</p>
<p>Какой подход лучше зависит от многих факторов. Для начала давайте рассмотрим предположительные плюсы и минусы каждого подхода</p>
<p><strong>Backend-ориентированные:</strong></p>
<p>Плюсы:</p>
<ul>
<li>Бизнес-логика работает как правило на более структурированных и более производительных технологиях, нежели JavaScript, который хоть и кажется универсальным, достаточно медленен. (<strong>Поправка:</strong> в последнее время с развитием технологии V8 на некоторых задачах бэкенд на JS иногда даже рвет например Java)</li>
<li>Бизнес-логика скрыта от конечного пользователя и заниматься ее реверсом (reverse engineering) он не имеет возможности</li>
<li>Как правило в силу большей «взрослости» и более высокого порога вхождения, бэкенды как правило лучше спроектированы и реализованы</li>
<li>Благодаря тому, что код бэкенда работает в известном окружении и управляется квалифицированными людьми, допустимые использования как правило шире, чем на JavaScript’е в браузере (который работает в песочнице)</li>
<li>Как правило стеки технологий для реализация бэкендов закончены и самостоятельны, как правило вам приходится работать с одной определенной версией стека, следовательно вы можете давать некие гарантии надежности</li>
</ul>
<p>Минусы:</p>
<ul>
<li>Как правило более сложная разработка, связанная с временем компиляции и более витиеватым развертыванием (привет долгие редеплои на сервере приложений и известная отмазка программистов - “мой код компилируется”)</li>
<li>Более высокий порог вхождения означает более высокую стоимость разработки</li>
<li>Необходимость фактического перехода между «экранами», которая выглядит как загрузка страницы в браузере, при медленном соединении может быть проблемой</li>
</ul>
<p><strong>Frontend-ориентированные</strong></p>
<p>Плюсы:</p>
<ul>
<li>Низкий порог вхождения – большое количество специалистов, которых вы можете нанять (правда о их квалификации лучше задумываться заранее – скупой платит дважды)</li>
<li>Ввиде того, что JavaScript – интерпретатор, возможна практически интерактивная разработка (а особенно если смотреть в сторону новомодных технологий, которые даже автоматом перерисовывают экран не меняя данные и путь к этому экрану - привет redux)</li>
<li>Инструменты в виде браузера хром весьма и весьма хороши</li>
<li>Отсутствие необходимости перезагружать страницу в браузере</li>
</ul>
<p>Минусы:</p>
<ul>
<li>Скорость работы. Когда вы запускаете сложное MVC приложение со всей логикой запущенной в JavaScript, производительность решения оказывается как правило ниже плинтуса</li>
<li>Спагетти код. Будьте готовы, низкий порог вхождения и отсутствие привычки думать перед написанием кода приводят к печальным результатам</li>
<li>Чтобы исправить ситуацию, вам надо будет все переписать</li>
<li>Количество браузеров практически неисчислимо, кроме собственно разных вендоров и их моделей (хром, файрфокс, опера, сафари и т.д.) вы имеете дело практически со всеми известными версиями этих браузеров, а чего стоят версии программы для скачивания браузеров версий 5.5, 6.0, 7.0 и т.д., которые практически несовместимы друг с другом, а ребро так и вообще некий непонятный зверь. Гарантии надежности, даже самые простые вы давать не можете.</li>
</ul>
<p>Теперь, когда мы определились с терминами скажу свое слово относительно фреймворков. Честно говоря лично мне в данный момент они кажуться чем-то странным и ненужным, но требование объективного изложения требуют от меня аргументации. Что ж попробуем.</p>
<p><strong>Принцип работы Backend-ориентированного приложения</strong></p>
<ul>
<li>Все приложение разделяется на набор «экранов», выполняющих небольшую, но законченную функцию</li>
<li>Представление (HTML) каждого такого экрана формируется на бэкенде, в том числе вспомогательные окна, сообщения и т.д. (тут кстати возможна простая локализация)</li>
<li>Динамика описывается в виде сценариев JavaScript и описывает только конкретный «экран». Код нескольких «экранов» разделен на уровне JavaScript файлов</li>
<li>Клиент запрашивает такой экран вместе с необходимыми представлениями и дополнительным программным кодом, обеспечивающим динамику</li>
<li>ВСЯ сложная обработка обеспечивается на бэкенде и запрашивается представлением посредством AJAX запросов</li>
</ul>
<p><strong>Принцип работы Frontend-ориентированного приложения</strong></p>
<ul>
<li>Бэкенд обеспечивает только отдачу статического содержимого и обработку запросов к источникам данных (БД, …)</li>
<li>Все представления (в том числе отображаемый HTML) формируется в процессе отображения данных в браузере, что вызывает необходимость переноса таких вещей как модель и контроллер фактически в код представления на JavaScript</li>
<li>После того, как клиент получил статику и сформировал представления на стороне клиента, AJAX запросы к серверу делаются исключительно при необходимости работы с источником данных</li>
<li>Собственно, исходя из всего вышеизложенного вытекает инструментарий.</li>
</ul>
<p><strong>Для Backend-ориентированных:</strong></p>
<ul>
<li>J2EE, Spring MVC, JSP – для бэкенда</li>
<li>Twitter Bootstrap, jQuery, Knockout (или другой binding-фреймворк) – для динамики фронтенда</li>
</ul>
<p>Большего не нужно, этого вполне хватает для разработки качественных backend-ориентированных приложений.</p>
<p><strong>Для frontend-ориентированных</strong></p>
<ul>
<li>Любой Web-сервер – для отдачи статики не важно, какой у вас там сервер, хоть с локальной файловой системы запускайтесь</li>
<li>%любой JavaScript фреймворк% – любой из новомодных, а потому может быть еще сырых. Выбирайте любой</li>
</ul>
<p>Иногда вам все же понадобиться что-то, работающее с базой данных. Конечно если вы не используете какой-нибудь MongoDB у которого есть REST-интерфейс</p>
<p>На этом все, можете писать комментарии – по ходу пьессы буду дополнять сий опус.</p>
Переименование виртуальной машины с DB22015-09-04T00:00:00+00:00http://mrdekk.ru/2015/09/04/db2-machine-rename<p>Случилось несчастье – пришлось переименовать виртуальную машину на которой была запущена DB2. После этого DB2 отказалась запускаться со словами</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>«db2start : SQL1022C There is not enough memory available to process the command.».
</code></pre></div></div>
<p>С учетом свободных 6 ГБ оперативной памяти было ясно, что проблема не в ней. После некоторого гугления был найден рецепт. Тут привожу его перевод.</p>
<p>Изначальное виртуалка называлась пусть VM1, после переименования стала VM2 (названия естественно изменены). Что сделать чтобы чудо случилось:</p>
<p>В C:\Windows\system32\drivers\etc\hosts добавляем строки</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>127.0.0.1 VM1
127.0.0.1 VM1.company.com
</code></pre></div></div>
<p>где company.com – ваш домен.</p>
<p>Далее поиском ищем db2nodes.cfg и заменяем там VM1 на VM2, должно получиться примерно так</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 VM2 VM2 0
</code></pre></div></div>
<p>Далее запускаем редактор реестра (regedit) и навигируемся сюда</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HKLM\Software\IBM\DB2\GLOBAL_PROFILE
</code></pre></div></div>
<p>Правим</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DB2_ADMINGROUP "VM1\DB2ADMINS" -> "VM2\DB2ADMINS"
DB2_USERSGROUP "VM1\DB2USERS" -> "VM2\DB2USERS"
DB2SYSTEM "VM1" -> "VM2"
</code></pre></div></div>
<p>После этого перезагружаемся и все должно работать…</p>
<p>И не работает …</p>
<p>Надо сделать еще вот что:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>db2extsec <span class="nt">-r</span>
<span class="nv">$ </span>db2extsec /a DB2ADMNS /u DB2USERS
</code></pre></div></div>
<p>После этого все заработало</p>
Как выковырять из Visual Studio компилятор2015-07-14T00:00:00+00:00http://mrdekk.ru/2015/07/14/get-compiler-from-visual-studio<p>Предположим вам понадобился компилятор Visual C++, но по каким-то причинам вам не нужна сама студия. Просто так компилятор не скачать, Micrоsоft не предоставлять такой опции. Поэтому попытаемся получить его самостоятельно, чисто в исследовательских интересах.</p>
<p>Нам понадобится дистрибутив какой-нибудь студии. Пусть это будет Express, т.к. ее можно для личных целей скачать с официального сайта.</p>
<p>Далее нам потребуются следующие msi из дистрибутива</p>
<ul>
<li>vc_compilerCore86.msi – инструментарий MSVC</li>
<li>vc_compilerCore86res.msi – MUI ресурсы инструментария MSVC</li>
<li>vc_librarycore86.msi – библиотеки MSVC</li>
<li>vc_LibraryDesktopX86.msi – дополнение к библиотекам MSVC</li>
<li>Windows Software Development Kit-x86_en-us.msi – разное из Windows SDK (например, WinSock2.h, WS2_32.lib, может быть что-то еще).</li>
</ul>
<p>Далее это все распаковываем в отдельную папку (собственно это и будет пакет инструментария) следующей командой:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>msiexec /a <span class="k">*</span>.msi <span class="nv">TARGETDIR</span><span class="o">=</span>C:<span class="se">\c</span>ompiler<span class="se">\.</span>..
</code></pre></div></div>
Скрипт запуска сервера WildFly для FreeBSD2015-07-09T00:00:00+00:00http://mrdekk.ru/2015/07/09/wildfly-run-script-freebsd<p>После компиляции java с исправлением бага, дошла очередь до установки WildFly. Однако, все встало без проблем вообще и сразу заработало. Оставалась одна проблема – автозапуск сервера при рестарте сервера (пардон за каламбур). Прошерстив мануалы FreeBSD набросал небольшой скрипт, может кому будет полезен:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="nb">.</span> /etc/rc.subr
<span class="nv">name</span><span class="o">=</span>wildfly
<span class="nv">rcvar</span><span class="o">=</span>wildfly_enable
<span class="nv">start_cmd</span><span class="o">=</span><span class="s2">"/usr/local/wildfly/bin/standalone.sh &"</span>
<span class="nv">stop_cmd</span><span class="o">=</span><span class="s2">"/usr/local/wildfly/bin/jboss-cli.sh --connect controller=127.0.0.1 command=:shutdown"</span>
load_rc_config <span class="nv">$name</span>
run_rc_command <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span>
</code></pre></div></div>
Боремся с «Translet class loaded, but unable to create translet instance.»2015-05-07T00:00:00+00:00http://mrdekk.ru/2015/05/07/translet-not-loaded-problem<p>При работе с XSLT преобразованиями на сервере приложений WildFly возникает такой Exception</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Caused by: javax.xml.transform.TransformerConfigurationException:
Translet class loaded, but unable to create translet instance.
</code></pre></div></div>
<p>Судя по всему, пока в WildFly эту проблему не поправили, существует такой Workaround:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"><!-- Workaround for: "Translet class loaded, but unable to create translet instance." --></span>
<span class="nt"><dependency></span>
<span class="nt"><groupid></span>xalan<span class="nt"></groupid></span>
<span class="nt"><artifactid></span>xalan<span class="nt"></artifactid></span>
<span class="nt"><version></span>2.7.2<span class="nt"></version></span>
<span class="nt"><exclusions></span>
<span class="nt"><exclusion></span>
<span class="nt"><groupid></span>xml-apis<span class="nt"></groupid></span>
<span class="nt"><artifactid></span>xml-apis<span class="nt"></artifactid></span>
<span class="nt"></exclusion></span>
<span class="nt"></exclusions></span>
<span class="nt"></dependency></span>
</code></pre></div></div>
Инсталляция deb пакетов в Ubuntu2015-03-19T00:00:00+00:00http://mrdekk.ru/2015/03/19/ubuntu-deb-installation<p>Если у вас есть .deb пакет и вы не знаете как его инсталлировать, то вот рецепт</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>dpkg <span class="nt">-i</span> software.deb
</code></pre></div></div>
<p>Удалить</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>dpkg <span class="nt">-r</span> software.deb
</code></pre></div></div>
Сброс состояния репозиториев в Ubuntu2015-03-17T00:00:00+00:00http://mrdekk.ru/2015/03/17/ubuntu-repo-state-reset<p>Когда бубунта начинает выдавать что-то вроде «software has no installation candidate» сие означает, что у нее что-то не так с репозиториями, чтобы сие поправить делаем так:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo</span> <span class="nt">-i</span>
<span class="nv">$ </span>apt-get clean
<span class="nv">$ </span><span class="nb">cd</span> /var/lib/apt
<span class="nv">$ </span>mv lists lists.old
<span class="nv">$ </span>mkdir <span class="nt">-p</span> lists/partial
<span class="nv">$ </span>apt-get clean
<span class="nv">$ </span>apt-key update
<span class="nv">$ </span>apt-get update
</code></pre></div></div>
Git Orphan Branch2015-03-17T00:00:00+00:00http://mrdekk.ru/2015/03/17/git-orphan-branch<p>Иногда необходимо в GIT создать ветку, не имеющую ничего общего с уже существующими (orphan branch). Как правило, такая необходимость возникает при размещении в одном репозитории нескольких проектов.</p>
<p>Делается это так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git checkout <span class="nt">--orphan</span> newbranch
<span class="nv">$ </span>git rm <span class="nt">-rf</span> <span class="nb">.</span>
<span class="c"># do work</span>
<span class="nv">$ </span>git add your files
<span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s1">'Initial commit'</span>
</code></pre></div></div>
Windows создать рандомный тестовый файл2015-02-27T00:00:00+00:00http://mrdekk.ru/2015/02/27/windows-random-file<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>fsutil file createnew <span class="s2">"filename"</span> <span class="s2">"length"</span>
</code></pre></div></div>
Интересный момент2015-02-20T00:00:00+00:00http://mrdekk.ru/2015/02/20/arm-interesting-moment<p>Сразу скажу – не моё – взял <a href="https://closedcircles.com/">тут</a> тут для того, чтоб не забыть.</p>
<p>На ARM (который ARM cannot into integer division) операция</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">hash</span> <span class="o">=</span> <span class="n">key</span> <span class="o">%</span> <span class="n">array</span><span class="p">.</span><span class="n">GetSize</span><span class="p">(</span> <span class="p">)</span>
</code></pre></div></div>
<p>Лучше сделать размер – степень двойки и заменить код на</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">hash</span> <span class="o">=</span> <span class="n">key</span> <span class="o">&</span> <span class="p">(</span> <span class="n">array</span><span class="p">.</span><span class="n">GetSize</span><span class="p">(</span> <span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="p">);</span>
</code></pre></div></div>
<p>Так как из-за того, что ARM cannot into integer division компилятор вставляет интринсик, который делает деление софтварно</p>
IGMPv3 на Windows 7, 8.12014-12-24T00:00:00+00:00http://mrdekk.ru/2014/12/24/igmpv3-windows7-81<p>Для работы с биржей ММВБ по протоколу FAST потребовалось использовать протокол IGMPv3 (биржа с v2 не работает), однако винда хоть убей посылает IGMPv2 и все.</p>
<p>Единственное что можно сделать, прописать в реестре здесь:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Params
</code></pre></div></div>
<p>ключ</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>IGMPVersion DWORD 4
</code></pre></div></div>
<p>и тогда вроде должно заработать</p>
MongoDB, обновить запись на основе её же полей2014-11-28T00:00:00+00:00http://mrdekk.ru/2014/11/28/mongodb-update-record-inplace<p>Случилась тут необходимость, обновить в mongodb некоторые записи, на основе их же полей – фактически разбить одно поле на два. StackOverflow дал одну идею, которую я решил законспектировать на будущее</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">db</span><span class="p">.</span><span class="nx">person</span><span class="p">.</span><span class="nx">find</span><span class="p">().</span><span class="nx">forEach</span><span class="p">(</span> <span class="kd">function</span> <span class="p">(</span> <span class="nx">elem</span> <span class="p">)</span>
<span class="p">{</span>
<span class="nx">db</span><span class="p">.</span><span class="nx">person</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span>
<span class="p">{</span>
<span class="na">_id</span><span class="p">:</span> <span class="nx">elem</span><span class="p">.</span><span class="nx">_id</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="na">$set</span><span class="p">:</span>
<span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="nx">elem</span><span class="p">.</span><span class="nx">firstname</span> <span class="o">+</span> <span class="s1">' '</span> <span class="o">+</span> <span class="nx">elem</span><span class="p">.</span><span class="nx">lastname</span>
<span class="p">}</span>
<span class="p">}</span> <span class="p">);</span>
<span class="p">}</span> <span class="p">);</span>
</code></pre></div></div>
The Reactive Manifesto2014-10-17T00:00:00+00:00http://mrdekk.ru/2014/10/17/reactive-manifesto-rus<p>Натолкнулся тут на <a href="http://www.reactivemanifesto.org/">интересный манифест</a> по принципам разработки больших систем. Потрудился его перевести и представляю его здесь. Если вы владеете английским языком – то лучше почитайте его в оригинале. Для всех остальных, перевод ниже.</p>
<p>Организации, работающие в разных областях деятельности независимо находят сходные паттерны построения информационных систем. Системы, построенные на этих паттернах, обладают большей надежностью, более живучи, более гибкие и лучше позиционируются для решения современных задач.</p>
<p>Это происходит потому, что требования к приложениям радикально изменились в последние годы. Всего лишь несколько лет назад большое приложение могло состоять из пары десятков серверов, время ответа измерялось в секундах, время технических работ могло измеряться часами а данные умещались в несколько гигабайт. Сегодня приложения развернуты везде, где только можно – от мобильных устройств до облачных кластеров с тысячами многоядерных процессоров. Пользователи же ожидают мгновенного ответа от сервера и 100% аптайма. Данные теперь измеряются в петабайтах. Запросы сегодняшнего дня просто не могут быть удовлетворены вчерашними архитектурами.</p>
<p>Мы верим, что требуется сбалансированный подход к архитектуре систем, и мы верим, что все необходимые аспекты уже открыты порознь: нам требуются системы, которые обладают качествами отзывчивости (Responsive), живучи (Resilient), гибкие (Elastic) и основываются на сообщения (Message Driven). Мы называем такие системы «Реактивными» (Reactive Systems).</p>
<p>Такие системы более гибкие, менее связные и масштабируемые. Это упрощает их разработку и развитие. Они более устойчивы к нештатным ситуациям, а когда что-то непредвиденное все-таки случаются, они легко восстанавливаются а не приводят к катастрофе. Реактивные системы обеспечивают пользователю эффективную интерактивную обратную связь.</p>
<h3 id="Реактивные-системы">Реактивные системы:</h3>
<p><strong>Отзывчивы (Responsive):</strong> Система отвечает настолько быстро, насколько это возможно. Отзывчивость – это краеугольный камень удобных и полезных в использовании систем, но кроме этого, отзывчивость также означает то, что проблемы могут быть быстро диагностированы и эффективно устранены. Отзывчивые системы сфокусированы на обеспечении быстрого и логичного ответа, тем самым формируя верхнюю границу для качества обслуживания. Согласованное ответ в свою очередь упрощает обработку ошибок, сборку и интерфейс пользователя, поощряет дальнейшее взаимодействие.</p>
<p><strong>Живучи (Resilient):</strong> Система остается отзывчивой даже в случае отказа. Это правило применяется не только для высокодоступных критичных систем, любая «неживучая» система при сбое станет неотзывчивой. Живучесть обеспечивается репликацией, включением (containment), изоляцией и делегированием. Сбои могут случиться в любом компоненте системы, поэтому изоляция компонентов друг от друга позволяет всей системе оставаться в работоспособном состоянии, даже в случае когда произошел сбой в отдельном компоненте и происходит операция восстановления. Восстановление компонента делегируется другому (внешнему) компоненту, а высокая доступность обеспечевается через резервирование там, где необходимо. Клиент компонента не должен задумываться о том, как действовать в случае его отказа.</p>
<p><strong>Гибки (Elastic):</strong> Система должна оставаться отзывчивой под различными нагрузками. Реактивные системы должны реагировать на изменение нагрузки увеличением или уменьшением задействованных ресурсов. Это в свою очередь требует отсутствия ключевых точек или «бутылочных горлышек», что позволяет распределять (shard) или реплицировать (replicate) компоненты, и распределять нагрузку между ними. Реактивные системы должны быть предсказуемыми, алгоритмы масштабирования должны предоставлять все необходимые показатели работоспособности. Такие системы обеспечивают экономию при работе на обычном (не специальном) оборудовании и программных платформах.</p>
<p><strong>Основываются на сообщениях (Message Driven):</strong> Реактивные системы основываются на асинхронном обмене сообщениями для обеспечения слабой связности между программными компонентами, изоляции, прозрачности местоположения и дают инструменты для делегирования обработки ошибок через ошибочные сообщения. Благодаря явному обмену сообщениями становится возможным организация балансировки нагрузки, эластичности и управления потоком путем мониторинга и управления потоком очередей сообщений. Прозрачность местоположения сообщений как параметр коммуникации позволяет управлять сбоями одними и теми же методами, вне зависимости от того, работает система на кластере или на отдельном хосте. Неблокирующее взаимодействие позволяет потреблять ресурсы только в периоды активности, что в свою очередь ведет к меньшей перегрузке системы.</p>
<p><img src="/media/images/reactive-traits.svg" alt="traits" /></p>
<p>Большие системы состоят из маленьких и поэтому зависят от реактивных свойств своих составляющих. Если система построена на реактивных принципах, то это означает что эти принципы применяются на всех уровнях построяния этой системы. Самые большие системы в мире основываются на этих принципах и служат целям миллионов людей по всему миру ежедневно. Пришло время применять эти принципы с самого начала разработки систем, вместо того, чтобы каждый раз открывать их снова.</p>
<p>Подписать манифест можно <a href="http://www.reactivemanifesto.org/">тут</a>.</p>
Об RAII замолвите слово2014-10-02T00:00:00+00:00http://mrdekk.ru/2014/10/02/a-word-on-raii<p>Давненько я сюда не писал ничего технического, настало время заполнить сий пробел. Считаю важным зафиксировать здесь несколько заметок о технологии RAII (Resource Acquisition Is Initialization, получение ресурса есть инициализация). Еще информацию об этой технологии на просторах англоязычного интернета можно найти по названию Scoped-Based Resource Management (SBRM), по русски это будет звучать как-то так – управление ресурсами на основе области видимости.</p>
<p>Технология RAII достаточно часто используется и является важной технологией управления временем жизни ресурсов. Управление осуществляется с использованием вспомогательного объекта, в котором ресурс выделяется в конструкторе, а освобождается в деструкторе.</p>
<p>Давайте проиллюстрируем важность и удобство техники на примере.</p>
<p>Например, у нас есть функция, которая получает мьютекс в начале и освобождает его в конце:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">foo</span><span class="p">(</span> <span class="n">Mutex</span><span class="o">&</span> <span class="n">mutex</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">mutex</span><span class="p">.</span><span class="n">acquire</span><span class="p">(</span> <span class="p">);</span>
<span class="c1">// выполняем какой-нибудь код
</span>
<span class="n">mutex</span><span class="p">.</span><span class="n">release</span><span class="p">(</span> <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Позже, может понадобится добавить досрочный выход из функции по какому-нибудь условию. Пока функция небольшая по размерам, не проблема увидеть, что функция получается мьютекс и освобождает его в конце, поэтому необходимо добавить освобождение мьютекса при досрочном выходе:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">foo</span><span class="p">(</span> <span class="n">Mutex</span><span class="o">&</span> <span class="n">mutex</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">mutex</span><span class="p">.</span><span class="n">acquire</span><span class="p">(</span> <span class="p">);</span>
<span class="c1">// выполняем какой-нибудь код
</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">shoudExit</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// неплохо бы сделать следующее
</span> <span class="n">mutex</span><span class="p">.</span><span class="n">release</span><span class="p">(</span> <span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// выполняем какой-нибудь код
</span>
<span class="n">mutex</span><span class="p">.</span><span class="n">release</span><span class="p">(</span> <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>По прошествии длительного времени (год, например) функция разрастается и начинает занимать много строк кода. В процессе роста было добавлено много досрочных выходов, но освобождение мьютекса всегда добавлялось. Однажды, над кодом начинает работать новый человек, он решает добавить очередной досрочный выход… Из-за больших размеров функции мьютекса он может и не увидеть:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">foo</span><span class="p">(</span> <span class="n">Mutex</span><span class="o">&</span> <span class="n">mutex</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">mutex</span><span class="p">.</span><span class="n">acquire</span><span class="p">(</span> <span class="p">);</span>
<span class="c1">// очень много строк кода
</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">newShouldExit</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// опаньки ...
</span> <span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// очень много строк кода
</span>
<span class="n">mutex</span><span class="p">.</span><span class="n">release</span><span class="p">(</span> <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Из примера видно, что такая техника управления ресурсами очень чувствительна к человеческой ошибке. Поэтому в таких случаях целесообразно применять RAII. Согласно стандарту С++ если мы определяем объект на стеке, его конструктор всегда будет выполнен в процессе инициализации, а деструктор в момент выхода из зоны видимости, то есть при return. Поэтому давайте использовать это соглашение в своих целях для управления ресурсами. Для начала напишем вспомогательный класс для автоматического получения и освобождения мьютекса:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">MutexLock</span>
<span class="p">{</span>
<span class="k">public</span><span class="o">:</span>
<span class="n">MutexLock</span><span class="p">(</span> <span class="n">Mutex</span><span class="o">&</span> <span class="n">mutex</span> <span class="p">)</span>
<span class="o">:</span> <span class="n">_mutex</span><span class="p">(</span> <span class="n">mutex</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">_mutex</span><span class="p">.</span><span class="n">acquire</span><span class="p">(</span> <span class="p">);</span>
<span class="p">}</span>
<span class="o">~</span><span class="n">MutexLock</span><span class="p">(</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">_mutex</span><span class="p">.</span><span class="n">release</span><span class="p">(</span> <span class="p">);</span>
<span class="p">}</span>
<span class="k">private</span><span class="o">:</span>
<span class="n">Mutex</span><span class="o">&</span> <span class="n">_mutex</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Все что требуется – это получить мьютекс в начале нашей функции, и нет больше нужды беспокоиться об его освобождении перед каждым возвратом из функции, потому что об освобождении позаботится деструктор:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">foo</span><span class="p">(</span> <span class="n">Mutex</span><span class="o">&</span> <span class="n">mutex</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">MutexLock</span> <span class="n">lock</span><span class="p">(</span> <span class="n">mutex</span> <span class="p">);</span>
<span class="c1">// очень много строк кода
</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">newShouldExit</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// теперь нет необходимости освобождать мьютекс здесь
</span> <span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// очень много строк кода
</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">shouldExit</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// и здесь тоже не надо
</span> <span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// очень много строка кода
</span>
<span class="c1">// и в конце также не надо
</span><span class="p">}</span>
</code></pre></div></div>
<p>Эту технику можно использовать в разных сценариях. Например, если в начале функции выделяется память из кучи, и требуется, чтобы память освобождалась в момент выхода из функции, можно использовать умные указатели STL. Ниже приведен скелет реализации умного указателя с использованием обозначенной техники:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">template</span> <span class="o"><</span> <span class="k">typename</span> <span class="n">T</span> <span class="o">></span>
<span class="k">class</span> <span class="nc">SmartPointer</span>
<span class="p">{</span>
<span class="k">public</span><span class="o">:</span>
<span class="n">SmartPointer</span><span class="p">(</span> <span class="n">T</span><span class="o">*</span> <span class="n">ptr</span> <span class="p">)</span>
<span class="o">:</span> <span class="n">_ptr</span><span class="p">(</span> <span class="n">ptr</span> <span class="p">)</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="o">~</span><span class="n">SmartPointer</span><span class="p">(</span> <span class="p">)</span>
<span class="p">{</span>
<span class="k">delete</span> <span class="n">_ptr</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">T</span><span class="o">&</span> <span class="k">operator</span> <span class="o">*</span> <span class="p">(</span> <span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="o">*</span><span class="n">_ptr</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">T</span><span class="o">*</span> <span class="k">operator</span> <span class="o">-></span> <span class="p">(</span> <span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_ptr</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">private</span><span class="o">:</span>
<span class="n">T</span><span class="o">*</span> <span class="n">_ptr</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Наша функция в свою очередь будет выглядеть так:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">foo</span><span class="p">(</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// выделяем память
</span> <span class="n">SmartPointer</span> <span class="n">ptr</span><span class="p">(</span> <span class="k">new</span> <span class="n">MyClass</span><span class="p">(</span> <span class="p">)</span> <span class="p">);</span>
<span class="c1">// очень много строк кода
</span>
<span class="c1">// это работает правильно, т.к. мы переопределили оператор ->
</span> <span class="n">ptr</span><span class="o">-></span><span class="n">DoSomething</span><span class="p">(</span> <span class="p">);</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">shouldExit</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// память автоматически очищается в деструкторе
</span> <span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// очень много строк кода
</span>
<span class="c1">// память автоматически очищается здесь тоже
</span><span class="p">}</span>
</code></pre></div></div>
<p>С памятью есть еще один интересный пример. Иногда требуется неким образом занять всю доступную память при запуске. Далее мы будем использовать эту память по своему усмотрению с помощью технологии placement new. Соответственно куски такой «сырой» превыделенной памяти мы назовем контекстами памяти. Соответственно мы имеем глобальный набор таких контекстов, после чего мы решаем, из какого контекста будет возвращен указатель на «выделенную» память.</p>
<p>Если дальше необходимо использовать какой-то контекст в рамках области видимости функции, то опять наблюдается уже знакомый паттерн:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">MemoryChunkUse</span>
<span class="p">{</span>
<span class="k">public</span><span class="o">:</span>
<span class="n">MemoryChunkUse</span><span class="p">(</span> <span class="n">MemoryChunk</span><span class="o">&</span> <span class="n">chunk</span> <span class="p">)</span>
<span class="o">:</span> <span class="n">_chunk</span><span class="p">(</span> <span class="n">chunk</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">UseMemoryChunk</span><span class="p">(</span> <span class="n">chunk</span> <span class="p">);</span>
<span class="p">}</span>
<span class="o">~</span><span class="n">MemoryChunkUse</span><span class="p">(</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">ReleaseMemoryChunk</span><span class="p">(</span> <span class="n">chunk</span> <span class="p">);</span>
<span class="p">}</span>
<span class="k">private</span><span class="o">:</span>
<span class="n">MemoryChunk</span><span class="o">&</span> <span class="n">_chunk</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Функция будет выглядеть так:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">foo</span><span class="p">(</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// используем контекст
</span> <span class="n">MemoryChunkUse</span> <span class="n">useChunk</span><span class="p">(</span> <span class="n">GRAPHICS_MEMORY_CHUNK</span> <span class="p">);</span>
<span class="c1">// очень много строк кода
</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">shouldExit</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// здесь контекст памяти будет автоматически освобожден
</span> <span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// очень много строк кода
</span>
<span class="c1">// здесь контекст памяти также будет освобожден
</span><span class="p">}</span>
</code></pre></div></div>
<p>С помощью технологии RAII вы заставляете компилятор освобождать ресурсы за вас, причем практически бесплатно. Круто, не так ли?</p>
Выправляем буквы2014-05-23T00:00:00+00:00http://mrdekk.ru/2014/05/23/order-the-letters<p>После победного “да!” я решил нарисовать что-то посложнее. Например “пулѣ!” или “пПуУфФ” (который кстати показал всю ущербность расстановки букв). Однако не пулять, не пуф не дали правильного эффекта. Загрузив GIMP и сравнив то что получается в графическом редакторе стало ясно, что полный ахтунг. Увы, скринов не сделал поэтому не покажу. Но было не очень хорошо, или даже “очень не”.</p>
<p>Проблем оказалось несколько. Первая состояла в том, что текстурные координаты я назначаю не совсем правильно. Фактически либо портилось форматное соотношение буквы, либо, если следовать размерам глифа из шрифта не получался мягкий контур, либо нарушался отступ между буквами. Вторая проблема заключалась в неправильной задаче вершинных координат, которые, вкупе с ошибкой в текстурных, приводили к огромному отступу между буквами (кстати, на скринах с “да!” предыдущего поста эти отступы хорошо видны).</p>
<p>Вооружившись листочком я начал искать корень зла (да, кстати он равен примерно 28). В итоге нашел. Текстурные координаты надо выбирать так, чтобы кроме глифа в нужный прямоугольник влезло еще немного пустого пространства (благо метрики шрифта это вполне позволяют), тогда не будет обрезок. Но самое главное, этот запас также надо проецировать и на вершинные координаты. Упражнения с математикой дали результат. Буквы нарисовались правильно, даже сравнение в GIMP’е это подтвердило. Но появилась новая проблема, хотя и понятная сразу – z-fighting. Ввиду дополнительного допуска, прямоугольники букв стали перекрываться. Но z-fighting в этом случае решается быстро – достаточно развести буквы по глубине и вуаля – z-fighting побежден. Кстати, для экранных координат оказалось достаточно значения 0.0001, для мировых – 0.001.</p>
<p>Ну и напоследок скрин. Белые буквы – то что получается в движке, красные (с 50% прозрачностью) наложены в GIMP’е – для сравнения. По моему – отлично!</p>
<p><img src="/media/images/puf.jpg" alt="puf" /></p>
Да, да и еще раз да!2014-05-20T00:00:00+00:00http://mrdekk.ru/2014/05/20/yes-yes-and-again-yes<p>После того, как успешно получилось нарисовать строчку “да!” следующим шагом стало включение отрисовки шрифтов в конвейер. Причем задачу я поставил такую, чтобы отрисовка 2D (в экранных координатах) и 3D (в мировых) выполнялось как можно более идентичным кодом, а еще лучше – одним и тем же с парой настроек. Пришлось немного доработать конвейер и реализовать таки 2D камеру (до этого была только 3d, а 2d интерфейс рисовался с помощью пары хаков в рендере). Но в итоге результат превзошел все ожидания. Шейдеры (и пиксельный, и вершинный) – абсолютно одни и те же. Код объекта отрисовки (render entry) практически одинаковый, с одной лишь выборкой – брать матрицу проекции-вида из 2d или 3d камеры. Кроме того, благодаря такому объединению практически бесплатно получился еще и billboarding.</p>
<p>А теперь картинки, в нижнем углу маленькими буквами “да!” в экранных координатах. Справа – “да!” в мировых координатах, слева – “да!” в мировых координатах с применением технологии billboarding. Биллбоард однозначно видно по мере смещения и поворота камеры.</p>
<p><img src="/media/images/da1.jpg" alt="da1" /></p>
<p><img src="/media/images/da2.jpg" alt="da2" /></p>
<p><img src="/media/images/da3.jpg" alt="da3" /></p>
<p>P.S. К слову, полученные алгоритмы и шейдеры впоследствии пригодятся когда буду реализовывать системы частиц (Particle Systems)</p>
Ну-ка буквы встаньте в ряд!2014-05-14T00:00:00+00:00http://mrdekk.ru/2014/05/14/letters-stands-the-row<p>Просто сказать, но не так просто сделать. В ходе попыток сделать вывод строк, обнаружилось, что во-первых, не все нужные параметры выгружались, во-вторых, центрирование букв в текстуре привело к забавным эффектам (один только восклицательный знак толщиной в три пикселя чего стоит), в-третьих, расчет текстурных координат для шейдера надо проводить с учетом форматного соотношения сторон буквы, а не просто по краю канвы.</p>
<p>Но все проблемы были таки решены и сегодня показываю рендер строчек, пока рисуем только “да!”, отчасти от того, что конвейер движка еще не совсем готов рисовать буквы шейдерами, но я двигаюсь в этом направлении. Как только появится возможность рендерить строчки любые, нарисую что-нибудь посложнее и по длиннее. Пока же, только “да!”.</p>
<p>Кстати, выяснилось, что придется реализовывать traits для STL’евской basic_string чтобы она могла поддерживать правильно мой тип unichar (typedef unsigned long). Но об этом в другой раз.</p>
<p>И еще, то что буквы рисуются в “мировых” координатах – это я так захотел. При желании можно сделать так чтоб строки стали биллбордами, или же рисовать в экранных координатах. Все зависит только от матрицы проекции-вида. Здесь она для мира.</p>
<p>А теперь обещанные рендеры:</p>
<p><img src="/media/images/str_0.jpg" alt="str_0" /></p>
<p><img src="/media/images/str_1.jpg" alt="str_1" /></p>
<p><img src="/media/images/str_2.jpg" alt="str_2" /></p>
<p><img src="/media/images/str_3.jpg" alt="str_3" /></p>
Продолжаем рисовать буковки, теперь и Юникод!2014-05-03T00:00:00+00:00http://mrdekk.ru/2014/05/03/letters-with-unicode<p>После того, как удалось нарисовать букву “g”, следующим логичным шагом было бы нарисовать буквы русские, т.е. Юникод. Однако возникает одна небольшая проблема. Имя ей – UTF-8, а точнее – отсутствие встроенных в С++ нормальных механизмов работы с unicode (wchar_t не в счет – это не совсем Юникод). Пока для целей тестирования были взяты определенные коды символов (буквы ё и ѣ), получены их utf-8 коды (0xd1 0×91 и 0xd1 0xa3 соответственно) и уже по этим кодам рассчитаны необходимые данные.</p>
<p>В дальнейшем же при визуализации текста стоит решить, какого подхода придерживаться. Можно использовать UTF-8 и const char<em>, либо wchar_t. Первый вариант дает более компактное представление, но функции strcmp, strlen и т.д. будут работать неправильно. С точки же зрения wchar_t функции будут работать правильно, но в Windows sizeof(wchar_t) = 2, что не совсем Юникод, и кроме того, например, китайские символы – это три байта даже в UTF-8. Поэтому хочется const char</em> и UTF-8, посмотрим что получится.</p>
<p>А теперь что получилось, буквы взяты специально экзотические</p>
<p>“ё”</p>
<p><img src="/media/images/bukva-Io.jpg" alt="bukva-Io" /></p>
<p>“ѣ” (ять, да-да тот самый ять :) )</p>
<p><img src="/media/images/bukva-Yat.jpg" alt="bukva-Yat" /></p>
Неожиданная удача, глиф “g” на шейдерах!2014-04-30T00:00:00+00:00http://mrdekk.ru/2014/04/30/bonanza-glyph-g-on-shaders<p>У меня таки получилось победить артефакты в рендерах, которые я приводил раньше. Однако это потребовало вычисления в шейдере факта попадания точки в контур глифа. Все это привело к ужасающему падению производительности. Т.к. все кривые глифа задавались аналитически и шейдеру приходилось все это рассчитывать в реальном времени.</p>
<p>В качестве оптимизации можно было бы конечно один раз глиф отрисовать в рендер-таргет, а потом просто блит, но это равносильно предрасчету глифов и просто блиту, разве что не во время разработки, а при старте.</p>
<p>Поэтому аналитическую информацию решено было предрасчитать и “запечь” в текстуру, а потом ей только пользоваться. С учетом фильтрации текстур еще и практически бесплатная интерполяция. С помощью библиотечки Cairo удалось просчитать нужную аналитическую информацию и быренько наваять шейдер. Вот что получилось.</p>
<p>Буква “g” при разных приближениях:</p>
<p><img src="/media/images/g-1.jpg" alt="g-1" /></p>
<p><img src="/media/images/g-2.jpg" alt="g-2" /></p>
<p><img src="/media/images/g-3.jpg" alt="g-3" /></p>
<p><img src="/media/images/g-4.jpg" alt="g-4" /></p>
<p>Как видно контуры плавные даже при огромном увеличении. Задача минимум (а изначально это вообще была задача максимум) достигнуто. Но дальше, как говориться, “Остапа понесло”.</p>
<p>Контур (красный), кроме того еще и плавный:</p>
<p><img src="/media/images/g-glow.jpg" alt="g-glow" /></p>
<p>Тень (синяя), тоже плавная:</p>
<p><img src="/media/images/g-shadow.jpg" alt="g-shadow" /></p>
<p>В принципе и тень и контур можно делать жесткими. Но плавными они эффектнее смотряться.</p>
<p>Ну и как обычно – чайник для антуража, плюс в этот раз еще и цветной куб. Так – эксперименты…</p>
Ох уж этот Cairo!!!2014-04-29T00:00:00+00:00http://mrdekk.ru/2014/04/29/oh-my-cairo<p>Небольшая заметка. Может сэкономить кому-нибудь кучу времени. Понадобилось тут воспользоваться библиотекой Cairo, да еще чтоб png-шки с прозрачностью генерировать. Так вот, библиотека Cairo позволяет в формате ARGB32 использовать альфу, но только когда вы устанавливаете пиксель, то его нужно умножить на альфу. Иначе не получается.</p>
<p>Т.е. 50% красного будет выглядеть как 0×80800000 а не привычные 0x80ff0000</p>
Первые рендеры шрифтов на шейдерах2014-04-25T00:00:00+00:00http://mrdekk.ru/2014/04/25/fonts-on-shaders-first-renders<p>Продолжаем работу над реализацией рендеринга шрифтов на шейдерах в движке. Удалось наладить экспорт глифов шрифта из SVG (который получается из TTF) в нужном формате. С учетом преобразования кривых Безье в дуги окружности. Сегодня наладил импорт нужного формата в движок и рендер на шейдерах. Пока правда много багов, но даже первые результаты вполне обнадеживают.</p>
<p>На картинках – красной каемкой обозначена область рендеринга глифа (простой прямоугольник из четырех вершин).</p>
<p>Звездочка, имеются артефакты, вызванные расчетом. В принципе чинятся – надо немного подтюнить математику:</p>
<p><img src="/media/images/shader_text_1.jpg" alt="shader_text_1" /></p>
<p>Буква М (перевернута, т.к. сама область рендеринга перевернута – ура 3D преобразованиям). Артефактов практически нет:</p>
<p><img src="/media/images/shader_text_2.jpg" alt="shader_text_2" /></p>
<p>А вот знак доллара малость подкачал. Где-то глобальная ошибка в математике. Скорее всего в преобразованиях:</p>
<p><img src="/media/images/shader_text_3.jpg" alt="shader_text_3" /></p>
<p>Чайник на фоне – это так – для антуража =)</p>
Преобразование кривой Безье в набор дуг окружности2014-04-17T00:00:00+00:00http://mrdekk.ru/2014/04/17/bezier-to-arcs<p>Для реализации визуализации шрифтов на шейдерах в движке потребовалось преобразование кривой Безье (кубической, квадратичной) в набор дуг окружности. Долго штудировал математику, просмотрел даже некоторые забугорные whitepaper’s, но красивого решения не нашел.</p>
<p>Исходные данные, кубические кривые:</p>
<p>B = { ( 0, 0 ), ( 0.25, 0.25 ), ( 0.75, 0.75 ), ( 1, 0 ) }</p>
<p><img src="/media/images/b3_1.jpg" alt="b3_1" /></p>
<p>B = { ( 0, 0 ), ( 0.25, 0.75 ), ( 0.75, 0.25 ), ( 1, 1 ) }</p>
<p><img src="/media/images/b3_2.jpg" alt="b3_2" /></p>
<p>B = { ( 0.25, 0.25 ), ( 0.75, 0.75 ), ( 0.25, 0.5 ), ( 0.75, 0.25 ) }</p>
<p><img src="/media/images/b3_3.jpg" alt="b3_3" /></p>
<p>Квадратичные кривые:</p>
<p>B = { ( 0, 0 ), ( 0.75, 0.75 ), ( 1, 0 ) }</p>
<p><img src="/media/images/b2_1.jpg" alt="b2_1" /></p>
<p>B = { ( 0, 0 ), ( 0, 1 ), ( 1, 1 ) }</p>
<p><img src="/media/images/b2_2.jpg" alt="b2_2" /></p>
<p>B = { ( 0.25, 0.25 ), ( 0.75, 0.75 ), ( 0.45, 1 ) }</p>
<p><img src="/media/images/b2_3.jpg" alt="b2_3" /></p>
<p>В итоге был найден достаточно простой рекурсивный алгоритм, которые аппроксимирует кривые. В результате получены следующие картинки.</p>
<p>Кубические кривые (в порядке предоставления входных данных):</p>
<p><img src="/media/images/b3_1_r.jpg" alt="b3_1_r" /></p>
<p><img src="/media/images/b3_2_r.jpg" alt="b3_2_r" /></p>
<p><img src="/media/images/b3_3_r.jpg" alt="b3_3_r" /></p>
<p>Квадратичные кривая (в порядке представления входных данных):</p>
<p><img src="/media/images/b2_1_r.jpg" alt="b2_1_r" /></p>
<p><img src="/media/images/b2_2_r.jpg" alt="b2_2_r" /></p>
<p><img src="/media/images/b2_3_r.jpg" alt="b2_3_r" /></p>
<p>На каждую кривую получается в среднем 5-10 дуг окружностей. Вполне допустимо.</p>
Honda Quality2014-01-12T00:00:00+00:00http://mrdekk.ru/2014/01/12/honda-quality<p>Наткнулся вот на такую картинку. За последние 25 лет 75% машин производства Honda до сих пор ездят по дорогам. Это рекорд.</p>
<p><img src="/media/images/honda-quality.jpg" alt="honda quality" /></p>
Навеяно (англ.)2013-11-11T00:00:00+00:00http://mrdekk.ru/2013/11/11/data-thoughts<p>Наткнулся вот тут на такую мысль. Считаю важной.</p>
<blockquote>
<p>«The ability to take data – to be able to understand it, to process it, to extract value from it, to visualize it, to communicate it is going to be a hugely important skill in the next decades.»</p>
</blockquote>
<p>Автор: Hal Varian, Chief Economist, Google</p>
Eclipse RCP (4.3) приложение с использованием maven. Часть 4. Продукт2013-10-03T00:00:00+00:00http://mrdekk.ru/2013/10/03/eclipse-rcp-with-maven-part-4-product<p>Сегодня как и обещал, расскажу про то, как сделать готовый продукт. В следующих статьях, если они будут – расскажу про то, как подключить в Eclipse RCP обычные maven’овские зависимости.</p>
<p>Итак, открываем наши проекты в eclipse и делаем вот что. В проекте ru.mrdekk.ercp.gui есть файлик ru.mrdekk.ercp.gui.product. Так вот – этот файлик надо перенести в проект ru.mrdekk.ercp.repository:</p>
<p><img src="/media/images/ercp4_1.png" alt="move file" /></p>
<p>Необходимо сконфигурировать уровни запуска и параметры автостарта. Открываем файл продукта, который мы только что перенесли. Переходим на вкладку Configuration. В Start Levels … нажимаем кнопку Add …</p>
<p><img src="/media/images/ercp4_2.png" alt="configurate product" /></p>
<p>Далее в появившемся окне необходимо выбрать бандлы org.eclipse.equinox.common и org.eclipse.equinox.ds и нажать ОК</p>
<p><img src="/media/images/ercp4_3.png" alt="ok" /></p>
<p>Для обоих бандлов установить start level = 2 и auto start = true, далее таким же способом необходимо добавить бандл org.eclipse.core.runtime:</p>
<p><img src="/media/images/ercp4_4.png" alt="config" /></p>
<p>Для этого бандла необходимо установить только auto start = true. В итоге получим что-то вроде:</p>
<p><img src="/media/images/ercp4_5.png" alt="auto start" /></p>
<p>На вкладке Overview в поле ID введите ru.mrdekk.ercp.product:</p>
<p><img src="/media/images/ercp4_6.png" alt="product id" /></p>
<p>Измените «The product configuration is base on…» на features:</p>
<p><img src="/media/images/ercp4_7.png" alt="base on feature" /></p>
<p>На вкладке Dependencies нажмите кнопку Add …</p>
<p><img src="/media/images/ercp4_8.png" alt="add" /></p>
<p>И выберите org.eclipse.rcp:</p>
<p><img src="/media/images/ercp4_9.png" alt="rcp" /></p>
<p>Также нажмите кнопку Add Required … eclipse добавить требуемые зависимости:</p>
<p><img src="/media/images/ercp4_10.png" alt="deps" /></p>
<p>И конечно добавить нашу фичу:</p>
<p><img src="/media/images/ercp4_11.png" alt="add feature" /></p>
<p>Теперь запустим сборку проекта через maven, мы должны получить как обычно успешный результат:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[INFO] Reactor Summary:
[INFO]
[INFO] mrdekk.ru eclipse rcp demo - parent ............... SUCCESS [0.109s]
[INFO] mrdekk.ru eclipse rcp demo - gui .................. SUCCESS [2.875s]
[INFO] mrdekk.ru eclipse rcp demo - feature .............. SUCCESS [0.125s]
[INFO] mrdekk.ru eclipse rcp demo - update site .......... SUCCESS [5.531s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 16.734s
[INFO] Finished at: Thu Oct 03 11:12:56 NOVST 2013
[INFO] Final Memory: 16M/38M
[INFO] ------------------------------------------------------------------------
</code></pre></div></div>
<p>В папке target/repository мы должны увидеть метаданные и файлы для продукта:</p>
<p><img src="/media/images/ercp4_12.png" alt="see product" /></p>
<p>Теперь заставим maven собирать для нас пакет инсталляции продукта, и архивный файл. Для этого в pom.xml проекта ru.mrdekk.ercp.repository необходимо написать следующее:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><build></span>
<span class="nt"><plugins></span>
<span class="nt"><plugin></span>
<span class="nt"><groupId></span>org.eclipse.tycho<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>tycho-p2-director-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>${tycho-version}<span class="nt"></version></span>
<span class="nt"><executions></span>
<span class="nt"><execution></span>
<span class="c"><!-- install the product using the p2 director --></span>
<span class="nt"><id></span>materialize-products<span class="nt"></id></span>
<span class="nt"><goals></span>
<span class="nt"><goal></span>materialize-products<span class="nt"></goal></span>
<span class="nt"></goals></span>
<span class="nt"></execution></span>
<span class="nt"><execution></span>
<span class="c"><!-- create zip file with the installed product --></span>
<span class="nt"><id></span>archive-products<span class="nt"></id></span>
<span class="nt"><goals></span>
<span class="nt"><goal></span>archive-products<span class="nt"></goal></span>
<span class="nt"></goals></span>
<span class="nt"></execution></span>
<span class="nt"></executions></span>
<span class="nt"></plugin></span>
<span class="nt"></plugins></span>
<span class="nt"></build></span>
</code></pre></div></div>
<p>И запустите сборку снова, вы должны будете увидеть каталог продукта и архивный файл в папке target:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[INFO] Reactor Summary:
[INFO]
[INFO] mrdekk.ru eclipse rcp demo - parent ............... SUCCESS [0.094s]
[INFO] mrdekk.ru eclipse rcp demo - gui .................. SUCCESS [2.766s]
[INFO] mrdekk.ru eclipse rcp demo - feature .............. SUCCESS [0.109s]
[INFO] mrdekk.ru eclipse rcp demo - update site .......... SUCCESS [12.953s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 24.250s
[INFO] Finished at: Thu Oct 03 11:17:54 NOVST 2013
[INFO] Final Memory: 17M/42M
[INFO] ------------------------------------------------------------------------
</code></pre></div></div>
<p><img src="/media/images/ercp4_13.png" alt="hurray" /></p>
<p>Вы можете запустить полученный продукт, и увидите примерно следующее:</p>
<p><img src="/media/images/ercp4_14.png" alt="see" /></p>
<p>На этом основные статьи по Eclipse RCP закончены. Вы можете добавить этот проект например в Hudson и автоматически собирать его там. Я постараюсь сделать еще несколько статей, в которых расскажу про то, как подключать в Eclipse RCP проекты обычные maven’овские зависимости.</p>
<p>P.S. Исходники как всегда доступны в <a href="http://github.com/mrdekk/ercp">github</a></p>
Eclipse RCP (4.3) приложение с использованием maven. Часть 3. Сайт обновлений2013-09-27T00:00:00+00:00http://mrdekk.ru/2013/09/27/eclipse-rcp-with-maven-part-3-updatesite<p>Продолжаю повествование о том, как собрать Eclipse RCP проект с помощью maven’а. Сегодня поговорим о том, как сделать Update Site для нашего проекта. Update Site нужен для распространения компонентов Eclipse RCP и готовых приложений. Когда Вы устанавливаете что-то себе в Eclipse вы пользуетесь одним или несколькими Update Site’ами.</p>
<p>Итак, открываем наш проект.</p>
<p>Для начала создадим проект Update Site’а через стандартные функции Eclipse, для этого File > New > Other … > Plug-In Development > Update Site Project:</p>
<p><img src="/media/images/ercp3_1.png" alt="new update site project" /></p>
<p><img src="/media/images/ercp3_2.png" alt="new update site project" /></p>
<p>Eclipse создаст нам проект. Далее необходимо site.xml переименовать в category.xml. Для этого нужно кликнуть правой кнопкой мыши на этом файле и выбрать Refactor > Rename:</p>
<p><img src="/media/images/ercp3_3.png" alt="rename" /></p>
<p>Далее необходимо открыть полученный файл в Manifest Editor. В Eclipse есть бага, которая не позволяет сделать это простым двойным щелчком, поэтому нужно по правой кнопке выбрать Open With > Category Manifest Editor.</p>
<p>Далее создадим и отредактируем некоторые параметры в нашей категории:</p>
<p><img src="/media/images/ercp3_4.png" alt="add and edit params" /></p>
<p>Добавим ссылку на новую фичу (надо нажать на кнопку Add Feature). Выберем нашу фичу:</p>
<p><img src="/media/images/ercp3_5.png" alt="add feature" /></p>
<p><img src="/media/images/ercp3_6.png" alt="add feature" /></p>
<p>После этого, как обычно преобразуем полученный проект в проект maven. Для этого по правой кнопке Configure > Convert to Maven Project. Не забываем о том, что в настройках maven’а необходимо повторять те же идентификаторы. Тип проекта – eclipse-repository</p>
<p><img src="/media/images/ercp3_7.png" alt="convert to maven" /></p>
<p>После преобразования проекта в проект maven’а необходимо в pom.xml указать ссылку на родительский проект, после чего полученный pom.xml примет примерно следующий вид:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><project</span>
<span class="na">xmlns=</span><span class="s">"http://maven.apache.org/POM/4.0.0"</span>
<span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span class="na">xsi:schemaLocation=</span><span class="s">"http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"</span> <span class="nt">></span>
<span class="nt"><modelVersion></span>4.0.0<span class="nt"></modelVersion></span>
<span class="nt"><parent></span>
<span class="nt"><groupId></span>ru.mrdekk.ercp<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>ru.mrdekk.ercp.parent<span class="nt"></artifactId></span>
<span class="nt"><version></span>1.0.0-SNAPSHOT<span class="nt"></version></span>
<span class="nt"><relativePath></span>..<span class="nt"></relativePath></span>
<span class="nt"></parent></span>
<span class="nt"><artifactId></span>ru.mrdekk.ercp.repository<span class="nt"></artifactId></span>
<span class="nt"><packaging></span>eclipse-repository<span class="nt"></packaging></span>
<span class="nt"><name></span>mrdekk.ru eclipse rcp demo - update site<span class="nt"></name></span>
<span class="nt"></project></span>
</code></pre></div></div>
<p>И добавим полученный проект в качестве модуля в родительский pom.xml. Секция modules будет выглядеть следующим образом:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><modules></span>
<span class="nt"><module></span>gui<span class="nt"></module></span>
<span class="nt"><module></span>feature<span class="nt"></module></span>
<span class="nt"><module></span>repository<span class="nt"></module></span>
<span class="nt"></modules></span>
</code></pre></div></div>
<p>После чего все это дело надо сохранить и попробовать собрать через родительский проект maven’ом. Должно получится примерно следующее:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] mrdekk.ru eclipse rcp demo - parent ............... SUCCESS [0.094s]
[INFO] mrdekk.ru eclipse rcp demo - gui .................. SUCCESS [2.984s]
[INFO] mrdekk.ru eclipse rcp demo - feature .............. SUCCESS [0.109s]
[INFO] mrdekk.ru eclipse rcp demo - update site .......... SUCCESS [1.157s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.266s
[INFO] Finished at: Fri Sep 27 10:38:27 NOVST 2013
[INFO] Final Memory: 15M/37M
[INFO] ------------------------------------------------------------------------
</code></pre></div></div>
<p>Теперь протестируем полученный репозиторий, для этого надо через правую кнопку мыши зайти в свойства проекта папки target/repository проекта repository (Properties).</p>
<p><img src="/media/images/ercp3_8.1.png" alt="test repo" /></p>
<p>Затем выделить и сохранить в буфер обмена путь к полученному репозиторию:</p>
<p><img src="/media/images/ercp3_9.1.png" alt="save path" /></p>
<p>Теперь выберем в главном меню Help > Install New Software … Добавим новый Update Site (надо нажать кнопку Add):</p>
<p><img src="/media/images/ercp3_9.2.png" alt="add new site" /></p>
<p>Мы должны увидеть содержимое этого репозитория, т.е. нашу фичу:</p>
<p><img src="/media/images/ercp3_10.png" alt="our repo" /></p>
<p>Нажмите Cancel, ведь мы пока не собираемся устанавливать нашу фичу в Eclipse. Как обычно исходники доступны на github <a href="https://github.com/mrdekk/ercp">тут</a></p>
Eclipse RCP (4.3) приложение с использованием maven. Часть 2. Фича!2013-09-25T00:00:00+00:00http://mrdekk.ru/2013/09/25/eclipse-rcp-with-maven-part-2-feature<p>Добрый день, читатели!</p>
<p>Сегодня продолжил создавать приложение на основе Eclipse RCP. Сегодня сделаем фичу (feature). Фича – это компонент rcp который может быть отдельно установлен в Eclipse, в том числе в тот, под которым вы его разрабатываете. В дальнейшем нам этот компонент понадобится чтобы создать пакет приложения и сайт апдейтов.</p>
<p>Работу будем продолжать в уже созданном нами проекте. Создадим проект фичи, для этого File > New > Other … > Plug-in Development > Feature Project.</p>
<p><img src="/media/images/ercp2_1.png" alt="new feature project" /></p>
<p>Заполним необходимые поля. Обязательно убедитесь в том, что проект находится в каталоге родительского проекта в отдельной папке, в нашем случае это папка feature. Дальше мы будем использовать имя этой папки кое где.</p>
<p><img src="/media/images/ercp2_2.png" alt="new feature project" /></p>
<p>Выберем созданный нами в предыдущей статье плагин:</p>
<p><img src="/media/images/ercp2_3.png" alt="new feature project" /></p>
<p>Далее преобразуем полученный проект в проект Maven’а. Правой кнопкой мыши на проекте, Configure > Convert to Maven Project. Далее не забываем, что настройки артефакта maven’а должны совпадать с настройками в билде эклипса. Кроме того, обратите внимание на packaging type, он должен быть eclipse-feature.</p>
<p><img src="/media/images/ercp2_4.png" alt="convert to maven" /></p>
<p>pom.xml проекта должен выглядеть примерно следующим образом. Ссылку на родительский проект необходимо вписать руками.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><project</span>
<span class="na">xmlns=</span><span class="s">"http://maven.apache.org/POM/4.0.0"</span>
<span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span class="na">xsi:schemaLocation=</span><span class="s">"http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"</span> <span class="nt">></span>
<span class="nt"><modelVersion></span>4.0.0<span class="nt"></modelVersion></span>
<span class="nt"><parent></span>
<span class="nt"><groupId></span>ru.mrdekk.ercp<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>ru.mrdekk.ercp.parent<span class="nt"></artifactId></span>
<span class="nt"><version></span>1.0.0-SNAPSHOT<span class="nt"></version></span>
<span class="nt"><relativePath></span>..<span class="nt"></relativePath></span>
<span class="nt"></parent></span>
<span class="nt"><artifactId></span>ru.mrdekk.ercp.feature<span class="nt"></artifactId></span>
<span class="nt"><packaging></span>eclipse-feature<span class="nt"></packaging></span>
<span class="nt"><name></span>mrdekk.ru eclipse rcp demo - feature<span class="nt"></name></span>
<span class="nt"></project></span>
</code></pre></div></div>
<p>Теперь пропишем в родительском проекте этот проект в качестве модуля для того, чтобы он собирался при сборке всего проекта. Секция modules в родительском проекте должна выглядеть примерно следующим образом:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><modules></span>
<span class="nt"><module></span>gui<span class="nt"></module></span>
<span class="nt"><module></span>feature<span class="nt"></module></span>
<span class="nt"></modules></span>
</code></pre></div></div>
<p>После этого попробуем собрать проект. Должно получится примерно следующее:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] mrdekk.ru eclipse rcp demo - parent ............... SUCCESS [0.109s]
[INFO] mrdekk.ru eclipse rcp demo - gui .................. SUCCESS [2.953s]
[INFO] mrdekk.ru eclipse rcp demo - feature .............. SUCCESS [0.109s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.171s
[INFO] Finished at: Wed Sep 25 10:47:53 NOVST 2013
[INFO] Final Memory: 14M/35M
[INFO] ------------------------------------------------------------------------
</code></pre></div></div>
<p>В следующий раз настроим сборку продукта и сайта апдейтов.</p>
Eclipse RCP (4.3) приложение с использованием maven2013-09-23T00:00:00+00:00http://mrdekk.ru/2013/09/23/eclipse-rcp-with-maven-part-1<p>Итак. Намедни потребовалось создать eclipse rcp приложение на платформе Kepler. Положение осложнялось тем, что сборку надо было проводить с использованием maven’а. Ввиду того, что платформа kepler (4.3) достаточно новая и в ней было много всего переделано, информации по ней – кот наплакал. Поэтому пришлось подключать лом и чью-то матерь. Но в итоге все получилось. Методику этого получилось я и постараюсь описать тут.</p>
<p>Начнем с того, что у Вас должен быть установлен eclipse последней версии, в нем должен быть настроен maven и инструменты для разработки eclipse rcp. Я использовал для этого Spring ToolSuite, в котором мавен уже есть из коробки, а eclipse rcp tools легко ставятся через Install New Software. Методику этого всего я тут описывать не буду – материала навалом даже на русском языке.</p>
<p>Для начала создадим родительский проект для мавена – File > New > Maven Project.</p>
<p><img src="/media/images/ercp1_1.png" alt="file new maven project" /></p>
<p>Затем настроим основные вещи</p>
<p><img src="/media/images/ercp1_2.png" alt="basic setup" /></p>
<p>pom.xml будет выглядеть следующим образом</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><project</span>
<span class="na">xmlns=</span><span class="s">"http://maven.apache.org/POM/4.0.0"</span>
<span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span class="na">xsi:schemaLocation=</span><span class="s">"http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"</span> <span class="nt">></span>
<span class="nt"><modelVersion></span>4.0.0<span class="nt"></modelVersion></span>
<span class="nt"><groupId></span>ru.mrdekk.ercp<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>ru.mrdekk.ercp.parent<span class="nt"></artifactId></span>
<span class="nt"><version></span>1.0.0-SNAPSHOT<span class="nt"></version></span>
<span class="nt"><packaging></span>pom<span class="nt"></packaging></span>
<span class="nt"><name></span>mrdekk.ru eclipse rcp demo - parent<span class="nt"></name></span>
<span class="nt"></project></span>
</code></pre></div></div>
<p>Для дальнейшего прогресса необходимо добавить некоторые вещи. Для начала – настройки, пока только версия tycho – инструмента для сборки rcp проектов maven’ом. Все это добавляем в pom.xml:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><properties></span>
<span class="nt"><tycho-version></span>0.18.1<span class="nt"></tycho-version></span>
<span class="nt"></properties></span>
</code></pre></div></div>
<p>Теперь добавим репозиторий eclipse rcp который нужен будет maven’у и tycho для получения артефактов:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><repositories></span>
<span class="nt"><repository></span>
<span class="nt"><id></span>eclipse-platform<span class="nt"></id></span>
<span class="nt"><layout></span>p2<span class="nt"></layout></span>
<span class="nt"><url></span>http://download.eclipse.org/eclipse/updates/4.3/R-4.3-201306052000<span class="nt"></url></span>
<span class="nt"></repository></span>
<span class="nt"></repositories></span>
</code></pre></div></div>
<p>Добавляем maven’овский плагин tycho в сборку:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><build></span>
<span class="nt"><plugins></span>
<span class="nt"><plugin></span>
<span class="nt"><groupId></span>org.eclipse.tycho<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>tycho-maven-plugin<span class="nt"></artifactId></span>
<span class="nt"><version></span>${tycho-version}<span class="nt"></version></span>
<span class="nt"><extensions></span>true<span class="nt"></extensions></span>
<span class="nt"></plugin></span>
<span class="nt"></plugins></span>
<span class="nt"></build></span>
</code></pre></div></div>
<p>C родительским проектом пока все, теперь создаем непосредственно проект eclipse rcp – File > New > Project … > Eclipse 4 > Eclipse 4 Application Project:</p>
<p><img src="/media/images/ercp1_3.png" alt="new eclipse rcp project" /></p>
<p><img src="/media/images/ercp1_4.png" alt="new eclipse rcp project" /></p>
<p><img src="/media/images/ercp1_5.png" alt="new eclipse rcp project" /></p>
<p><img src="/media/images/ercp1_6.png" alt="new eclipse rcp project" /></p>
<p>Запустим приложение для проверки работоспособности, для этого:</p>
<ol>
<li>Открываем файл определения продукта – ru.mrdekk.ercp.gui.product</li>
<li>На вкладке «Overview» нажмите «Launch an Eclipse application».</li>
</ol>
<p>Вы должны увидеть примерно следующее:</p>
<p><img src="/media/images/ercp1_7.png" alt="rcp sample launch" /></p>
<p>Теперь преобразуем проект в maven’овский проект. Для этого нажимаем правой кнопкой мыши на проекте и выбираем Configure > Convert to Maven Project. Появится следующее окошко, в нем вбиваем нужные данные. Внимание! Важно чтобы эти данные совпадали с тем, что Вы ввели когда создавали проект, т.е.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MANIFEST/Bundle-SymbolicName == POM/artifactId
</code></pre></div></div>
<p>и</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MANIFEST/.qualifier == POM/-SNAPSHOT!
</code></pre></div></div>
<p><img src="/media/images/ercp1_8.png" alt="convert to maven" /></p>
<p>Eclipse будет ругаться на полученный pom, т.к. он не увидит tycho. Чтобы все получилось, необходимо указать ссылку на созданный нами родительский проект, Ваш pom.xml должен выглядеть примерно следующим образом:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><project</span>
<span class="na">xmlns=</span><span class="s">"http://maven.apache.org/POM/4.0.0"</span>
<span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span class="na">xsi:schemaLocation=</span><span class="s">"http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"</span> <span class="nt">></span>
<span class="nt"><modelVersion></span>4.0.0<span class="nt"></modelVersion></span>
<span class="nt"><parent></span>
<span class="nt"><groupId></span>ru.mrdekk.ercp<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>ru.mrdekk.ercp.parent<span class="nt"></artifactId></span>
<span class="nt"><version></span>1.0.0-SNAPSHOT<span class="nt"></version></span>
<span class="nt"><relativePath></span>..<span class="nt"></relativePath></span>
<span class="nt"></parent></span>
<span class="nt"><artifactId></span>ru.mrdekk.ercp.gui<span class="nt"></artifactId></span>
<span class="nt"><packaging></span>eclipse-plugin<span class="nt"></packaging></span>
<span class="nt"><name></span>mrdekk.ru eclipse rcp demo - gui<span class="nt"></name></span>
<span class="nt"></project></span>
</code></pre></div></div>
<p>Дальше на проекте ru.mrdekk.ercp.gui нажимаем правой кнопкой и Maven > Update Project …</p>
<p>Надо кроме того добавить проект ru.mrdekk.ercp.gui как модуль основного (родительского проекта), для этого в pom.xml родительского проекта добавляем:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><modules></span>
<span class="nt"><module></span>gui<span class="nt"></module></span>
<span class="nt"></modules></span>
</code></pre></div></div>
<p>Далее на проекте ru.mrdekk.ercp.parent нажимаем правой кнопкой и Run As > Maven build … Указываем цели «clean package» и нажимаем Apply, затем Run.</p>
<p><img src="/media/images/ercp1_9.png" alt="rcp launch configuration" /></p>
<p>Все должно нормально собраться:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[INFO] Reactor Summary:
[INFO]
[INFO] mrdekk.ru eclipse rcp demo - parent ............... SUCCESS [0.109s]
[INFO] mrdekk.ru eclipse rcp demo - gui .................. SUCCESS [2.860s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 9.797s
[INFO] Finished at: Mon Sep 23 11:02:05 NOVST 2013
[INFO] Final Memory: 14M/34M
[INFO] ------------------------------------------------------------------------
</code></pre></div></div>
<p>В следующих статьях расскажу что делать дальше. Мы соберем feature и update site, что позволит нам публиковать приложения для установки в eclipse и распространять бинарные билды.</p>
<p>Для референсов исходники проекта доступны на github’е <a href="https://github.com/mrdekk/ercp">вот тут</a>.</p>
Мысли вслух2013-06-29T00:00:00+00:00http://mrdekk.ru/2013/06/29/loud-thoughts<p>Давно я сюда уже ничего постоянно не писал, видимо стоит возобновить практику. В последнее время по долгу службы приходится работать с языком Java в области Enterprise приложений. Можно говорить много слов об этом, но я сейчас не об этом. Для Java есть замечательный Spring Framework, который позволяет сосредоточиться на проблеме, а не на вспомогательных вещах, которые кочуют из проекта в проект.</p>
<p>Так как я сам лично люблю С++, то попытался я поискать чего нибудь подобного для этого языка. И не нашел. Причем по отдельности, по разной функциональности есть готовые разработки и библиотеки, есть даже в Open Source, однако чего-то целостного я не нашел. Может быть кто в курсе? Или С++ настолько непопулярен для разработки такого рода приложений?…</p>
<p>Впрочем, увидим…</p>
Устанавливаем C++ драйвер для MongoDB на Mac OS X Mountain Lion2013-03-16T00:00:00+00:00http://mrdekk.ru/2013/03/16/osx-cppdriver-mongodb-installation<p>Потребовался мне вдруг С++ драйвер для MongoDB на Mac OS X, так как писать для FreeBSD софт на С++ лучше всего под Mac OS X в xCode, вот такой уж винегрет.</p>
<p>Поэтому, первое что делаем, качаем вот <a href="http://dl.mongodb.org/dl/cxx-driver">отсюдова вот</a> файлик с названием cxx-driver/mongodb-linux-x86_64-latest.tgz пофигу что linux – скомпиляем и так, ведь для Mac OS X специально нету :( а жаль.</p>
<p>Распаковываем, и идем в распакованную папку, пытаемся сделать</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>scons
</code></pre></div></div>
<p>Но вот блин, scons’а нет на маке, увы :(</p>
<p>Ладно, качаем его <a href="http://prdownloads.sourceforge.net/scons/scons-2.3.0.tar.gz">отсюдова вот</a></p>
<p>и дальше</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python setup.py install
</code></pre></div></div>
<p>Возвращаемся в папку с распакованным драйвером MongoDB, и пытаемся снова сделать</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>scons
</code></pre></div></div>
<p>Веселье продолжается – на маке не установлен Boost, отлично идем и качаем исходники <a href="http://www.boost.org/users/download/">отсюдова вот</a></p>
<p>Как скачалось распаковываем и идем в распакованную папку и делаем</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./bootstrap.sh
<span class="nv">$ </span>./b2
<span class="nv">$ </span><span class="nb">sudo</span> ./b2 install
</code></pre></div></div>
<p>Алилуйя! Возвращаемся в папку с распакованным драйвером и снова делаем</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>scons
<span class="nv">$ </span><span class="nb">sudo </span>scons install
</code></pre></div></div>
<p>И вуаля – теперь у нас есть MongoDB C++ драйвер для Mac OS X Mountain Lion</p>
<h3 id="Комментарии-со-старой-версии-блога">Комментарии со старой версии блога</h3>
<blockquote>
<p>Артем:
18.07.2013 в 20:33
Спасибо большое. Установить получилось ) но tutorial.cpp так и не получилось запустить. Выдает:</p>
</blockquote>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tutorial.cpp:3:35: error: mongo/client/dbclient.h: No such file or directory
</code></pre></div></div>
<blockquote>
<p>Может подскажете, почему такое получается ?</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
18.07.2013 в 20:37
Добрый день!
Попробую подсказать. Компилируете чем? Если xcode, то надо в настройках проекта установить Header Search Paths туда где у вас стоит mongo (у меня например такой путь получился – /usr/local/include). Также не забудьте прописать в Other Linker Flags параметр -lmongoclient чтобы после компиляции у Вас все слинковалось.</p>
<p>Ежели компиляете руками (make, gmake и иже с ними), то -I/usr/local/include -lmongoclient</p>
<p>Ежели не получится – отпишитесь по симптомам.</p>
</blockquote>
<hr />
<blockquote>
<p>Артем:
18.07.2013 в 21:11
Решил переустановить драйвер. При запуске:</p>
</blockquote>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>scons
</code></pre></div></div>
<blockquote>
<p>в конце получил вот такое:</p>
</blockquote>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>In file included from src/mongo/logger/ramlog.cpp:20:
src/mongo/logger/ramlog.h:154: error: ISO C++ forbids declaration of ‘lock_guard’ with no type
src/mongo/logger/ramlog.h:154: error: invalid use of ‘::’
src/mongo/logger/ramlog.h:154: error: expected ‘;’ before » token
src/mongo/logger/ramlog.cpp:49: error: ‘lk’ was not declared in this scope
src/mongo/logger/ramlog.cpp: In constructor ‘mongo::RamLog::LineIterator::LineIterator(mongo::RamLog*)’:
src/mongo/logger/ramlog.cpp:174: error: class ‘mongo::RamLog::LineIterator’ does not have any field named ‘_lock’
scons: *** [build/mongo/logger/ramlog.o] Error 1
scons: building terminated because of errors.
</code></pre></div></div>
<blockquote>
<p>p.s. В /usr/local/include пусто (</p>
</blockquote>
<hr />
<blockquote>
<p>Артем:
18.07.2013 в 21:39
Нашел mongo в /opt/local/include/
Подключил так как вы описали. Теперь получаю вот такое:</p>
</blockquote>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/opt/local/include/mongo/pch.h:48:10: ‘boost/shared_ptr.hpp’ file not found
</code></pre></div></div>
<blockquote>
<p>этот путь тоже нужно подключить так ?</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
18.07.2013 в 22:26
ну boost тоже надо подключать, тем же способом</p>
</blockquote>
<hr />
<blockquote>
<p>Артем:
19.07.2013 в 14:01
Добавил вот такое:</p>
</blockquote>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/opt/local/include/** /usr/local/include/boost/** /usr/local/include/boost/tr1/detail/**
</code></pre></div></div>
<blockquote>
<p>Но по прежнему ругается на</p>
</blockquote>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/usr/local/include/boost/tr1/tr1/iostream:16:12: ‘boost/tr1/detail/config_all.hpp’ file not found
</code></pre></div></div>
<hr />
<blockquote>
<p>MrDekk:
21.07.2013 в 13:43
А можно iostream этот в студию?
Может быть вместо</p>
</blockquote>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/usr/local/include/boost/tr1/detail
</code></pre></div></div>
<blockquote>
<p>надо указать</p>
</blockquote>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/usr/local/include/boost/tr1
</code></pre></div></div>
Поиск подстроки в файлах на сервере FreeBSD2012-10-28T00:00:00+00:00http://mrdekk.ru/2012/10/28/substring-in-files-search-freebsd<p>Ок, чтобы долго не бегать, сразу дам ответ на вопрос «Как найти строку во всех файлах в текущей папке»:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>find <span class="nv">$PWD</span> <span class="nt">-name</span> <span class="s1">'*.*'</span> <span class="nt">-exec</span> <span class="nb">grep</span> <span class="nt">-l</span> <span class="s2">"TEST"</span> <span class="o">{}</span> <span class="se">\;</span>
</code></pre></div></div>
<p>Использовать по протоколу ssh, $PWD указывает на текущую директорию (можно посмотреть путь командой pwd).</p>
<p>Параметр -name ‘<em>.</em>’ указывает что идёт поиск всех файлов по маске <em>.</em>. К этим файлам выполняем (-exec) команду grep, которая ищет строку TEST в списке файлов и (можете изменять на свою, не забывайте только экранировать некоторые символы, например кавычки) и выводит список всех файлов (-l).</p>
<p>Взято <a href="http://www.tsng.ru/node/8">отсюда</a></p>
Получения хэша ключа для Facebook2012-10-28T00:00:00+00:00http://mrdekk.ru/2012/10/28/keyhash-for-facebook<p>Для реализации интеграции с Facebook необходим хэш ключа, чтобы его сделать, надо пойти туда, где этот ключ лежит и сделать</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>keytool <span class="nt">-exportcert</span> <span class="nt">-keystore</span> mygame.keystore | openssl sha1 <span class="nt">-binary</span> | openssl base64
</code></pre></div></div>
Аз буки веди2012-09-30T00:00:00+00:00http://mrdekk.ru/2012/09/30/az-buki-vedi<p><img src="/media/images/az-buki-vedi.jpg" alt="az buki vedi" /></p>
<p>Азбука: послание к славянам. Ярослав Кеслер</p>
<p>Азъ буки веде. Глаголъ добро есте. Живите зело, земляне, и, иже како люди, мыслите нашъ онъ покои. Рцы слово твердо – укъ фъретъ херъ. Цы, черве, шта ъра юсъ яти!</p>
<p>Я знаю буквы: Письмо – это достояние. Трудитесь усердно, земляне, как подобает разумным людям – постигайте мироздание! Несите слово убежденно – Знание – дар Божий! Дерзайте, вникайте, чтобы Сущего свет постичь!</p>
Мудрые мысли на злобу дня!2012-08-29T00:00:00+00:00http://mrdekk.ru/2012/08/29/it-is-not-the-end<p><img src="/media/images/not-the-end.jpeg" alt="not the end" /></p>
Если вы не заплатили за это, то вы не клиент. Вы товар, который продают...2012-05-01T00:00:00+00:00http://mrdekk.ru/2012/05/01/no-money-no-honey<p>Наткнулся тут на просторах интернетов вот на какую мысль касательно бесплатных сервисов:</p>
<p><img src="/media/images/your-the-product.jpg" alt="no money no honey" /></p>
10 заповедей программиста2012-02-12T00:00:00+00:00http://mrdekk.ru/2012/02/12/10-rules-of-programmers<ol>
<li>Программирование – твоя главная страсть. И да не будет у тебя страсти главней.</li>
<li>Не сотвори себе кумира из конкретной технологии. Ибо программирование требует постоянного развития, а технологии-кумиры останавливают развитие.</li>
<li>Не возноси хвальбу программированию в неподходящей компании. Ты сам себя накажешь, ибо будешь не понят, и люди отвернутся от тебя.</li>
<li>Работай много и хорошо, но не забывай и про отдых. Ибо нет ничего страшнее, чем код усталого, засыпающего программиста.</li>
<li>Уважай учителей и учеников своих. Постоянно учись и учи окружающих, чтобы было тебе всё легче и легче делать всё более и более сложные вещи.</li>
<li>Не убий в себе ребенка. Не забывай эмоции от первого запуска первой написанной тобой программы и воспринимай каждую следующую, как ту – первую.</li>
<li>Не изменяй программированию. Ибо программист может стать кем угодно, но этот кто угодно обратно программистом уже не станет.</li>
<li>Не кради код ближнего своего.</li>
<li>Не программируй то, что может принести вред другим. Ибо встав раз на путь дьявола – на нем и останешься.</li>
<li>Не завидуй ближнему твоему, если он умеет лучше программировать. Ибо программирование – это божественный дар, но его можно развить. Так что не завидуй, а развивай.</li>
</ol>
<p>Источник не понятен, взято вот <a href="http://sly-and-fluffy.blogspot.com/2010/10/10.html">отсюдова</a></p>
Блокировка ориентации экрана2012-01-27T00:00:00+00:00http://mrdekk.ru/2012/01/27/android-lock-screen-orientation<p>Возникла необходимость у меня сделать так чтобы ориентация экрана не менялась при повороте устройства, все-то навсего необходимо добавить в манифест в нужную Activity следующую строчку</p>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">android</span>:<span class="n">screenOrientation</span>=<span class="s2">"portrait"</span>
</code></pre></div></div>
Мысли о масштабируемости2012-01-14T00:00:00+00:00http://mrdekk.ru/2012/01/14/scaleability-thoughts<p><em>На просторах интернета наткнулся на замечательную статью про масштабируемость. Хочу представить Вам вольный ее перевод. Далее повествование идет от лица автора оригинальной статьи. Кроме того, под масштабирование вверх понимается вертикальное масштабирование, под масштабирование вширь – горизонтальное.</em></p>
<p>Все мы надеемся, что наши проекты будут успешны и превзойдут самые смелые наши ожидания. Всем известны стартапы, которые выходили на многомиллиардные IPO, и имели в своем парке сотни, тысячи и даже больше серверов. Каждый хостинг провайдер твердит нам о своих новых облачных технологиях, которые позволят выйти нам на вершины пройзводительности. Я создал и уничтожил много вещей за свою карьеру.</p>
<h3 id="build-it-to-break-Стройте-так-чтобы-сломать">Build it to Break (Стройте так, чтобы сломать)</h3>
<p>Все что Вы делаете, рано или поздно сломается. Поэтому имейте это ввиду и будьте к этому готовы.</p>
<p>Когда это возможно, сделайте так, чтобы каждый слой приложения работал независимо, кроме того, следует озаботиться должным резервированием. Начните всего с двух – Web-сервера и сервера-приложений, иногда стоит добавить сервер баз данных. Как только Вы поймете, что в сущности все может сломаться и непременно сломается, Вы будете куда счастливее с тем, что у Вас есть, особенно, когда что-то пойдет не так. Хорошие приложение строятся с учетом того, что они могут сломаться. Системные архитекторы признают, что проблемы неизбежны. Это конечно не то, что мы хотим, но это то, что мы должны принять.</p>
<p>Распределенные архитектуры позволяют перемещать рабочие нагрузки между многими автономными серверами. Балансировщики нагрузки и Web-фермы позволяют управлять отказами на уровне серверов-приложений. В мире баз данных, мы можем управлять отказами с помощью кластеризации, зеркалирования и реплик в режиме только чтение.</p>
<h3 id="everything-is-a-feature-Все--это-фича-отличительная-особенность">Everything is a Feature (Все – это фича, отличительная особенность)</h3>
<p>Как однажды лихо сказал Джефф Этвуд (Jeff Atwood), <a href="http://www.codinghorror.com/blog/2011/06/performance-is-a-feature.html">производительность – это фича</a>. Основная мысль Джеффа в том, что дать приложению максимальную производительность – это решение, которые Вы принимаете в процессе разработки. Поэтому, размышляя в том же ключе, сделать приложение отказоустойчивым – это тоже должно быть сознательным решением.</p>
<p>Посему, любое решение – это компромисс. Обзор всего приложения как ряд компромиссов ведет к лучшему пониманию того, как приложение будет работать в реальном мире. И разница в том, можно ли расширять приложение вверх и вширь закладывается именно в нескольких ключевых решениях, которые были сделаны на ранних стадиях разработки.</p>
<h3 id="scale-out-not-up-Масштабируйте-вширь-но-не-вверх">Scale Out, Not Up (Масштабируйте вширь, но не вверх)</h3>
<p>Это не так аксиоматично, как кажется. Например, такие облачные сервисы как Azure или AWS являются одними из самых гибких ввиду того, что сервера можно добавлять и убавлять по необходимости, в ответ на запросы клиентов. Масштабирование вширь только тогда будет максимально эффективным, когда будет оставаться возможность дать обратный ход.</p>
<p>Самый простой способ масштабирования, как правило, находится на уровне приложений, требуется просто добавить еще серверов. Что происходит, когда возникает необходимость масштабировать базу данных? Нынешняя тенденция состоит в покупке более быстрого сервера с более быстрыми дисками и большим объемом памяти. И этот процесс постоянно повторяется. Надеюсь, что Ваша необходимость в новых серверах будет продолжаться в темпе, которые меньше, либо равен темпу инноваций. Есть и другие проблемы с масштабированием. В процессе повышения производительности, железо становится все более и более дорогим, однако удельный прирост производительности становится все меньше и меньше. Разница в цене между самым быстрым CPU и вторым самым быстрым CPU как правило куда больше, чем получаемый выигрыш в производительности – масштабирование вверх как правило имеет огромную стоимость.</p>
<p>Кроме масштабирования вверх существует масштабирование вширь. Когда мы говорим о масштабирование вширь, то для обработки дополнительной нагрузки просто добавляются дополнительные сервера. Один из простейших способов масштабировать вширь базу данных – это добавить реплики только для чтения. Запросы на запись обрабатываются на главном сервере, потому что масштабирование вширь записи может быть очень и очень сложным. Но что делать, если нам все-таки необходимо масштабировать вширь запись? К счастью, есть много методов для горизонтального масштабирования слоя базы данных. Фичи могут использовать разные хранилища данных, метаданные реплицируются между всеми серверами, в том время как бизнес данные разбиваются на шарды (shards). Кроме того, можно использовать автоматизированные техники, как например SQL Azure Federation.</p>
<p>Самой важной вещью, которую стоит непременно держать в голове, что возможность расширяться по необходимости также важно, как и заключать договора. По мере того, как бизнес будет расти, можно будет просто докупить серверов по мере роста нагрузки. Покупка нового оборудования, как правило, дешевле и быстрее, чем тюнинг кода. Однако, как только приложение достигнет зрелого уровня, важно оттюнить приложение для использования меньшего количества ресурсов. Меньше аппаратуры означает меньше ее поддержки. Меньше аппаратуры – меньше затраты. Никто не хочет сталкиваться с другой возможностью, однако, бизнес может сократится. Пользовательская база может уменьшиться. Способность бизнеса реагировать на изменение затрат, может стать решающим отличием между успешным средним бизнесом, и провалившимся большим.</p>
<h3 id="buy-more-storage-Покупайте-больше-жестких-дисков">Buy more storage (Покупайте больше жестких дисков)</h3>
<p>В дополнении к масштабированию вширь серверов, масштабируйте вширь систему хранения. Если Вы стоите перед выбором купить несколько больших дисков или много маленьких – выберите второй вариант. Большее количество мелких быстрых дисков будут в состоянии быстрее обрабатывать запросы ввода/вывода. Большее количество дисков, работающих вместе, означает также и то, что с каждого из дисков будет считано меньше данных.</p>
<p>Хитрость в том, что современные системы управления базами данных могут умело распределять нагрузку между несколькими файлами или даже дисками. Если в процесс считывания данных вовлечено несколько файлов/дисков/шпинделей/логических томов, то данные будут считаны быстрее, чем если бы был вовлечен только один большой диск. Принцип горизонтального масштабирования лучше вертикального масштабирования даже на уровне системы хранения – несколько дисков, как правило, быстрее чем один большой.</p>
<h3 id="youre-going-to-do-it-wrong-Вы-все-равно-сделаете-это-неправильно">You’re Going to Do It Wrong (Вы все равно сделаете это неправильно)</h3>
<p>Вне зависимости от того, насколько умна и опытна Ваша команда, будьте готовы к ошибкам. Существует всего несколько жестких и быстрых реализаций принципов масштабирования бизнеса. Будьте готовы попробовать несколько идей, прежде чем найдете то правильное сочетание техники и технологий, которые будут работать хорошо. Вы конечно можете получить это сочетание и при первой попытке. Хотя обычно, надо сделать несколько попыток. Но, в любом случае, будьте готовы вернуться к рассмотрению идеи.</p>
<p>Поэтому, будьте готовы переписать ядро приложения при масштабировании. Твиттер был изначально построен на Ruby On Rails. С течением времени, они <a href="http://blog.evanweaver.com/2009/03/13/qcon-presentation/">реализовали различные части приложения с помощью разных инструментов</a>. Готовность Twitter переписать основные компоненты свой инфраструктуры, стала важной частью успеха.</p>
<p>Не бойтесь изменений – делайте их. (Don’t be afraid to change everything)</p>
Получение списка таблиц в текущем подключении ORACLE2011-11-13T00:00:00+00:00http://mrdekk.ru/2011/11/13/oracle-tables-in-current-connection<p>Появилась тут одна задача связанная с Oracle и Java. В процессе ее решения потребовались некоторые кусочки кода, которые я и хочу предложить читателям. Вдруг куда понадобятся.</p>
<p>Подключение к Oracle (Java) (Проверка на ошибки и прочая мишура намеренно опущены)</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Class</span><span class="o">.</span><span class="na">forName</span><span class="o">(</span> <span class="s">"oracle.jdbc.driver.OracleDriver"</span> <span class="o">);</span>
<span class="n">String</span> <span class="n">url</span> <span class="o">=</span> <span class="s">"jdbc:oracle:thin:@"</span> <span class="o">+</span> <span class="n">server</span> <span class="o">+</span> <span class="s">":"</span> <span class="o">+</span> <span class="n">port</span> <span class="o">+</span> <span class="s">":"</span> <span class="n">sid</span><span class="o">;</span>
<span class="n">Connection</span> <span class="n">connection</span> <span class="o">=</span> <span class="n">DriverManager</span><span class="o">.</span><span class="na">getConnection</span><span class="o">(</span> <span class="n">url</span><span class="o">,</span> <span class="n">username</span><span class="o">,</span> <span class="n">password</span> <span class="o">);</span>
</code></pre></div></div>
<p>Получение таблиц в текущем подключении</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Statement</span> <span class="n">stmt</span> <span class="o">=</span> <span class="n">connection</span><span class="o">.</span><span class="na">createStatement</span><span class="o">(</span> <span class="o">);</span>
<span class="n">ResultSet</span> <span class="n">rs</span> <span class="o">=</span> <span class="n">stmt</span><span class="o">.</span><span class="na">executeQuery</span><span class="o">(</span> <span class="s">"select TABLE_NAME from TABS"</span> <span class="o">);</span>
<span class="k">while</span> <span class="o">(</span> <span class="n">rs</span><span class="o">.</span><span class="na">next</span><span class="o">(</span> <span class="o">)</span> <span class="o">)</span>
<span class="o">{</span>
<span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span> <span class="n">rs</span><span class="o">.</span><span class="na">getString</span><span class="o">(</span> <span class="s">"TABLE_NAME"</span> <span class="o">)</span> <span class="o">);</span>
<span class="o">}</span>
<span class="n">rs</span><span class="o">.</span><span class="na">close</span><span class="o">(</span> <span class="o">);</span>
<span class="n">rs</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="n">stmt</span><span class="o">.</span><span class="na">close</span><span class="o">(</span> <span class="o">);</span>
<span class="n">stmt</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
</code></pre></div></div>
Проблема с git push2011-09-18T00:00:00+00:00http://mrdekk.ru/2011/09/18/git-push-problem<p>Решил тут запушить на удаленный репозиторий большое количество файлов, однако гит ругнулся с примерно следующей ошибкой:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>error: RPC failed; result=22, HTTP code = 411
</code></pre></div></div>
<p>Оказалось, что проблема в больших файлах, чтобы это дело обойти надо сделать в репозитории примерно следующее</p>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">git</span> <span class="n">config</span> <span class="n">http</span>.<span class="n">postBuffer</span> <span class="m">524288000</span>
</code></pre></div></div>
С Днем Программиста! Ура!2011-09-12T00:00:00+00:00http://mrdekk.ru/2011/09/12/happy-coders-day<p><img src="/media/images/happy-coders-day.gif" alt="happy coders day" /></p>
<p>Вот и наступил 256 день в году, а значит уже в третий раз официально – День Программиста. Посему поздравляю всех коллег по цеху с этим знаменательным днем.</p>
<p>Хочу пожелать всем программистам нашей необъятной страны вежливых пользователей, хорошего кода, ну а самое главное – счастья не только в коде, но и в жизни.</p>
Тестирование карандаша2011-09-10T00:00:00+00:00http://mrdekk.ru/2011/09/10/pencil-testing<p>При разработке программного обеспечения кроме самой проектирования и программирования следует уделять внимание тестированию. Однако, задумывались ли Вы о том, что и обычные вещи тоже можно потестировать. Например – карандаш.</p>
<p>Совсем недавно мне показали одну очень интересную картинку про то, как можно протестировать карандаш, вот она:</p>
<p><img src="/media/images/pencil.png" alt="pencil testing" /></p>
<p>Автора увы не записал тогда, поэтому если Вы автор этой картинки – прошу сообщить мне, я с удовольствием впишу Вас как автора.</p>
Забавно об управлении проектами…2011-08-24T00:00:00+00:00http://mrdekk.ru/2011/08/24/project-management<p><img src="/media/images/pmbuildswing.gif" alt="project management" /></p>
MySQL. Проблема с русскими буквами «И» и «ш».2011-06-09T00:00:00+00:00http://mrdekk.ru/2011/06/09/mysql-russian-i-and-sh-problem<p>Пришлось тут намедни осуществлять переезд системы redmine с одного сервера на другой. Переезд файлов и скриптов прошел без проблем, а вот переезд базы омрачился тем, что вместо нормальных русских букв «И» и «ш» стали отображаться кракозябры.</p>
<p>После применения черной магии стало понятно, что косяк в кодировке. База имеет кодировку по-умолчанию utf8, а redmine (в силу исторических причин) использует cp1251.</p>
<p>Чтобы все заработало как надо, экспортировать базу надо так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mysqdump <span class="nt">--user</span><span class="o">=</span><user> <span class="nt">--password</span><span class="o">=</span><censored> <span class="nt">--default-character-set</span><span class="o">=</span>cp1251 redmine <span class="o">></span> redmine.sql
</code></pre></div></div>
<p>А импортировать</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mysql <span class="nt">--user</span><span class="o">=</span><user> <span class="nt">--password</span><span class="o">=</span><censored> <span class="nt">--default-character-set</span><span class="o">=</span>cp1251 redmine < redmine.sql
</code></pre></div></div>
Kohana 3.1. Деревья на ORM2011-04-12T00:00:00+00:00http://mrdekk.ru/2011/04/12/kohana-trees-orm<p>Kohana 3 оооочень интересный фреймворк – простой и понятный. Однако, как и во всех новых продуктах в нем иногда отсутствуют вполне логичные вещи. ORM присутствует, однако ему не хватает одной очень важной на мой взгляд вещи – деревьев. Например, мы хотим сделать дерево категорий на ORM… Как мы это должны сделать? Вот это сегодня и рассмотрим.</p>
<p>Задача: мы хотим сделать дерево категорий. С неограниченной вложенностью.</p>
<p>Для начала создадим таблицу в базе данных:</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="nv">`categories`</span> <span class="p">(</span>
<span class="nv">`id`</span> <span class="n">int</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="n">unsigned</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="n">AUTO_INCREMENT</span><span class="p">,</span>
<span class="nv">`parent_id`</span> <span class="n">int</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="n">unsigned</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
<span class="nv">`name`</span> <span class="n">varchar</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
<span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="p">(</span><span class="nv">`id`</span><span class="p">)</span>
<span class="p">)</span> <span class="n">ENGINE</span><span class="o">=</span><span class="n">MyISAM</span> <span class="n">AUTO_INCREMENT</span><span class="o">=</span><span class="mi">16</span> <span class="k">DEFAULT</span> <span class="n">CHARSET</span><span class="o">=</span><span class="n">latin1</span><span class="p">;</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="nv">`categories`</span> <span class="k">VALUES</span>
<span class="p">(</span><span class="s1">'1'</span><span class="p">,</span> <span class="s1">'0'</span><span class="p">,</span> <span class="s1">'Colors'</span><span class="p">),</span> <span class="p">(</span><span class="s1">'2'</span><span class="p">,</span> <span class="s1">'1'</span><span class="p">,</span> <span class="s1">'Blue'</span><span class="p">),</span> <span class="p">(</span><span class="s1">'3'</span><span class="p">,</span> <span class="s1">'1'</span><span class="p">,</span> <span class="s1">'Green'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'4'</span><span class="p">,</span> <span class="s1">'1'</span><span class="p">,</span> <span class="s1">'Red'</span><span class="p">),</span> <span class="p">(</span><span class="s1">'5'</span><span class="p">,</span> <span class="s1">'1'</span><span class="p">,</span> <span class="s1">'Yellow'</span><span class="p">),</span> <span class="p">(</span><span class="s1">'6'</span><span class="p">,</span> <span class="s1">'1'</span><span class="p">,</span> <span class="s1">'Purple'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'7'</span><span class="p">,</span> <span class="s1">'0'</span><span class="p">,</span> <span class="s1">'Food'</span><span class="p">),</span> <span class="p">(</span><span class="s1">'8'</span><span class="p">,</span> <span class="s1">'7'</span><span class="p">,</span> <span class="s1">'Steak'</span><span class="p">),</span> <span class="p">(</span><span class="s1">'9'</span><span class="p">,</span> <span class="s1">'7'</span><span class="p">,</span> <span class="s1">'Baked Potato'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'10'</span><span class="p">,</span> <span class="s1">'7'</span><span class="p">,</span> <span class="s1">'Blue Berry Pie'</span><span class="p">),</span> <span class="p">(</span><span class="s1">'11'</span><span class="p">,</span> <span class="s1">'0'</span><span class="p">,</span> <span class="s1">'Movies'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'12'</span><span class="p">,</span> <span class="s1">'11'</span><span class="p">,</span> <span class="s1">'Avatar'</span><span class="p">),</span> <span class="p">(</span><span class="s1">'13'</span><span class="p">,</span> <span class="s1">'11'</span><span class="p">,</span> <span class="s1">'Kung Fu Panda'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'14'</span><span class="p">,</span> <span class="s1">'11'</span><span class="p">,</span> <span class="s1">'The Incredibles'</span><span class="p">),</span> <span class="p">(</span><span class="s1">'15'</span><span class="p">,</span> <span class="s1">'11'</span><span class="p">,</span> <span class="s1">'Meet the Robinsons'</span><span class="p">);</span>
</code></pre></div></div>
<p>Далее создадим класс модели-категории.</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span> <span class="nb">defined</span><span class="p">(</span><span class="s1">'SYSPATH'</span><span class="p">)</span> <span class="k">or</span> <span class="k">die</span><span class="p">(</span><span class="s1">'No direct script access.'</span><span class="p">);</span>
<span class="k">class</span> <span class="nc">Model_Category</span> <span class="k">extends</span> <span class="nx">ORM</span>
<span class="p">{</span>
<span class="c1">// Relationships
</span> <span class="k">protected</span> <span class="nv">$_belongs_to</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="p">);</span>
<span class="k">protected</span> <span class="nv">$_has_many</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Добавим отношения ко многим, это будут дочерние категории:</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span>
<span class="s1">'children'</span> <span class="o">=></span> <span class="k">array</span><span class="p">(</span><span class="s1">'model'</span> <span class="o">=></span> <span class="s1">'category'</span><span class="p">,</span> <span class="s1">'foreign_key'</span> <span class="o">=></span> <span class="s1">'parent_id'</span><span class="p">),</span>
</code></pre></div></div>
<p>Ну и конечно добавим отношение к одному - к родительской категории.</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span>
<span class="s1">'parent'</span> <span class="o">=></span> <span class="k">array</span><span class="p">(</span><span class="s1">'model'</span> <span class="o">=></span> <span class="s1">'category'</span><span class="p">,</span> <span class="s1">'foreign_key'</span> <span class="o">=></span> <span class="s1">'parent_id'</span><span class="p">),</span>
</code></pre></div></div>
<p>в итоге наш класс будет выглядеть примерно следующим образом</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span> <span class="nb">defined</span><span class="p">(</span><span class="s1">'SYSPATH'</span><span class="p">)</span> <span class="k">or</span> <span class="k">die</span><span class="p">(</span><span class="s1">'No direct script access.'</span><span class="p">);</span>
<span class="k">class</span> <span class="nc">Model_Category</span> <span class="k">extends</span> <span class="nx">ORM</span>
<span class="p">{</span>
<span class="c1">// Relationships
</span> <span class="k">protected</span> <span class="nv">$_belongs_to</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">'parent'</span> <span class="o">=></span> <span class="k">array</span><span class="p">(</span><span class="s1">'model'</span> <span class="o">=></span> <span class="s1">'category'</span><span class="p">,</span> <span class="s1">'foreign_key'</span> <span class="o">=></span> <span class="s1">'parent_id'</span><span class="p">),</span>
<span class="p">);</span>
<span class="k">protected</span> <span class="nv">$_has_many</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">'children'</span> <span class="o">=></span> <span class="k">array</span><span class="p">(</span><span class="s1">'model'</span> <span class="o">=></span> <span class="s1">'category'</span><span class="p">,</span> <span class="s1">'foreign_key'</span> <span class="o">=></span> <span class="s1">'parent_id'</span><span class="p">),</span>
<span class="p">);</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">delete</span><span class="p">(</span><span class="nv">$id</span> <span class="o">=</span> <span class="k">NULL</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$id</span> <span class="o">===</span> <span class="k">NULL</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// Use the the primary key value
</span> <span class="nv">$id</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-></span><span class="na">pk</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span> <span class="o">!</span> <span class="k">empty</span><span class="p">(</span><span class="nv">$id</span><span class="p">)</span> <span class="nx">OR</span> <span class="nv">$id</span> <span class="o">===</span> <span class="s1">'0'</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">foreach</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="na">children</span><span class="o">-></span><span class="na">find_all</span><span class="p">()</span> <span class="k">as</span> <span class="nv">$child</span><span class="p">)</span>
<span class="p">{</span>
<span class="nv">$child</span><span class="o">-></span><span class="na">delete</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">parent</span><span class="o">::</span><span class="na">delete</span><span class="p">(</span><span class="nv">$id</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nv">$this</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Функция delete определена для того, чтобы правильно удалить дерево категорий, если база данных не поддерживает ON DELETE CASCADE.</p>
<p>Использовать данный класс можно следующим образом:</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span>
<span class="nv">$categories</span> <span class="o">=</span> <span class="nx">ORM</span><span class="o">::</span><span class="na">factory</span><span class="p">(</span><span class="s1">'category'</span><span class="p">)</span><span class="o">-></span><span class="na">where</span><span class="p">(</span><span class="s1">'parent_id'</span><span class="p">,</span> <span class="s1">'='</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span><span class="o">-></span><span class="na">find_all</span><span class="p">();</span>
<span class="k">foreach</span> <span class="p">(</span><span class="nv">$categories</span> <span class="k">as</span> <span class="nv">$category</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">foreach</span> <span class="p">(</span><span class="nv">$category</span><span class="o">-></span><span class="na">children</span><span class="o">-></span><span class="na">find_all</span><span class="p">()</span> <span class="k">as</span> <span class="nv">$sub_category</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">echo</span> <span class="nv">$sub_category</span><span class="o">-></span><span class="na">name</span><span class="p">;</span>
<span class="k">echo</span> <span class="nv">$sub_category</span><span class="o">-></span><span class="na">parent</span><span class="o">-></span><span class="na">name</span><span class="p">;</span>
<span class="p">}</span>
<span class="nv">$category</span><span class="o">-></span><span class="na">delete</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Надеюсь это поможет всем любителям Коханы.</p>
<h3 id="Комментарии-со-старой-версии-блога">Комментарии со старой версии блога</h3>
<blockquote>
<p>Новичок:
13.02.2012 в 16:48
Подскажите пожалуйста а как в цикле при помощи вашего решения выстроить HTML древо куда нужно расположить теги.</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
15.02.2012 в 21:20
Ну для начала создать соответствующую вьюшку. Можно рекурсивную. Из контроллера в нее передаете рут или несколько рутовых категорий, дальше согласно приведенному сниппету рекурсивно строите нужную вложенность. Если будет время в выходные постараюсь набросать Вам пример.</p>
</blockquote>
<hr />
<blockquote>
<p>Павел:
19.10.2012 в 13:47
Пример конечно классный! Использовать также удобно. Вроде бы все даже хорошо, но вот представьте, если в базе огромное количество данных, то сколько времени потребуется на обработку рекурсивных запросов?</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
20.10.2012 в 00:10
Во-первых, никто не мешает эту самую рекурсию ограничивать.
Во-вторых, никто не мешает разворачивать папки с помощью ajax, т.е. только те которые нужно развернуть в данный момент.</p>
<p>А так да, комментарий справедлив, если в базе ОООЧЕНЬ много данных, то запрос может длиться вечность. Кроме того, вечность может длится доставка контента пользователю.</p>
</blockquote>
Запаковать папку с помощью GZip2011-02-11T00:00:00+00:00http://mrdekk.ru/2011/02/11/pack-folder-with-gzip<p>Понадобилось вот запаковать папку именно с помощью gzip. Обычно использовал tar со стандартными ключами, вроде</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">tar</span> <span class="nt">-cvf</span> archive.tar folder
</code></pre></div></div>
<p>А для того, чтобы применять сжатие gzip надо делать так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">tar</span> <span class="nt">-cvzf</span> archive.tgz folder
</code></pre></div></div>
FreeBSD. Удалить порт с зависимостями2011-02-04T00:00:00+00:00http://mrdekk.ru/2011/02/04/freebsd-remove-port-with-deps<p>Иногда бывает необходимо удалить установленный порт со всеми зависимостями. Можно конечно колдовать с pkg_… но это долго и утомительно. На днях обнаружил интересную утилиту: pkg_rmleaves.</p>
<p>Ставим так</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd</span> /usr/ports/ports-mgmt/pkg_rmleaves/
<span class="nv">$ </span>make install clean
<span class="nv">$ </span>rehash
<span class="nv">$ </span>pkg_rmleaves <span class="nt">-d</span>
</code></pre></div></div>
<p>Для информации читаем маны. Надеюсь будет полезно.</p>
15 000 день unix эпохи2011-01-26T00:00:00+00:00http://mrdekk.ru/2011/01/26/15000-unix-epoch<p>Сегодня, 26 января, в полночь по GMT, наступил 15000 день от начала летоисчисления Unix машин. Юниксоиды всех стран встречаются, празднуют, проводят массовые гуляния и гадают. Встречи локальных групп можно найти на специальном сайте, посвящённом этому знаменательному дню.</p>
<p>День можно посмотреть командой:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo</span> <span class="sb">`</span>date +%s<span class="sb">`</span>/86400|bc
</code></pre></div></div>
Админские стихи2011-01-15T00:00:00+00:00http://mrdekk.ru/2011/01/15/sysadmins-poems<p>Произведение не мое, но уж очень понравилось…</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>У сисадмина серв от Intel,
Ядро i7 стоит на нём.
И днём и ночью ты, убитый,
Е@#шься с ним, как с букварём.
Как «линь» поставил — песнь заводишь;
Под Windows матом говоришь.
Там чудеса, там BSoD выводит,
Там в Микрософте Билл сидит…
Там от неведомых экзешек
Следы невиданных зверей.
Там выдаёт «Не отвечает»,
Когда нажмёшь ты на «ОК».
Там SSH не обитает —
Хреново без него порой…
А грёбаный telnet слетает,
Когда брутфорсят твой пароль.
Там chroot’а нет — как хостинг ставить?
Как обойтись без IIS?!
А вот никак! Format спасает…
Apache без «никса» есть не жилец.
Там злостный вирь над hosts’ом чахнет,
Там гейтский дух, там Дверью пахнет.
</code></pre></div></div>
Замена подстроки в std:wstring2011-01-10T00:00:00+00:00http://mrdekk.ru/2011/01/10/wstring-substr-replace<p>Продолжаю публиковать серию сниппетов. На этот раз задача – заменить подстроку в строке wstring другой. Делается это так:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">std</span><span class="o">::</span><span class="n">wstring</span> <span class="n">strSource</span> <span class="o">=</span> <span class="s">L"this string is XXXX and YYYY"</span><span class="p">;</span>
<span class="kt">size_t</span> <span class="n">posn</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">wstring</span> <span class="n">replaceFrom</span> <span class="o">=</span> <span class="s">L"XXXX"</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">wstring</span> <span class="n">replaceTo</span> <span class="o">=</span> <span class="s">L"SOMETHING"</span><span class="p">;</span>
<span class="n">posn</span> <span class="o">=</span> <span class="n">strSource</span><span class="p">.</span><span class="n">find</span><span class="p">(</span> <span class="n">replaceFrom</span> <span class="p">);</span>
<span class="n">strSource</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span> <span class="n">posn</span><span class="p">,</span> <span class="n">replaceFrom</span><span class="p">.</span><span class="n">length</span><span class="p">(</span> <span class="p">),</span> <span class="n">replaceTo</span> <span class="p">);</span>
</code></pre></div></div>
<p>если надо заменить все вхождения, то последние две строки надо заменить на</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="p">(</span> <span class="n">std</span><span class="o">::</span><span class="n">wstring</span><span class="o">::</span><span class="n">npos</span> <span class="o">!=</span> <span class="p">(</span> <span class="n">posn</span> <span class="o">=</span> <span class="n">strSource</span><span class="p">.</span><span class="n">find</span><span class="p">(</span> <span class="n">replaceFrom</span> <span class="p">)</span> <span class="p">)</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">strSource</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span> <span class="n">posn</span><span class="p">,</span> <span class="n">replaceFrom</span><span class="p">.</span><span class="n">length</span><span class="p">(</span> <span class="p">),</span> <span class="n">replaceTo</span> <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Надеюсь будет полезно.</p>
Чтение текстового файла целиком в переменную2011-01-08T00:00:00+00:00http://mrdekk.ru/2011/01/08/read-entire-file-to-var<p>Бывает полезная функция, однако в интернете редко можно найти сниппет, поэтому предлагаю его здесь. Надеюсь будет полезен.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">length</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kt">char</span><span class="o">*</span> <span class="n">buffer</span> <span class="o">=</span> <span class="n">null</span><span class="p">;</span>
<span class="n">std</span><span class="o">::</span><span class="n">ifstream</span> <span class="n">is</span><span class="p">;</span>
<span class="n">is</span><span class="p">.</span><span class="n">open</span><span class="p">(</span> <span class="s">"config.xml"</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ios</span><span class="o">::</span><span class="n">binary</span> <span class="p">);</span>
<span class="n">is</span><span class="p">.</span><span class="n">seekg</span><span class="p">(</span> <span class="mi">0</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ios</span><span class="o">::</span><span class="n">end</span> <span class="p">);</span>
<span class="n">length</span> <span class="o">=</span> <span class="n">is</span><span class="p">.</span><span class="n">tellg</span><span class="p">(</span> <span class="p">);</span>
<span class="n">is</span><span class="p">.</span><span class="n">seekg</span><span class="p">(</span> <span class="mi">0</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">ios</span><span class="o">::</span><span class="n">beg</span> <span class="p">);</span>
<span class="n">buffer</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">char</span><span class="p">[</span> <span class="n">length</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">];</span>
<span class="n">buffer</span><span class="p">[</span> <span class="n">length</span> <span class="p">]</span> <span class="o">=</span> <span class="sc">'\0'</span><span class="p">;</span>
<span class="n">is</span><span class="p">.</span><span class="n">read</span><span class="p">(</span> <span class="n">buffer</span><span class="p">,</span> <span class="n">length</span> <span class="p">);</span>
<span class="n">is</span><span class="p">.</span><span class="n">close</span><span class="p">(</span> <span class="p">);</span>
</code></pre></div></div>
FreeBSD gnome2 русификация2010-10-25T00:00:00+00:00http://mrdekk.ru/2010/10/25/freebsd-gnome2-rus<p>Для начала идем в /etc/login.conf и добавляем туда профиль пользователя для UTF-8</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mcedit /etc/login.conf
</code></pre></div></div>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#
# Russian User Accounts with UTF-8
#
</span><span class="n">russian</span>-<span class="n">utf</span>|<span class="n">UTF</span>-<span class="m">8</span> <span class="n">Russian</span> <span class="n">User</span> <span class="n">Accounts</span>:\
:<span class="n">charset</span>=<span class="n">UTF</span>-<span class="m">8</span>:\
:<span class="n">lang</span>=<span class="n">ru_RU</span>.<span class="n">UTF</span>-<span class="m">8</span>:\
:<span class="n">lc_all</span>=<span class="n">ru_RU</span>.<span class="n">UTF</span>-<span class="m">8</span>:\
:<span class="n">tc</span>=<span class="n">default</span>:
</code></pre></div></div>
<p>Далее применяем профиль к целевому пользователю</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>cap_mkdb /etc/login.conf
<span class="nv">$ </span>pw usermod < user <span class="o">></span> <span class="nt">-L</span> russian-utf
<span class="nv">$ </span>pw usershow < user <span class="o">></span>
< user <span class="o">></span>:<span class="k">*</span>:1001:1001:russian-utf:0:0:User &:/home/< user <span class="o">></span>:/bin/sh
</code></pre></div></div>
<p>Устанавливаем язык по умолчанию в gdm</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mcedit /etc/rc.conf
</code></pre></div></div>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">gdm_lang</span><span class="p">=</span><span class="s">"ru_RU.UTF-8"</span>
</code></pre></div></div>
<h3 id="Комментарии-со-старой-версии-блога">Комментарии со старой версии блога</h3>
<blockquote>
<p>Алекс:
13.07.2013 в 16:44
Русский GNOME2 на FreeBSD 8.4 (без gdm):</p>
</blockquote>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mcedit /etc/rc.conf:
</code></pre></div></div>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">keyrate</span>=»<span class="n">fast</span>»
<span class="n">keymap</span>=»<span class="n">ru</span>.<span class="n">koi8</span>-<span class="n">r</span>.<span class="n">win</span>»
<span class="n">scrnmap</span>=»<span class="n">koi8</span>-<span class="n">r2cp866</span>″
<span class="n">font8x16</span>=»<span class="n">cp866b</span>-<span class="m">8</span>×<span class="m">16</span>″
<span class="n">font8x14</span>=»<span class="n">cp866</span>-<span class="m">8</span>×<span class="m">14</span>″
<span class="n">font8x8</span>=»<span class="n">cp866</span>-<span class="m">8</span>×<span class="m">8</span>″
</code></pre></div></div>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mcedit /etc/csh.login:
</code></pre></div></div>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">setenv</span> <span class="n">LANG</span> <span class="n">ru_RU</span>.<span class="n">KOI8</span>-<span class="n">R</span>
<span class="n">setenv</span> <span class="n">MM_CHARSET</span> <span class="n">KOI8</span>-<span class="n">R</span>
<span class="n">setenv</span> <span class="n">LC_TYPE</span> <span class="n">ru_RU</span>.<span class="n">KOI8</span>-<span class="n">R</span>
<span class="n">setenv</span> <span class="n">LC_LANG</span> <span class="n">ru_RU</span>.<span class="n">KOI8</span>-<span class="n">R</span>
<span class="n">setenv</span> <span class="n">LC_MONETARY</span> <span class="n">ru_RU</span>.<span class="n">KOI8</span>-<span class="n">R</span>
<span class="n">setenv</span> <span class="n">LC_TIME</span> <span class="n">ru_RU</span>.<span class="n">KOI8</span>-<span class="n">R</span>
<span class="n">setenv</span> <span class="n">LC_NUMERIC</span> <span class="n">ru_RU</span>.<span class="n">KOI8</span>-<span class="n">R</span>
<span class="n">setenv</span> <span class="n">LC_COLLATE</span> <span class="n">ru_RU</span>.<span class="n">KOI8</span>-<span class="n">R</span>
<span class="n">setenv</span> <span class="n">LC_MESSAGES</span> <span class="n">ru_RU</span>.<span class="n">KOI8</span>-<span class="n">R</span>
<span class="n">setenv</span> <span class="n">LC_ALL</span> <span class="n">ru_RU</span>.<span class="n">KOI8</span>-<span class="n">R</span>
</code></pre></div></div>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mcedit /root/.xinitrc
</code></pre></div></div>
<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/<span class="n">usr</span>/<span class="n">local</span>/<span class="n">bin</span>/<span class="n">gnome</span>-<span class="n">session</span>
</code></pre></div></div>
<blockquote>
<p>Запускаем startx</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
18.07.2013 в 20:39
Ежели работает – отлично :)</p>
</blockquote>
FreeBSD gnome2 Logon Window2010-10-25T00:00:00+00:00http://mrdekk.ru/2010/10/25/freebsd-gnome2-logon-win<p>Если после установки гнома в бзде не появляется поле ввода логина пароля, а видна только тоненькая строчка и все, то надо проделать следующие пассы руками.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ee /etc/fstab
</code></pre></div></div>
<p>и написать туды</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>proc /proc procfs rw 0 0
</code></pre></div></div>
<p>UPD: для заметки, гномий листик при старте хранится тут:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/usr/local/share/pixmaps/backgrounds/gnome/background-default.jpg
</code></pre></div></div>
Простой проект red5+eclipse2010-10-20T00:00:00+00:00http://mrdekk.ru/2010/10/20/red5-eclipse-simple-project<p>Сегодня займемся создание простого проекта для red5 под eclipse. Будем считать, что red5, eclipse, flashdevelope, flex sdk вы уже поставили, поэтому сразу перейдем к делу.</p>
<p>Запустите eclipse, выберите пункт меню File->New Project, далее Java Project, далее Next, далее наберите «appz» в поле имя проекта, затем нажмите Finish.</p>
<p>Вы создали новый проект и теперь можете видеть его в диспетчере проектов, используется стандартная JRE установленная в вашей системе. Нажмите правой кнопкой на проекте, далее New->Folder, наберите WEB-INF. Создайте подкаталоги classes и src в только что созданной папке WEB-INF.</p>
<p>Нажмите правой кнопкой мыши на каталоге WEB-INF/src -> Build Path -> Use as Source Folder, теперь вы установили WEB-INF/src как корневой каталог для исходных текстов.</p>
<p>Теперь необходимо установить WEB-INF/classes как каталог куда буду складываться объектные файлы после компиляции. Для этого в контекстном меню проекта надо выбрать Properties, Далее пункт Java Build Path, в нем закладка Sources, там внизу Default Output Folder следует нажать Browse и выбрать папку WEB-INF/classes или просто ввести этот путь в поле ввода.</p>
<p>Все почти готово. Нет только нужных конфигурационных файлов. Для этого откройте Ваш любимые файловый менеджер и скопируйте файлы red5-web.properties, red5-web.xml, web.xml из готового проекта в вашу папку WEB-INF.</p>
<p>Теперь переключитесь обратно в eclipse, нажмите правой кнопкой на проекте appz и выберите Refresh, эти файлы появятся в каталоге WEB-INF.</p>
<h3 id="Базовое-серверное-приложение">Базовое серверное приложение</h3>
<p>Теперь мы готовы чтобы создать новое серверное приложение. Для этого нажмите правой кнопкой на WEB-INF/src выберите New->Class, введите src для package и Application для имени класса. Затем нажмите Finish.</p>
<p>Это будет наш основной класс, поэтому мы должны реализовать в нем интерфейс ApplicationAdapter. Добавьте red5.jar в библиотеки, для этого выберите в контекстном меню проекта Properties -> Java Build Path -> Libraries -> Add External Jars, выберите red5.jar в корневом каталоге red5. Теперь eclipse может компилировать проект для нас.</p>
<p>Создайте функции appStart и appStop следующим образом:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">src</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.red5.server.adapter.ApplicationAdapter</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Application</span> <span class="kd">extends</span> <span class="n">ApplicationAdapter</span>
<span class="o">{</span>
<span class="kd">public</span> <span class="n">Boolean</span> <span class="nf">appStart</span> <span class="o">(</span> <span class="o">)</span>
<span class="o">{</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">appStop</span> <span class="o">(</span> <span class="o">)</span>
<span class="o">{</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Теперь надо импортировать интерфейс для работы с пользовательскими соединениями</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">org.red5.server.api.IConnection</span><span class="o">;</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">appConnect</span><span class="o">(</span> <span class="n">IConnection</span> <span class="n">conn</span><span class="o">,</span> <span class="n">Object</span><span class="o">[]</span> <span class="n">params</span> <span class="o">)</span>
<span class="o">{</span>
<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">appDisconnect</span><span class="o">(</span> <span class="n">IConnection</span> <span class="n">conn</span><span class="o">)</span>
<span class="o">{</span>
<span class="kd">super</span><span class="o">.</span><span class="na">appDisconnect</span><span class="o">(</span><span class="n">conn</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Для отладки нашего приложения нам нужно журналирование, поэтому для создания журналов воспользуемся библиотекой apache commons logging. Возможно Вам придется скачать ее с официального сайта.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">org.apache.commons.logging.Log</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.commons.logging.LogFactory</span><span class="o">;</span>
</code></pre></div></div>
<p>Журналируем потихоньку:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">Log</span> <span class="n">log</span> <span class="o">=</span> <span class="n">LogFactory</span><span class="o">.</span><span class="na">getLog</span><span class="o">(</span> <span class="n">Application</span><span class="o">.</span><span class="na">class</span> <span class="o">);</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">appStart</span> <span class="o">(</span> <span class="o">)</span>
<span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span> <span class="s">"Red5First.appStart"</span> <span class="o">);</span>
<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">appStop</span> <span class="o">(</span> <span class="o">)</span>
<span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span> <span class="s">"Red5First.appStop"</span> <span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">appConnect</span><span class="o">(</span> <span class="n">IConnection</span> <span class="n">conn</span> <span class="o">,</span> <span class="n">Object</span><span class="o">[]</span> <span class="n">params</span> <span class="o">)</span>
<span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span> <span class="s">"Red5First.appConnect "</span> <span class="o">+</span> <span class="n">conn</span><span class="o">.</span><span class="na">getClient</span><span class="o">().</span><span class="na">getId</span><span class="o">()</span> <span class="o">);</span>
<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">appDisconnect</span><span class="o">(</span> <span class="n">IConnection</span> <span class="n">conn</span><span class="o">)</span>
<span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span> <span class="s">"Red5First.appDisconnect "</span> <span class="o">+</span> <span class="n">conn</span><span class="o">.</span><span class="na">getClient</span><span class="o">().</span><span class="na">getId</span><span class="o">()</span> <span class="o">);</span>
<span class="kd">super</span><span class="o">.</span><span class="na">appDisconnect</span><span class="o">(</span><span class="n">conn</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Теперь нам нужно немного тестовой логики, поэтому давайте сделаем так, если клиент передает true в соединении, то мы его принимаем, если false – то нет</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">Log</span> <span class="n">log</span> <span class="o">=</span> <span class="n">LogFactory</span><span class="o">.</span><span class="na">getLog</span><span class="o">(</span> <span class="n">Application</span><span class="o">.</span><span class="na">class</span> <span class="o">);</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">appStart</span> <span class="o">(</span> <span class="o">)</span>
<span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span> <span class="s">"Red5First.appStart"</span> <span class="o">);</span>
<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">appStop</span> <span class="o">(</span> <span class="o">)</span>
<span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span> <span class="s">"Red5First.appStop"</span> <span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">appConnect</span><span class="o">(</span> <span class="n">IConnection</span> <span class="n">conn</span><span class="o">,</span> <span class="n">Object</span><span class="o">[]</span> <span class="n">params</span> <span class="o">)</span>
<span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span> <span class="s">"Red5First.appConnect "</span> <span class="o">+</span> <span class="n">conn</span><span class="o">.</span><span class="na">getClient</span><span class="o">().</span><span class="na">getId</span><span class="o">()</span> <span class="o">);</span>
<span class="kt">boolean</span> <span class="n">accept</span> <span class="o">=</span> <span class="o">(</span><span class="n">Boolean</span><span class="o">)</span><span class="n">params</span><span class="o">[</span><span class="mi">0</span><span class="o">];</span>
<span class="k">if</span> <span class="o">(</span> <span class="o">!</span><span class="n">accept</span> <span class="o">)</span> <span class="n">rejectClient</span><span class="o">(</span> <span class="s">"you passed false..."</span> <span class="o">);</span>
<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">appDisconnect</span><span class="o">(</span> <span class="n">IConnection</span> <span class="n">conn</span><span class="o">)</span>
<span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span> <span class="s">"Red5First.appDisconnect "</span> <span class="o">+</span> <span class="n">conn</span><span class="o">.</span><span class="na">getClient</span><span class="o">().</span><span class="na">getId</span><span class="o">()</span> <span class="o">);</span>
<span class="kd">super</span><span class="o">.</span><span class="na">appDisconnect</span><span class="o">(</span><span class="n">conn</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>В итоге код выглядит следующим образом</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="n">src</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.commons.logging.Log</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.commons.logging.LogFactory</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.red5.server.api.IConnection</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.red5.server.adapter.ApplicationAdapter</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Application</span> <span class="kd">extends</span> <span class="n">ApplicationAdapter</span>
<span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="n">Log</span> <span class="n">log</span> <span class="o">=</span> <span class="n">LogFactory</span><span class="o">.</span><span class="na">getLog</span><span class="o">(</span> <span class="n">Application</span><span class="o">.</span><span class="na">class</span> <span class="o">);</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">appStart</span> <span class="o">(</span> <span class="o">)</span>
<span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span> <span class="s">"Red5First.appStart"</span> <span class="o">);</span>
<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">appStop</span> <span class="o">(</span> <span class="o">)</span>
<span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span> <span class="s">"Red5First.appStop"</span> <span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">appConnect</span><span class="o">(</span> <span class="n">IConnection</span> <span class="n">conn</span><span class="o">,</span> <span class="n">Object</span><span class="o">[]</span> <span class="n">params</span> <span class="o">)</span>
<span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span> <span class="s">"Red5First.appConnect "</span> <span class="o">+</span> <span class="n">conn</span><span class="o">.</span><span class="na">getClient</span><span class="o">().</span><span class="na">getId</span><span class="o">()</span> <span class="o">);</span>
<span class="kt">boolean</span> <span class="n">accept</span> <span class="o">=</span> <span class="o">(</span><span class="n">Boolean</span><span class="o">)</span><span class="n">params</span><span class="o">[</span><span class="mi">0</span><span class="o">];</span>
<span class="k">if</span> <span class="o">(</span> <span class="o">!</span><span class="n">accept</span> <span class="o">)</span> <span class="n">rejectClient</span><span class="o">(</span> <span class="s">"you passed false..."</span> <span class="o">);</span>
<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">appDisconnect</span><span class="o">(</span> <span class="n">IConnection</span> <span class="n">conn</span><span class="o">)</span>
<span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span> <span class="s">"Red5First.appDisconnect "</span> <span class="o">+</span> <span class="n">conn</span><span class="o">.</span><span class="na">getClient</span><span class="o">().</span><span class="na">getId</span><span class="o">()</span> <span class="o">);</span>
<span class="kd">super</span><span class="o">.</span><span class="na">appDisconnect</span><span class="o">(</span><span class="n">conn</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Теперь разберемся с кофигами, они должны выглядеть примерно так:</p>
<p>log4j.properties:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>application-related logging parameters
</code></pre></div></div>
<p>red5-web.properties:</p>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">webapp.contextPath</span><span class="p">=</span><span class="s">/firstapp</span>
<span class="py">webapp.virtualHosts</span><span class="p">=</span><span class="s">localhost, 127.0.0.1</span>
</code></pre></div></div>
<p>red5-web.xml:</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><bean</span> <span class="na">id=</span><span class="s">"web.handler"</span>
<span class="na">class=</span><span class="s">"com.milgra.Application"</span>
<span class="na">singleton=</span><span class="s">"true"</span> <span class="nt">/></span>
<span class="nt"><context-param></span>
<span class="nt"><param-name></span>webAppRootKey<span class="nt"></param-name></span>
<span class="nt"><param-value></span>/firstapp<span class="nt"></param-value></span>
<span class="nt"></context-param></span>
</code></pre></div></div>
<p>После всего скопируйте все содержимое каталога WEB-INF вместе с ним в каталог red5/webapps и перезапустите сервер.</p>
<h3 id="Создаем-клиента-на-flash">Создаем клиента на flash</h3>
<p>Хорошо, с серверной частью все хорошо. Теперь займемся клиентом. Для этого открываем FlashDevelope и создаем проект (ActionScript 3), называем его например fp</p>
<p>Выглядеть он должен примерно следующим образом</p>
<div class="language-actionscript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">package</span> <span class="nx">fp</span>
<span class="p">{</span>
<span class="kr">import</span> <span class="nx">flash</span><span class="p">.</span><span class="nx">display</span><span class="p">.</span><span class="nx">Sprite</span><span class="o">;</span>
<span class="kr">import</span> <span class="nx">flash</span><span class="p">.</span><span class="nx">events</span><span class="p">.</span><span class="nx">Event</span><span class="o">;</span>
<span class="kr">import</span> <span class="nx">flash</span><span class="p">.</span><span class="nx">events</span><span class="p">.</span><span class="nx">NetStatusEvent</span><span class="o">;</span>
<span class="kr">import</span> <span class="nx">flash</span><span class="p">.</span><span class="nx">net</span><span class="p">.</span><span class="nx">NetConnection</span><span class="o">;</span>
<span class="kr">import</span> <span class="nx">flash</span><span class="p">.</span><span class="nx">net</span><span class="p">.</span><span class="nx">ObjectEncoding</span><span class="o">;</span>
<span class="kr">import</span> <span class="nx">flash</span><span class="p">.</span><span class="nx">text</span><span class="p">.</span><span class="nx">TextField</span><span class="o">;</span>
<span class="cm">/**
* ...
* @author MrDekk
*/</span>
<span class="kr">public</span> <span class="kr">class</span> <span class="nx">Main</span> <span class="kr">extends</span> <span class="nx">Sprite</span>
<span class="p">{</span>
<span class="kr">private</span> <span class="kd">var</span> <span class="nx">nc</span> <span class="o">:</span> <span class="nx">NetConnection</span><span class="o">;</span>
<span class="kr">private</span> <span class="kd">var</span> <span class="nx">tf</span> <span class="o">:</span> <span class="nx">TextField</span><span class="o">;</span>
<span class="kr">public</span> <span class="kd">function</span> <span class="nx">Main</span><span class="p">()</span><span class="o">:</span><span class="nb">void</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span> <span class="nx">stage</span> <span class="p">)</span>
<span class="nx">init</span><span class="p">(</span> <span class="p">)</span><span class="o">;</span>
<span class="k">else</span>
<span class="nx">addEventListener</span><span class="p">(</span> <span class="nx">Event</span><span class="p">.</span><span class="nx">ADDED_TO_STAGE</span><span class="p">,</span> <span class="nx">init</span> <span class="p">)</span><span class="o">;</span>
<span class="p">}</span>
<span class="kr">private</span> <span class="kd">function</span> <span class="nx">init</span><span class="p">(</span> <span class="nx">e</span> <span class="o">:</span> <span class="nx">Event</span> <span class="o">=</span> <span class="kc">null</span> <span class="p">)</span> <span class="o">:</span> <span class="nb">void</span>
<span class="p">{</span>
<span class="nx">removeEventListener</span><span class="p">(</span><span class="nx">Event</span><span class="p">.</span><span class="nx">ADDED_TO_STAGE</span><span class="p">,</span> <span class="nx">init</span><span class="p">)</span><span class="o">;</span>
<span class="c1">// entry point</span>
<span class="nx">tf</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">TextField</span><span class="p">(</span> <span class="p">)</span><span class="o">;</span>
<span class="nx">tf</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="mi">10</span><span class="o">;</span>
<span class="nx">tf</span><span class="p">.</span><span class="nx">y</span> <span class="o">=</span> <span class="mi">10</span><span class="o">;</span>
<span class="nx">tf</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="mi">300</span><span class="o">;</span>
<span class="nx">tf</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="mi">700</span><span class="o">;</span>
<span class="nx">stage</span><span class="p">.</span><span class="nx">addChild</span><span class="p">(</span> <span class="nx">tf</span> <span class="p">)</span><span class="o">;</span>
<span class="nx">nc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">NetConnection</span><span class="p">(</span> <span class="p">)</span><span class="o">;</span>
<span class="nx">nc</span><span class="p">.</span><span class="nx">objectEncoding</span> <span class="o">=</span> <span class="nx">ObjectEncoding</span><span class="p">.</span><span class="nx">AMF0</span><span class="o">;</span>
<span class="nx">nc</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span> <span class="nx">NetStatusEvent</span><span class="p">.</span><span class="nx">NET_STATUS</span><span class="p">,</span> <span class="nx">onNetStatus</span> <span class="p">)</span><span class="o">;</span>
<span class="nx">nc</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span> <span class="s2">"rtmp://10.0.0.202/appz"</span><span class="p">,</span> <span class="kc">false</span> <span class="p">)</span><span class="o">;</span>
<span class="p">}</span>
<span class="kr">private</span> <span class="kd">function</span> <span class="nx">onNetStatus</span><span class="p">(</span> <span class="nx">event</span> <span class="o">:</span> <span class="nx">NetStatusEvent</span> <span class="p">)</span> <span class="o">:</span> <span class="nb">void</span>
<span class="p">{</span>
<span class="kr">trace</span><span class="p">(</span> <span class="nx">event</span><span class="p">.</span><span class="nx">info</span><span class="p">.</span><span class="nx">code</span> <span class="p">)</span><span class="o">;</span>
<span class="nx">tf</span><span class="p">.</span><span class="nx">appendText</span><span class="p">(</span> <span class="nx">event</span><span class="p">.</span><span class="nx">info</span><span class="p">.</span><span class="nx">code</span> <span class="p">)</span><span class="o">;</span>
<span class="nx">tf</span><span class="p">.</span><span class="nx">appendText</span><span class="p">(</span> <span class="s2">"\n"</span> <span class="p">)</span><span class="o">;</span>
<span class="k">if</span> <span class="p">(</span> <span class="nx">event</span><span class="p">.</span><span class="nx">info</span><span class="p">.</span><span class="nx">code</span> <span class="o">==</span> <span class="s2">"NetConnection.Connect.Rejected"</span> <span class="p">)</span>
<span class="p">{</span>
<span class="kr">trace</span><span class="p">(</span> <span class="nx">event</span><span class="p">.</span><span class="nx">info</span><span class="p">.</span><span class="nx">application</span> <span class="p">)</span><span class="o">;</span>
<span class="nx">tf</span><span class="p">.</span><span class="nx">appendText</span><span class="p">(</span> <span class="nx">event</span><span class="p">.</span><span class="nx">info</span><span class="p">.</span><span class="nx">application</span> <span class="p">)</span><span class="o">;</span>
<span class="nx">tf</span><span class="p">.</span><span class="nx">appendText</span><span class="p">(</span> <span class="s2">"\n"</span> <span class="p">)</span><span class="o">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>После этого загоняем его в любимый браузер и запускаем. Если мы передали false, то увидем следующее</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NetConnection.Connect.Rejected
you passed fals . . .
NetConnection.Connect.Closed
</code></pre></div></div>
<p>Теперь заменим false на true, перекомпиляем проект и посмотрим снова</p>
<div class="language-actionscript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">nc</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span> <span class="s2">"rtmp://localhost/firstapp"</span> <span class="p">,</span> <span class="kc">true</span> <span class="p">)</span><span class="o">;</span>
</code></pre></div></div>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NetConnection.Connect.Success
</code></pre></div></div>
<p>Все работает! Ура!</p>
Одна проблемка и ее решение2010-10-08T00:00:00+00:00http://mrdekk.ru/2010/10/08/mysql-upper-first-letter<p>Задача простая – есть поле в базе данных MySql. Требуется сделать чтобы значение этого поля начиналось с большой буквы, весь остальной текст остался таким каков есть.</p>
<p>запрос такой</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span>
<span class="n">concat</span><span class="p">(</span><span class="k">upper</span><span class="p">(</span><span class="k">LEFT</span><span class="p">(</span><span class="n">description_short</span><span class="p">,</span><span class="mi">1</span><span class="p">)),</span><span class="k">substring</span><span class="p">(</span><span class="n">description_short</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="k">char_length</span><span class="p">(</span><span class="n">description_short</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">))</span> <span class="k">AS</span> <span class="n">x</span>
<span class="k">FROM</span> <span class="n">am_product_lang</span>
<span class="k">WHERE</span> <span class="n">description_short</span> <span class="o">></span> <span class="s1">''</span>
<span class="k">LIMIT</span> <span class="mi">10</span><span class="p">;</span>
</code></pre></div></div>
<p>По мотивам этого запроса можно написать любой нужный update.</p>
node.js дополнительная настройка2010-09-28T00:00:00+00:00http://mrdekk.ru/2010/09/28/nodejs-advanced-setup<p>Для дальнейшей работы с node.js нам потребуется еще несколько библиотек. Для того чтобы поставить эти библиотеки нам потребуется пакетный менеджер для node.js – npm.</p>
<p>Для выполнения всех действий делаем следующие шаги:</p>
<ol>
<li>
<p>Ставим последнюю версию node.js</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>fetch <span class="s1">'http://nodejs.org/dist/node-v0.2.2.tar.gz'</span>
<span class="nv">$ </span><span class="nb">tar</span> <span class="nt">-xvf</span> node-v0.2.2.tar.gz
<span class="nv">$ </span><span class="nb">cd </span>node-v0.2.2.tar.gz
<span class="nv">$ </span>./configure
<span class="nv">$ </span>make
<span class="nv">$ </span>make install
<span class="nv">$ </span>rehash
</code></pre></div> </div>
</li>
<li>
<p>Ставим пакетный менеджер npm</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>fetch <span class="s1">'http://download.github.com/isaacs-npm-v0.2.2-0-g00adb09.tar.gz'</span>
<span class="nv">$ </span><span class="nb">tar</span> <span class="nt">-xvf</span> isaacs-npm-v0.2.2-0-g00adb09.tar.gz
<span class="nv">$ </span><span class="nb">cd </span>isaacs-npm-6acdb76
<span class="nv">$ </span>make
<span class="nv">$ </span>rehash
</code></pre></div> </div>
</li>
<li>
<p>Ставим библиотеку htmlparser</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm install htmlparser
</code></pre></div> </div>
</li>
<li>
<p>Ставим библиотеку jsdom</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm install jsdom
</code></pre></div> </div>
</li>
<li>
<p>Ставим библиотеку http-agent</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm install http-agent
</code></pre></div> </div>
</li>
</ol>
<p>Далее можно подключать jQuery и делать паука. Но об этом в следующих статьях</p>
node.js+nginx на freebsd2010-09-22T00:00:00+00:00http://mrdekk.ru/2010/09/22/simple-nodejs-and-nginx<p>Доброго всем дня!</p>
<p>Решил вот тут попробовать такого зверя как node.js. Говорят на нем хорошо писать веб-пауков, особенно если прикрутить jQuery – то это становится просто удовольствием. От слов к делу. Для начала поставим nginx.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd</span> /usr/ports/www/nginx
<span class="nv">$ </span>make install clean
</code></pre></div></div>
<p>пропишем автозапуск</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mcedit /etc/rc.conf
</code></pre></div></div>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># nginx
</span><span class="py">nginx_enable</span><span class="p">=</span><span class="s">"YES"</span>
</code></pre></div></div>
<p>отредактируем файл настроек nginx’а</p>
<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">user</span> <span class="s">www</span><span class="p">;</span>
<span class="k">worker_processes</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">pid</span> <span class="n">/var/run/nginx.pid</span><span class="p">;</span>
<span class="k">events</span>
<span class="p">{</span>
<span class="kn">worker_connections</span> <span class="mi">1024</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">http</span>
<span class="p">{</span>
<span class="kn">include</span> <span class="s">mime.types</span><span class="p">;</span>
<span class="kn">default_type</span> <span class="nc">application/octet-stream</span><span class="p">;</span>
<span class="kn">log_format</span> <span class="s">main</span> <span class="s">'</span><span class="nv">$remote_addr</span> <span class="s">-</span> <span class="nv">$remote_user</span> <span class="s">[</span><span class="nv">$time_local</span><span class="s">]</span> <span class="nv">$request</span><span class="s">'</span>
<span class="s">'"</span><span class="nv">$status</span><span class="s">"</span> <span class="nv">$body_bytes_sent</span> <span class="s">"</span><span class="nv">$http_referer</span><span class="s">"</span> <span class="s">'</span>
<span class="s">'"</span><span class="nv">$http_user_agent</span><span class="s">"</span> <span class="s">"</span><span class="nv">$http_x_forwarded_for</span><span class="s">"'</span><span class="p">;</span>
<span class="kn">access_log</span> <span class="no">off</span><span class="p">;</span>
<span class="kn">sendfile</span> <span class="no">on</span><span class="p">;</span>
<span class="kn">keepalive_timeout</span> <span class="mi">65</span><span class="p">;</span>
<span class="kn">gzip</span> <span class="no">on</span><span class="p">;</span>
<span class="kn">server</span>
<span class="p">{</span>
<span class="kn">listen</span> <span class="s">*:80</span><span class="p">;</span>
<span class="kn">server_name</span> <span class="s">localhost</span><span class="p">;</span>
<span class="kn">location</span> <span class="n">/</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>и запускаем nginx</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>/usr/local/etc/rc.d/nginx start
</code></pre></div></div>
<p>Теперь приступим к установке node.js. Для начала удостоверимся что у нас в системе стоит libexecinfo. Для этого выполним команду</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>pkg_add <span class="nt">-r</span> libexecinfo
</code></pre></div></div>
<p>После этого устанавливаем собственно ноду</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mkdir /usr/tmp
<span class="nv">$ </span><span class="nb">cd</span> /usr/tmp
<span class="nv">$ </span>fetch <span class="s1">'http://s3.amazonaws.com/four.livejournal/20100120/node-v0.1.26.tar.gz'</span>
<span class="nv">$ </span><span class="nb">tar</span> <span class="nt">-xzvf</span> node-v0.1.26.tar.gz
<span class="nv">$ </span><span class="nb">cd </span>node-v0.1.26
<span class="nv">$ </span>./configure <span class="nt">--prefix</span><span class="o">=</span>/usr/local
<span class="nv">$ </span>make
<span class="nv">$ </span>make install
<span class="nv">$ </span>rehash
</code></pre></div></div>
<p>Для проверки правильности компиляции и установки ноды создадим простой скрипт</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">sys</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'sys'</span><span class="p">);</span>
<span class="nx">sys</span><span class="p">.</span><span class="nx">puts</span><span class="p">(</span><span class="s1">'Hello, World!'</span><span class="p">);</span>
</code></pre></div></div>
<p>и проверим его</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>node example.js
</code></pre></div></div>
<p>Должны увидеть Hello, World. Теперь интегрируем все это безобразие с Nginx. Для этого добавим в секцию Server конфига nginx’а следующее:</p>
<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">...</span>
<span class="s">location</span> <span class="n">/nodejs/</span> <span class="p">{</span>
<span class="kn">proxy_pass</span> <span class="s">http://127.0.0.1:8081/</span><span class="p">;</span>
<span class="kn">proxy_redirect</span> <span class="no">off</span><span class="p">;</span>
<span class="kn">proxy_set_header</span> <span class="s">Host</span> <span class="nv">$host</span><span class="p">;</span>
<span class="kn">proxy_set_header</span> <span class="s">X-Real-IP</span> <span class="nv">$remote_addr</span><span class="p">;</span>
<span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-For</span> <span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">...</span>
</code></pre></div></div>
<p>Создадим простой серверный скрипт:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">sys</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'sys'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">http</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'http'</span><span class="p">);</span>
<span class="nx">http</span><span class="p">.</span><span class="nx">createServer</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">res</span><span class="p">.</span><span class="nx">sendHeader</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="p">{</span><span class="s1">'Content-Type'</span><span class="p">:</span> <span class="s1">'text/plain'</span><span class="p">});</span>
<span class="nx">res</span><span class="p">.</span><span class="nx">sendBody</span><span class="p">(</span><span class="s1">'Hello, World!'</span><span class="p">,</span> <span class="s1">'utf8'</span><span class="p">);</span>
<span class="nx">res</span><span class="p">.</span><span class="nx">finish</span><span class="p">();</span>
<span class="p">}).</span><span class="nx">listen</span><span class="p">(</span><span class="mi">8081</span><span class="p">);</span>
<span class="nx">sys</span><span class="p">.</span><span class="nx">puts</span><span class="p">(</span><span class="s1">'Server running at port 8081'</span><span class="p">);</span>
</code></pre></div></div>
<p>Запускаем тестовый сервер</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>node server.js
</code></pre></div></div>
<p>если теперь зайти сюда http://<ваш ip="">/nodejs то вы должны увидеть приветствие</ваш></p>
Поиск больших файлов на FreeBSD2010-09-17T00:00:00+00:00http://mrdekk.ru/2010/09/17/freebsd-find-large-files<p>Если вдруг на диске по каким-то причинам закончилось место, а Вы точно уверены, что ничего такого туда не записывали, то возможно диск занят какими-то нужными или ненужными файлами. Поэтому тут представляю ряд способов почистить место на диске.</p>
<h2 id="1-Поиск-больших-файлов">1. Поиск больших файлов</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>find / <span class="nt">-size</span> +500M <span class="nt">-exec</span> <span class="nb">ls</span> <span class="nt">-l</span> <span class="o">{}</span> <span class="se">\;</span>
</code></pre></div></div>
<h2 id="2-Очистка-портов">2. Очистка портов</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>/usr/ports/Tools/scripts/distclean.sh
<span class="nv">$ </span>rm <span class="nt">-rf</span> /usr/ports/<span class="k">*</span>/<span class="k">*</span>/work
</code></pre></div></div>
<p>или для portupgrade</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>portsclean <span class="nt">-D</span>
portsclean <span class="nt">-C</span>
</code></pre></div></div>
Как удалить дублирующие записи в mysql2010-09-13T00:00:00+00:00http://mrdekk.ru/2010/09/13/mysql-remove-duplicates<p>Требуется решить следующую проблему. Есть таблица. Первичный ключ id, кроме всех прочих полей есть поле name. Оказывается, что существует несколько строк с разными id, но одним и тем же name.</p>
<p>Понятно, что в некоторых случаях имеет место неправильный выбор первичного ключа. Причина и последствия этого оставим за гранью статьи. Задача – сделать так, чтобы была только одна строка с таким name. То есть требуется удалить дубликаты.</p>
<p>Колдовать это в mysql можно по разному, вплоть до временных таблиц и т.д. Однако есть способ проще – с помощью создания уникального индекса.</p>
<p>Итак</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">copy_table</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="nv">`таблица`</span><span class="p">;</span>
<span class="k">ALTER</span> <span class="k">IGNORE</span> <span class="k">TABLE</span> <span class="n">copy_table</span> <span class="k">ADD</span> <span class="k">UNIQUE</span> <span class="p">(</span> <span class="n">Name</span> <span class="p">);</span>
</code></pre></div></div>
<p>А теперь внимательно смотрите на дубликаты. Вуаля!</p>
C Днем программиста! 1010112010-09-13T00:00:00+00:00http://mrdekk.ru/2010/09/13/happy-coders-day<p>Всех коллег поздравляю с профессиональным праздником!</p>
<p><img src="/media/images/happycoders.jpg" alt="happy coders day" /></p>
Kohana: настройка2010-08-12T00:00:00+00:00http://mrdekk.ru/2010/08/12/kohana-setup<p><img src="/media/images/kohana-300x119.png" alt="kohana logo" /></p>
<p>Сегодня хочу рассказать про базовую установку и настройку коханы версии 3. Кое что я уже писал, однако технологии не стоят на месте и определенным образом устаревают. Посему сегодня новый мануал.</p>
<p>Для начала идем на <a href="http://kohanaframework.org">официальный сайт</a> и скачиваем последнюю версию. На данные момент это 3.0.7.</p>
<p>После того как архив скачается его необходимо распаковать в корневую директорию сайта (веб-приложения). Вы конечно можете распаковывать его не в корень, однако в таком случае поправки в файле .htaccess будут иными.</p>
<h4 id="Настроим-htaccess">Настроим .htaccess</h4>
<ol>
<li>Добавляем опцию Options -Indexes, если она еще не добавлена. Стоит иметь ввиду, что подобная опция может уже стоять в настройках самого вебсервера. А может и не стоять. Поэтому для уверенности напишем ее.</li>
<li>
<p>Проверить каталог установки (# InstallationDirectory). Если Вы ставили кохану в корень, то там должно быть написано</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Installation Directory
</span><span class="nx">RewriteBase</span> <span class="o">/</span>
</code></pre></div> </div>
</li>
</ol>
<h4 id="Настройка-indexphp">Настройка index.php</h4>
<ol>
<li>При желании ядро системы, каталог system, может быть перемещен вне сайта (это может быть необходимо для обновлений, или например если у Вас несколько сайтов, а ядро фреймворка Вы хотите использовать одно). Аналогично можно перемещать папки application и modules. Однако если с каталогом system все ясно, с остальными я рекомендую Вам делать это только в том случае, если Вы понимаете что делаете.</li>
<li>Если Вы что-то переместили – необходимо изменить переменные $system, $application и $modules на соответствующие пути.</li>
</ol>
<h4 id="Настройка-applicationbootstrapphp">Настройка application/bootstrap.php</h4>
<p>Этот файл необходим для базовой настройки веб-приложения. В нем можно задавать различные конфигуационные параметры, определяющие работу всего приложения.</p>
<ol>
<li>Изменить значение date_default_timezone_set(‘Asia/Yekaterinburg’); на ту временную зону, которая Вам необходима (вот <a href="http://ru.php.net/timezones">список доступных</a>).</li>
<li>Изменить значение setlocale(LC_ALL, ‘ru_RU.utf-8′); Обычно употребимая локаль в России – ru_RU.UTF-8. Если Вам по каким-то причинам нужна другая – Вы можете сделать это.</li>
</ol>
<p>На этом базовая установка и настройка заканчивается. Далее Вы можете приниматься за разработку своего веб-приложения. Удачи Вам на этом пути!</p>
Стоп не сработал? 0_02010-06-10T00:00:00+00:00http://mrdekk.ru/2010/06/10/stop-not-fired<p>Трейдеры шутят…</p>
<p><img src="/media/images/nonstop.jpg" alt="Стоп не сработал" /></p>
Проблемка с postfix2010-06-08T00:00:00+00:00http://mrdekk.ru/2010/06/08/postfix-problem<p>На настроенной почтовой системе, которая работает уже достаточно давно в логах постоянно наблюдается примерно следующая вещь</p>
<pre><code class="language-log">postfix/smtpd[...]: sql_select option missing
postfix/smtpd[...]: auxpropfunc error no mechanism available
</code></pre>
<p>Поимке багов по логам несколько мешало, недавно нашел способ. Если пойти в папку /usr/local/lib/sasl2</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">pwd</span>
/usr/local/lib/sasl2
<span class="nv">$ </span><span class="nb">ls </span>libsql.<span class="k">*</span>
libsql.a
libsql.la
libsql.so
libsql.so.2
</code></pre></div></div>
<p>Эврика! Делаем бэкап. Дальше выполняем следующую последовательность команд. Учтите, что если Вы используете авторизацию SQL в постфиксе, то для Вас этот метод не сработает.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">pwd</span>
/usr/local/lib/sasl2
<span class="nv">$ </span>mkdir ./deactivated.sql
<span class="nv">$ </span>mv libsql.<span class="k">*</span> ./deactivated.sql
<span class="nv">$ </span>postfix reload
</code></pre></div></div>
<p>И вуаля – в лог лишнее более не пишется!</p>
<p>P.S. Также если ваш лог одолевает сообщение</p>
<pre><code class="language-log">postfix/smtpd[...]: OTP unavailable because can't read/write key database /etc/opiekeys: ...
</code></pre>
<p>и ОТР как таковой Вам не нужен, то подобные указанным выше действия надо проделать с библиотекой libotp</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">pwd</span>
/usr/local/lib/sasl2
<span class="nv">$ </span>mkdir ./deactivated.otp
<span class="nv">$ </span>mv libotp.<span class="k">*</span> ./deactivated.otp
<span class="nv">$ </span>postfix reload
</code></pre></div></div>
Установка red5 на FreeBSD 8.02010-04-26T00:00:00+00:00http://mrdekk.ru/2010/04/26/red5-freebsd<p>Считаю что Java у Вас уже установлена. В противном случае – это тема отдельной статьи, которую вскорости может быть напишу. У меня Java установлена тут</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>/usr/local/diablo-jdk1.6.0
</code></pre></div></div>
<p>Для сборки сервера red5 нам потребуется Apache Ant, который можно легко поставить из портов:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd</span> /usr/ports/devel/apache-ant
<span class="nv">$ </span>make install clean
</code></pre></div></div>
<p>Теперь нам нужны исходники red5, чекаутим их с официального сайта примерно следующим образом:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mkdir /home/red5
<span class="nv">$ </span>svn co http://red5.googlecode.com/svn/java/server/trunk /home/red5
</code></pre></div></div>
<p>Теперь соберем его</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd</span> /home/red5
<span class="nv">$ </span>ant
</code></pre></div></div>
<p>Процесс на 2-3 минуты. Должны поставиться всякие там jar, а те которых нет, подкачаться и также поставиться. Создадим рабочую директорию Red5 и перенесем туда только что собраный сервер:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mkdir /usr/share/red5
<span class="nv">$ </span>cp <span class="nt">-R</span> /home/red5/dist/<span class="k">*</span> /usr/share/red5
</code></pre></div></div>
<p>На всякий случай:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>chmod 755 /usr/share/red5/red5.sh
</code></pre></div></div>
<p>Исправляем shell:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ee /usr/share/red5/red5.sh
/bin/bash -> /bin/sh
</code></pre></div></div>
<p>Делаем пускач:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>touch /usr/local/etc/rc.d/red5.sh
</code></pre></div></div>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="nv">RED5_DIR</span><span class="o">=</span>/usr/share/red5
<span class="nb">test</span> <span class="nt">-x</span> <span class="nv">$RED5_DIR</span>/red5.sh <span class="o">||</span> <span class="nb">exit </span>5
<span class="k">case</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="k">in
</span>start<span class="p">)</span>
<span class="nb">cd</span> <span class="s2">"</span><span class="nv">$RED5_DIR</span><span class="s2">"</span>
<span class="s2">"</span><span class="nv">$RED5_DIR</span><span class="s2">"</span>/red5.sh &
sleep 2
<span class="p">;;</span>
stop<span class="p">)</span>
<span class="nb">echo </span>Shutting down Red5
killall java
sleep 2
<span class="p">;;</span>
restart<span class="p">)</span>
<span class="nv">$0</span> stop
<span class="nv">$0</span> start
<span class="p">;;</span>
<span class="k">esac</span>
</code></pre></div></div>
<p>Запускаем Red5 и проверяем открытость портов:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>/usr/local/etc/rc.d/red5.sh start
<span class="nv">$ </span>netstat <span class="nt">-ant</span>
...
tcp4 0 0 <span class="k">*</span>.5080 <span class="k">*</span>.<span class="k">*</span> LISTEN
tcp4 0 0 <span class="k">*</span>.1935 <span class="k">*</span>.<span class="k">*</span> LISTEN
tcp4 0 0 <span class="k">*</span>.51724 <span class="k">*</span>.<span class="k">*</span> LISTEN
tcp4 0 0 <span class="k">*</span>.9999 <span class="k">*</span>.<span class="k">*</span> LISTEN
...
</code></pre></div></div>
<p>Все гуд. Контрольный выстрел:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>telnet name-server 5080 или http://name-server:5080
</code></pre></div></div>
<p>Вот вроде и все – сервер собран, установлен и запущен (дефолтные настройки). Вам осталось накрутить конфиги Red5 под свои нужды (не забываем после этого сделать /usr/local/etc/rc.d/red5.sh restart).</p>
<p>ЗЫ: Правда надо еще поправить пускач, чтобы он стартовал Red5 при старте системы. Ну это на досуге как-нибудь</p>
<h3 id="Комментарии">Комментарии</h3>
<blockquote>
<p>mandarin80:
01.02.2011 в 19:42
установил, но не запускается, в чем может быть проблема?</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
01.02.2011 в 23:09
Добрый день!
Проблема может быть во многих вещах. Для того, чтобы я Вам мог подсказать по делу надо бы посмотреть логи.</p>
</blockquote>
<hr />
<blockquote>
<p>Тайланд:
03.03.2011 в 00:05
У меня проблема в том, что red зависает, проблема решается только перезагрузкой, удалением файла red5.pid и запуском заново, что за фигня, кто знает?</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
03.03.2011 в 18:14
Ну, вообще, выключать его правильно. Там есть скриптик, для Windows вроде stop.bat или shutdown.bat, для Unix: stop.sh или shutdown.sh – я не помню точно как называется. Если же это не помогает, то можно написать свой файлик, или скрипт запуска пополнить функцией анализа наличия pid и либо не делать дальше действий и просто сообщать, либо удалять его и проводить нормальный процесс запуска.</p>
</blockquote>
Java singleton2010-04-22T00:00:00+00:00http://mrdekk.ru/2010/04/22/java-singleton<p>Возникла у меня тут необходимость в паттерне Singleton для явы. После некоторого общения с гуглом нашел вот такую реализацию, которая мне понравилась, спешу поделиться ею с Вами уважаемые читатели:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kd">static</span> <span class="kd">volatile</span> <span class="n">NetworkController</span> <span class="n">controller</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="n">NetworkController</span> <span class="nf">getInstance</span><span class="o">(</span> <span class="o">)</span>
<span class="o">{</span>
<span class="k">if</span> <span class="o">(</span> <span class="kc">null</span> <span class="o">==</span> <span class="n">controller</span> <span class="o">)</span>
<span class="o">{</span>
<span class="kd">synchronized</span><span class="o">(</span> <span class="n">NetworkController</span><span class="o">.</span><span class="na">class</span> <span class="o">)</span>
<span class="o">{</span>
<span class="k">if</span> <span class="o">(</span> <span class="kc">null</span> <span class="o">==</span> <span class="n">controller</span> <span class="o">)</span>
<span class="o">{</span>
<span class="n">controller</span> <span class="o">=</span> <span class="k">new</span> <span class="n">NetworkController</span><span class="o">(</span> <span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">controller</span><span class="o">;</span>
<span class="o">}</span>
<span class="c1">// only private constructor! because of singleton</span>
<span class="kd">private</span> <span class="nf">NetworkController</span><span class="o">(</span> <span class="o">)</span>
<span class="o">{</span>
<span class="o">}</span>
</code></pre></div></div>
Простой софтфон на SIP2010-04-11T00:00:00+00:00http://mrdekk.ru/2010/04/11/simple-softphone-SIP<p>Попалась мне тут намендни сложная задачка. Надо к разрабатываемой программе прикрутить программный софтфон для SIP (VOIP). Несколько дней с гуглом ничего толком не дали, пока однажды не наткнулся (хвала опять же гуглу :) ) на один проект, который хостится опять же на гугле :) . Названием ему SipekSdk. О нем то и пойдет сегодня речь. Сегодня я покажу как написать свой софтфон.</p>
<p>Для разработки будем ипользовать Visual Studio 2008 Express Edition. Почему именно EE – потому что он легальный. Впрочем Вы можете пользоваться той редакцией и версией студии, которая Вам по душе.</p>
<p>Для начала создадим новый проект.</p>
<ol>
<li>Откройте Visual Studio и выполните File/New/Project</li>
<li>Выберите тип проекта Visual C#/Windows Application</li>
<li>В поле имени введите какое-нибудь имя, например SoftPhone</li>
<li>Выберите место на диске где он у Вас будет сохранен</li>
<li>Нажмите ОК</li>
</ol>
<p>Проект создаться и Вы должны увидеть дизайнер окна Form1. Это хорошо. Если что-то не получилось – ищите ошибку или почитайте документацию по студии.</p>
<p>Далее накидаем несколько компонентов на нашу форму. Нам потребуются</p>
<ol>
<li>TextBox с именем cs_Phone для ввода номера телефона</li>
<li>TextBox с именем cs_RegState для вывода информации о регистрации</li>
<li>TextBox с именем cs_CallState для вывода информации о звонке</li>
<li>Две Label к cs_RegState и cs_CallState для пояснения</li>
<li>Кнопка MakeCall с именем cs_MakeCall для совершения звонка</li>
<li>Кнопка Release с именем cs_Release для того чтобы иметь возможность «Ложить трубку»</li>
</ol>
<p>Получилось что-то подобное</p>
<p><img src="/media/images/softphone01.jpg" alt="Экран софтфона" /></p>
<p>Далее необходимо подключить SipekSdk, для этого необходимо проделать следующие действия:</p>
<ol>
<li>Скопируйте библиотеку SipekSdk.dll в папку проекта. Ее можно взять тут <a href="http://sipeksdk.googlecode.com/svn/trunk/SipekSdk/Lib">SipekSdk.dll</a></li>
<li>В SolutionExplorer студии выберите Add Reference…</li>
<li>В диалоге выберите закладку Browse, а в ней выберите файл SipekSdk.dll</li>
<li>Жмите ОК.</li>
</ol>
<p>Аналогичные действия надо проделать с библиотекой pjsipDll.dll, взять ее можно отсюдова <a href="http://sipeksdk.googlecode.com/svn/trunk/pjsipdll/Lib">pjsipDll.dll</a>.</p>
<p>Кроме того, Вы можете собрать их самостоятельно.</p>
<p>Теперь нам нужно сконфигурировать эти библиотеки. Вообще SipekSdk содержит в себе только интерфейсы, которые мы должны имплементировать. Нас в данном случае будут интересовать два интерфейса:</p>
<ol>
<li>IConfigurationInterface. Насколько я понял – основной конфигурационный интерфейс.</li>
<li>IAccount. Настройки конкретного аккаунта.</li>
</ol>
<p>Итак</p>
<ol>
<li>Создаем новый класс. Правый клик в SolutionExplorer/Add/New item…</li>
<li>Выберите Class и введите имя, например rc_PhoneCfg</li>
<li>Откроется новый файл. Вы можете удалить его содержание, если там оно присутствует и Вы этого хотите.</li>
<li>
<p>Добавьте включение</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Sipek.Common</span><span class="p">;</span>
</code></pre></div> </div>
</li>
<li>
<p>Теперь определите интерфейс</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">internal</span> <span class="k">class</span> <span class="nc">rc_PhoneCfg</span> <span class="p">:</span> <span class="n">IConfigurationInterface</span>
</code></pre></div> </div>
</li>
<li>Имплементируйте стандартные методы. Студия Вам в этом поможет.</li>
<li>
<p>Студия добавит Вам везде</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">throw</span> <span class="k">new</span> <span class="nf">Exception</span><span class="p">(</span><span class="s">"The method or operation is not implemented."</span><span class="p">);</span>
</code></pre></div> </div>
</li>
<li>Пока оставьте, исправим позже</li>
<li>Теперь аналогичным образом имплементируйте интерфейс IAccount, пусть класс будет rc_AccountCfg</li>
<li>Попробуйте скомпилировать. Должно быть все ОК.</li>
</ol>
<p>Теперь добавим кой чего полезного</p>
<ol>
<li>
<p>Добавьте список аккаунтов в rc_PhoneCfg</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">List</span><span class="p"><</span> <span class="n">IAccount</span> <span class="p">></span> <span class="n">v_slAccList</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p"><</span> <span class="n">IAccount</span><span class="p">>(</span> <span class="p">);</span>
<span class="k">internal</span> <span class="nf">rc_PhoneCfg</span><span class="p">(</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">v_slAccList</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span> <span class="k">new</span> <span class="nf">rc_AccountCfg</span><span class="p">(</span> <span class="p">)</span> <span class="p">);</span>
<span class="p">}</span>
</code></pre></div> </div>
</li>
<li>
<p>Определите свойство Accounts</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="n">List</span><span class="p"><</span> <span class="n">IAccount</span><span class="p">></span> <span class="n">Accounts</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">v_slAccList</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div> </div>
</li>
<li>
<p>Остальные свойства определите следующим образом (свойства set везде пусть будут set { } )</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CFBFlag</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
<span class="n">CFBNumber</span> <span class="p">=</span> <span class="s">""</span><span class="p">;</span>
<span class="n">CFNRFlag</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
<span class="n">CFNRNumber</span> <span class="p">=</span> <span class="s">""</span><span class="p">;</span>
<span class="n">CFUFlag</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
<span class="n">CFUNumber</span> <span class="p">=</span> <span class="s">""</span><span class="p">;</span>
<span class="k">public</span> <span class="n">List</span><span class="p"><</span> <span class="n">String</span> <span class="p">></span> <span class="n">CodecList</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="n">List</span><span class="p"><</span> <span class="n">String</span> <span class="p">></span> <span class="n">slCodecs</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p"><</span> <span class="n">String</span> <span class="p">>(</span> <span class="p">);</span>
<span class="n">slCodecs</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span> <span class="s">"PCMA"</span> <span class="p">);</span>
<span class="k">return</span> <span class="n">slCodecs</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">set</span> <span class="p">{}</span>
<span class="p">}</span>
<span class="n">DNDFlag</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
<span class="n">DefaultAccountIndex</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
<span class="n">IsNull</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
<span class="n">PublishEnabled</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
<span class="n">SIPPort</span> <span class="p">=</span> <span class="m">5060</span><span class="p">;</span>
</code></pre></div> </div>
<p>То же самое сделайте в классе rc_AccountCfg. Введите правильные настройки.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">AccountName</span> <span class="p">=</span> <span class="s">"3000"</span><span class="p">;</span>
<span class="n">DisplayName</span> <span class="p">=</span> <span class="s">"3000"</span><span class="p">;</span>
<span class="n">DomainName</span> <span class="p">=</span> <span class="s">"*"</span><span class="p">;</span>
<span class="n">Enabled</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
<span class="n">HostName</span> <span class="p">=</span> <span class="s">"10.91.25.22"</span><span class="p">;</span>
<span class="n">Id</span> <span class="p">=</span> <span class="s">"3000"</span><span class="p">;</span>
<span class="n">Index</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
<span class="n">Password</span> <span class="p">=</span> <span class="s">"admin"</span><span class="p">;</span>
<span class="n">ProxyAddress</span> <span class="p">=</span> <span class="s">""</span><span class="p">;</span>
<span class="n">RegState</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
<span class="n">TransportMode</span> <span class="p">=</span> <span class="n">ETransportMode</span><span class="p">.</span><span class="n">TM_UDP</span><span class="p">;</span>
<span class="n">UserName</span> <span class="p">=</span> <span class="s">"3000"</span><span class="p">;</span>
</code></pre></div> </div>
</li>
</ol>
<p>Теперь займемся главной формой. Дальше весь код пишется в главной форме Form1, имейте ввиду.</p>
<p>Добавляем импорты</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">using</span> <span class="nn">Sipek.Common</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Sipek.Common.CallControl</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Sipek.Sip</span><span class="p">;</span>
</code></pre></div></div>
<p>Реализуем несколько свойств</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#region properties
</span><span class="n">CCallManager</span> <span class="n">CallManager</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">CCallManager</span><span class="p">.</span><span class="n">Instance</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">private</span> <span class="n">rc_PhoneCfg</span> <span class="n">v_hPhoneCfg</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">rc_PhoneCfg</span><span class="p">(</span> <span class="p">);</span>
<span class="k">internal</span> <span class="n">rc_PhoneCfg</span> <span class="n">Config</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">v_hPhoneCfg</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">private</span> <span class="n">IStateMachine</span> <span class="n">v_hCall</span> <span class="p">=</span> <span class="k">null</span><span class="p">;</span>
<span class="cp">#endregion
</span></code></pre></div></div>
<p>Теперь мы готовы зарегистрировать функции обратного вызова. Делаем в конструкторе</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CallManager</span><span class="p">.</span><span class="n">CallStateRefresh</span> <span class="p">+=</span> <span class="k">new</span> <span class="nf">DCallStateRefresh</span><span class="p">(</span> <span class="n">CallManager_CallStateRefresh</span> <span class="p">);</span>
<span class="n">pjsipRegistrar</span><span class="p">.</span><span class="n">Instance</span><span class="p">.</span><span class="n">AccountStateChanged</span> <span class="p">+=</span> <span class="k">new</span> <span class="nf">DAccountStateChanged</span><span class="p">(</span> <span class="n">Instance_AccountStateChanged</span> <span class="p">);</span>
</code></pre></div></div>
<p>Обработчики реализуем попозже</p>
<p>Теперь мы готовы инициализировать систему Sipek. Для этого делаем следующие шаги</p>
<ol>
<li>
<p>Присваиваем</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CallManager</span><span class="p">.</span><span class="n">StackProxy</span> <span class="p">=</span> <span class="n">pjsipStackProxy</span><span class="p">.</span><span class="n">Instance</span><span class="p">;</span>
</code></pre></div> </div>
</li>
<li>
<p>Отправляем настройки в систему</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CallManager</span><span class="p">.</span><span class="n">Config</span> <span class="p">=</span> <span class="n">Config</span><span class="p">;</span>
<span class="n">pjsipStackProxy</span><span class="p">.</span><span class="n">Instance</span><span class="p">.</span><span class="n">Config</span> <span class="p">=</span> <span class="n">Config</span><span class="p">;</span>
<span class="n">pjsipRegistrar</span><span class="p">.</span><span class="n">Instance</span><span class="p">.</span><span class="n">Config</span> <span class="p">=</span> <span class="n">Config</span><span class="p">;</span>
</code></pre></div> </div>
</li>
<li>
<p>Инициализируем</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CallManager</span><span class="p">.</span><span class="nf">Initialize</span><span class="p">(</span> <span class="p">);</span>
</code></pre></div> </div>
</li>
<li>
<p>И пытаемся зарегистрироваться на сервере</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">pjsipRegistrar</span><span class="p">.</span><span class="n">Instance</span><span class="p">.</span><span class="nf">registerAccounts</span><span class="p">(</span> <span class="p">);</span>
</code></pre></div> </div>
</li>
</ol>
<p>Теперь реализуем обработчики. Чтобы избежать проблему синхронизации потоков, будем использовать Invoke.</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#region callbacks
</span><span class="k">void</span> <span class="nf">Instance_AccountStateChanged</span><span class="p">(</span> <span class="n">Int32</span> <span class="n">iAccountId</span><span class="p">,</span> <span class="n">Int32</span> <span class="n">iAccState</span> <span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">InvokeRequired</span> <span class="p">)</span>
<span class="k">this</span><span class="p">.</span><span class="nf">BeginInvoke</span><span class="p">(</span> <span class="k">new</span> <span class="nf">DAccountStateChanged</span><span class="p">(</span> <span class="n">OnRegistrationUpdate</span> <span class="p">),</span> <span class="k">new</span> <span class="n">Object</span><span class="p">[</span> <span class="p">]</span> <span class="p">{</span> <span class="n">iAccountId</span><span class="p">,</span> <span class="n">iAccState</span> <span class="p">}</span> <span class="p">);</span>
<span class="k">else</span>
<span class="nf">OnRegistrationUpdate</span><span class="p">(</span> <span class="n">iAccountId</span><span class="p">,</span> <span class="n">iAccState</span> <span class="p">);</span>
<span class="p">}</span>
<span class="k">void</span> <span class="nf">CallManager_CallStateRefresh</span><span class="p">(</span> <span class="n">Int32</span> <span class="n">iSessionId</span> <span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">InvokeRequired</span> <span class="p">)</span>
<span class="k">this</span><span class="p">.</span><span class="nf">BeginInvoke</span><span class="p">(</span> <span class="k">new</span> <span class="nf">DCallStateRefresh</span><span class="p">(</span> <span class="n">OnStateUpdate</span> <span class="p">),</span> <span class="k">new</span> <span class="n">Object</span><span class="p">[</span> <span class="p">]</span> <span class="p">{</span> <span class="n">iSessionId</span> <span class="p">}</span> <span class="p">);</span>
<span class="k">else</span>
<span class="nf">OnStateUpdate</span><span class="p">(</span> <span class="n">iSessionId</span> <span class="p">);</span>
<span class="p">}</span>
<span class="cp">#endregion
</span></code></pre></div></div>
<p>И синхронизированные обработчики</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#region synchronized callbacks
</span><span class="k">private</span> <span class="k">void</span> <span class="nf">OnRegistrationUpdate</span><span class="p">(</span> <span class="n">Int32</span> <span class="n">iAccountId</span><span class="p">,</span> <span class="n">Int32</span> <span class="n">iAccState</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">cs_RegState</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">iAccState</span><span class="p">.</span><span class="nf">ToString</span><span class="p">(</span> <span class="p">);</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">void</span> <span class="nf">OnStateUpdate</span><span class="p">(</span> <span class="n">Int32</span> <span class="n">iSessionId</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">cs_CallState</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">CallManager</span><span class="p">.</span><span class="nf">getCall</span><span class="p">(</span> <span class="n">iSessionId</span> <span class="p">).</span><span class="n">StateId</span><span class="p">.</span><span class="nf">ToString</span><span class="p">(</span> <span class="p">);</span>
<span class="p">}</span>
<span class="cp">#endregion
</span></code></pre></div></div>
<p>И обработчики нажатия кнопок</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">void</span> <span class="nf">cs_MakeCall_Click</span><span class="p">(</span> <span class="n">Object</span> <span class="n">hSender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">hArgs</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">v_hCall</span> <span class="p">=</span> <span class="n">CallManager</span><span class="p">.</span><span class="nf">createOutboundCall</span><span class="p">(</span> <span class="n">cs_Phone</span><span class="p">.</span><span class="n">Text</span> <span class="p">);</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">void</span> <span class="nf">cs_Release_Click</span><span class="p">(</span> <span class="n">Object</span> <span class="n">hSender</span><span class="p">,</span> <span class="n">EventArgs</span> <span class="n">hArgs</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">cs_Phone</span><span class="p">.</span><span class="nf">Clear</span><span class="p">(</span> <span class="p">);</span>
<span class="n">CallManager</span><span class="p">.</span><span class="nf">onUserRelease</span><span class="p">(</span> <span class="n">v_hCall</span><span class="p">.</span><span class="n">Session</span> <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Теперь если Вы правильно ввели HostName, UserName и Password в rc_AccountCfg, ваш (или не Ваш) SIP сервер запущен и работает, то в поле RegState вы должны увидеть 200, что означает что ваш софтфон успешно зарегистрировался на SIP сервере.</p>
<p>Набирайте в cs_Phone номер и нажимайте MakeCall. Удачных телефонных переговоров.</p>
<h3 id="Комментарии-со-старой-версии-блога">Комментарии со старой версии блога</h3>
<blockquote>
<p>Nevin:
03.11.2010 в 00:55
Спасибо за статью. единственное замечание: IConfigurationInterface в сегодняшней версии библиотеки называется IConfiguratorInterface</p>
</blockquote>
<hr />
<blockquote>
<p>Alex:
17.11.2010 в 22:33
Спасибо! Отличное руководство =)
На этапе «Теперь мы готовы зарегистрировать функции обратного вызова.» не понятно где делать в конструкторе (где этот констркутор?):</p>
</blockquote>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">allManager</span><span class="p">.</span><span class="n">CallStateRefresh</span> <span class="p">+=</span> <span class="k">new</span> <span class="nf">DCallStateRefresh</span><span class="p">(</span> <span class="n">CallManager_CallStateRefresh</span> <span class="p">);</span>
<span class="n">pjsipRegistrar</span><span class="p">.</span><span class="n">Instance</span><span class="p">.</span><span class="n">AccountStateChanged</span> <span class="p">+=</span> <span class="k">new</span> <span class="nf">DAccountStateChanged</span><span class="p">(</span> <span class="n">Instance_AccountStateChanged</span> <span class="p">);</span>
</code></pre></div></div>
<blockquote>
<p>Прокомментируйте поподробней пожалуйста.
С Уважением, Alex</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
17.11.2010 в 22:49
Я имел в виду конструктора формы. Можете скачать исходники моих экспериментов вот тут <a href="/media/files/freesoftphone.rar">вот</a> Надеюсь это Вам поможет в Ваших изысканиях.
Всегда рад помочь</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
17.11.2010 в 22:50
И да – пост про IConfiguratorInterface очень верный. Теперь он действительно так называется.</p>
</blockquote>
<hr />
<blockquote>
<p>Koks:
21.11.2010 в 08:08
Вроде получилось, регистрируется и звонит, но никак не реагирует на входящий вызов( в чем может быть проблема? Исходники скачал те что выложил автор.</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
21.11.2010 в 13:53
Честно говоря я сам не до конца понял как реагировать на входящий вызов. Глубокий дебаг библиотеки показал, что она реагирует на входящий вызов, но делегат почему-то не вызывает.
Проблема видимо в настройках каких-то, которые мы не учли, либо в недрах библиотеки.</p>
</blockquote>
<hr />
<blockquote>
<p>Koks:
21.11.2010 в 21:02
Самое интересное что и sipek2 вроде бы законченный телефон тоже не реагирует.</p>
</blockquote>
<hr />
<blockquote>
<p>Koks:
21.11.2010 в 21:23
А нельзя соорудить какой нибудь костыль что бы получать входящие? Просто очень надо =)
Сейчас скачаю сырцы библиотеки посмотреть.</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
21.11.2010 в 23:44
sipek2 на основе этой же библиотеки написан. Я не рылся в этом направлении – для моей задачи были нужны только исходящие звонки.
С входящими пробовал – но так ничего и не добился.
Если найдете что-то интересное – дайте знать пожалуйста.</p>
</blockquote>
<hr />
<blockquote>
<p>Koks:
23.11.2010 в 04:13
Только что починил, пересоздав свой евент.
В классе CallManager добавил</p>
</blockquote>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Form</span> <span class="n">OnForm</span> <span class="p">=</span> <span class="k">null</span><span class="p">;</span>
<span class="k">public</span> <span class="k">delegate</span> <span class="k">void</span> <span class="nf">IncCallHandler</span><span class="p">(</span><span class="kt">int</span> <span class="n">SessionId</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Number</span><span class="p">,</span><span class="kt">string</span> <span class="n">Info</span><span class="p">);</span>
<span class="k">delegate</span> <span class="k">void</span> <span class="nf">IncDelegate</span><span class="p">(</span><span class="kt">int</span> <span class="n">SessionId</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Number</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Info</span><span class="p">);</span>
<span class="k">private</span> <span class="k">void</span> <span class="nf">IncEvent</span><span class="p">(</span><span class="kt">int</span> <span class="n">SessionId</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Number</span><span class="p">,</span> <span class="kt">string</span> <span class="n">Info</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">OnForm</span><span class="p">.</span><span class="n">InvokeRequired</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">IncDelegate</span> <span class="n">Invoker</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">IncDelegate</span><span class="p">(</span><span class="n">IncEvent</span><span class="p">);</span>
<span class="n">OnForm</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">(</span><span class="n">Invoker</span><span class="p">,</span> <span class="k">new</span> <span class="kt">object</span><span class="p">[]</span> <span class="p">{</span> <span class="n">SessionId</span><span class="p">,</span> <span class="n">Number</span><span class="p">,</span><span class="n">Info</span> <span class="p">});</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="nf">IncCall</span><span class="p">(</span><span class="n">SessionId</span><span class="p">,</span><span class="n">Number</span><span class="p">,</span><span class="n">Info</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">On_Form</span><span class="p">(</span><span class="n">Form</span> <span class="n">ParentForm</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">OnForm</span> <span class="p">=</span> <span class="n">ParentForm</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">event</span> <span class="n">IncCallHandler</span> <span class="n">IncCall</span><span class="p">;</span>
</code></pre></div></div>
<blockquote>
<p>Дальше изменил там же</p>
</blockquote>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">void</span> <span class="nf">OnIncomingCall</span><span class="p">(</span><span class="kt">int</span> <span class="n">sessionId</span><span class="p">,</span> <span class="kt">string</span> <span class="n">number</span><span class="p">,</span> <span class="kt">string</span> <span class="n">info</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">IStateMachine</span> <span class="n">call</span> <span class="p">=</span> <span class="k">this</span><span class="p">[</span><span class="n">sessionId</span><span class="p">];</span>
<span class="c1">//if (call.IsNull) return;</span>
<span class="c1">// inform automaton for incoming call</span>
<span class="n">call</span><span class="p">.</span><span class="n">State</span><span class="p">.</span><span class="nf">incomingCall</span><span class="p">(</span><span class="n">number</span><span class="p">,</span> <span class="n">info</span><span class="p">);</span>
<span class="c1">// call callback</span>
<span class="nf">IncEvent</span><span class="p">(</span><span class="n">sessionId</span><span class="p">,</span> <span class="n">number</span><span class="p">,</span> <span class="n">info</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<blockquote>
<p>Теперь когда создаем класс
Делаем еще вот так</p>
</blockquote>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CallManager</span><span class="p">.</span><span class="nf">On_Form</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="n">CallManager</span><span class="p">.</span><span class="n">IncCall</span> <span class="p">+=</span> <span class="k">new</span> <span class="n">CCallManager</span><span class="p">.</span><span class="nf">IncCallHandler</span><span class="p">(</span><span class="n">IncCaller</span><span class="p">);</span>
</code></pre></div></div>
<blockquote>
<p>И там например</p>
</blockquote>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">void</span> <span class="nf">IncCaller</span><span class="p">(</span><span class="kt">int</span> <span class="n">SessionId</span><span class="p">,</span> <span class="kt">string</span> <span class="n">number</span><span class="p">,</span> <span class="kt">string</span> <span class="n">info</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">cs_CallState</span><span class="p">.</span><span class="n">Text</span> <span class="p">=</span> <span class="n">number</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<blockquote>
<p>Все работает.</p>
</blockquote>
<hr />
<blockquote>
<p>Koks:
23.11.2010 в 04:51
Не работает еще кнопка ответа, если кто найдет как починить буду благодарен.</p>
</blockquote>
<hr />
<blockquote>
<p>Koks:
23.11.2010 в 05:29
Разбираться в дебрях библиотеки было лень, поэтому вот атким костылем можно и отвечать)</p>
</blockquote>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Sipek</span><span class="p">.</span><span class="n">Sip</span><span class="p">.</span><span class="n">pjsipCallProxy</span><span class="p">.</span><span class="nf">dll_answerCall</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">200</span><span class="p">);</span>
</code></pre></div></div>
<blockquote>
<p>0 это номер сессии</p>
</blockquote>
<hr />
<blockquote>
<p>Koks:
23.11.2010 в 05:34
Теперь еще одна проблема, со звуком он не дружит только у меня?</p>
</blockquote>
<hr />
<blockquote>
<p>Viktor:
15.01.2011 в 21:52
А с какими серверами вы работаете? я вот что-то совсем не могу подружить его с 3CX сервером… ни в какую не хочет регистрироваться… Может кто-нить что подскажет?</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
16.01.2011 в 11:32
Ну лично я работал с Asterisk. Все работало.</p>
</blockquote>
<hr />
<blockquote>
<p>Viktor:
16.01.2011 в 14:26
еще вопрос: что значит regState 171101? нигде не смог найти описания…</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
17.01.2011 в 12:08
171101 я честно говоря не знаю.
А по поводу 3СХ сервера, если бы Вы рассказали проблему и ее решение – было бы оочень замечательно.</p>
</blockquote>
<hr />
<blockquote>
<p>elg:
14.02.2011 в 13:31
А под вистой/семеркой работает?
Почему-то не слышно звука от собеседника. Связано ли с тем, что SipekSdk использует WaveLibMixer, а он не поддерживается в Vista? Можно ли как-то решить эту проблему?</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
16.02.2011 в 15:31
У меня под семеркой работает нормально. Под вистой не пробовал если честно.
Могу посоветовать только проверить настройки в панели управления. У меня были проблемы с микрофоном. Но эти проблемы к SipekSdk не имеют вообще никакого отношения.
Удачи в делах.</p>
</blockquote>
<hr />
<blockquote>
<p>jasja1:
01.03.2011 в 03:23
Запускал на Win7, результат – нулевой, не могу добиться даже регистрации на сервере. Думал дело в ОС, запустил на ВМ WinXP – та же ситуация. Софтфон от 3CX – работает нормально.. перепробовал уже много вариантов.. Буду очень рад, если кто-нибудь даст дельный совет, в чем может быть проблема</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
01.03.2011 в 09:14
Ну можно попробовать. Если Вы выполните два условия:</p>
<ol>
<li>Опишите проблему – что конкретно у Вас не получается</li>
<li>Выложите проблемный код</li>
</ol>
<p>Тогда будет возможность Вам чем-то помочь.</p>
</blockquote>
<hr />
<blockquote>
<p>jasja1:
01.03.2011 в 19:48</p>
<ol>
<li>не получается зарегистрироваться на сервере – после запуска программы абсолютно ничего не происходит</li>
<li>код был полностью взят из <a href="/media/files/freesoftphone.rar">исходника</a>, c настроенным интерфейсом IAccountCfg:</li>
</ol>
</blockquote>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">AccountName</span><span class="p">=</span><span class="s">"100"</span>
<span class="n">DisplayName</span><span class="p">=</span><span class="s">"100"</span>
<span class="n">DomainName</span><span class="p">=</span><span class="s">"*""
</span><span class="n">Enabled</span><span class="p">=</span><span class="k">true</span>
<span class="n">HostName</span><span class="p">=</span><span class="s">"192.168.199.1"</span>
<span class="n">id</span><span class="p">=</span><span class="s">"100"</span>
<span class="n">Index</span><span class="p">=</span><span class="m">0</span>
<span class="n">Password</span><span class="p">=</span><span class="s">"qwerty"</span>
<span class="n">ProxyAddress</span><span class="p">=</span><span class="s">""</span>
<span class="n">RegState</span><span class="p">=</span><span class="m">0</span>
<span class="n">TransportMode</span><span class="p">=</span><span class="n">ETransportMode</span><span class="p">.</span><span class="n">TM_TCP</span>
<span class="n">UserName</span><span class="p">=</span><span class="s">"100"</span>
</code></pre></div></div>
<blockquote>
<p>Установлен и настроен сервер 3cx (Sip порт 5060, Публичный IP: 192.168.199.1).
Добавлен абонент (Внутренний номер 100 Имя 100 Фамилия 100 ID=100 пароль qwerty)</p>
<p>После запуска программы регистрация на сервере не проходит. Лог сервера чист.
Соответственно пробовал софтфоны 3cx и X-Lite 4 – с аналогичными настройками все работает..</p>
</blockquote>
<hr />
<blockquote>
<p>jasja1:
01.03.2011 в 20:06
Часть лога программы</p>
</blockquote>
<pre><code class="language-log">17:55:29.607 pjsua_core.c 1 SIP worker threads created
17:55:29.607 pjsua_core.c pjsua version 1.2 for win32 initialized
17:55:29.611 pjsua_core.c SIP UDP socket reachable at 192.168.0.100:5060
17:55:29.611 udp055775A0 SIP UDP transport started, published address is 192.168.0.100:5060
17:55:29.611 pjsua_acc.c Account added with id 0
17:55:29.611 tcplis SIP TCP listener destroyed
17:55:29.611 pjsua_core.c Error creating SIP TCP listener: Address already in use (WSAEADDRINUSE) [status=130048]
</code></pre>
<blockquote>
<p>Откуда берется IP 192.168.0.100? + решил для эксперимента поменять порт в интерфейсе IPhoneCfg на 5070, на сервере порт остался прежним.. и регистрация прошла – RegState 200.
Не могу понять логику)</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
03.03.2011 в 18:12
Address already in use случается тогда, когда вы биндите сокет (socket bind) на порт, который уже занят (уже был бинд от другого приложения). Поэтому у Вас не получалось приконнектится (видимо какой-то софтфон был запущен и он биндил адрес). Когда Вы адрес поменяли, то все работало.</p>
</blockquote>
<blockquote>
<p>Чтобы работало без смены порта можно сделать следующее</p>
</blockquote>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="n">on</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">s</span> <span class="o">=</span> <span class="n">socket</span><span class="p">(</span> <span class="p">...</span> <span class="p">)</span>
<span class="k">if</span> <span class="p">(</span> <span class="n">setsockopt</span><span class="p">(</span> <span class="n">s</span><span class="p">,</span> <span class="n">SOL_SOCKET</span><span class="p">,</span> <span class="n">SO_REUSEADDR</span><span class="p">,</span> <span class="o">&</span><span class="n">on</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">on</span><span class="p">))</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// случилась ошибка
</span><span class="p">}</span>
</code></pre></div></div>
<blockquote>
<p>Ну или запускать с нестандартным портом…</p>
</blockquote>
<hr />
<blockquote>
<p>Алексей:
22.03.2011 в 00:27
Каким вы сервером пользуетесь??? Я запускаю программу и сервер(какой-то нашел сиповский) и никакой реакции, IP указал 127.0.0.1, пробовал и тот что получают от роутера, ничего не даёт. Никакой реакции по нажатию на кнопки. Обьясните что и как тут вообще??? и что вводить в поле имени кому звонить?</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
22.03.2011 в 19:23
Я пользовался Asterisk’ом. Ставил на сервер и даже звонил через ТФОП.</p>
</blockquote>
<hr />
<blockquote>
<p>Дмитрий:
28.04.2011 в 19:29
Привет, MrDekk.
Очень помогла ваша звонилка. Есть одно «НО». pjsipDLL написан на C++ и когда я пытаюсь запустить приложение под вистой или семеркой x64 возникает ошибка «An attempt was made to load a program with an incorrect format» при попытке обращения к pjsipDLL. Быстро решить эту проблему я не смог, поэтому пишу здесь. Если есть идеи, то я был бы очень благодарен.</p>
</blockquote>
<hr />
<blockquote>
<p>Дмитрий:
28.04.2011 в 19:35
Забыл написать.
Нашел это
http://blogs.msdn.com/b/arvindsh/archive/2009/06/21/tip-of-the-day-an-attempt-was-made-to-load-a-program-with-an-incorrect-format-net-p-invoke-issue.aspx
Долго втыкал, но самостоятельно разобраться не смог. Возможно в этом тесте есть решение проблемы, ноя настолько пень ,что не могу им воспользоваться. Хелп плз.
Заранее большое спасибо.</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
29.04.2011 в 14:12
В настройках компиляции .net проектов вместо ANY CPU поставьте x86 – все должно заработать.</p>
</blockquote>
<hr />
<blockquote>
<p>Дмитрий:
04.05.2011 в 20:48
Спасибо. С этим справился.
Вообще не звонит на windows 7.
После выполнения</p>
</blockquote>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CallManager</span><span class="p">.</span><span class="nf">createOutboundCall</span><span class="p">(</span><span class="n">txtPhone</span><span class="p">.</span><span class="n">Text</span><span class="p">);</span>
</code></pre></div></div>
<blockquote>
<p>событие CallStateRefresh не наступает вообще.
В чем может быть беда?</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
04.05.2011 в 21:51
Ну, файерволл у Вас есть? Может быть в нем беда. Вообще, мне кажется где-то что-то не дает пройти соединению. Можете попробовать включить native дебаггер и пройтись внутрь sipeksdk.</p>
</blockquote>
<hr />
<blockquote>
<p>Дмитрий:
05.05.2011 в 16:37
Буду писать сюда о своих действиях, вдруг у кого-то возникнут идеи как мне помочь. Итак, проблема все та же: не работает наш любимый софтфон под вин 7.
Нашел это:
http://groups.google.com/group/sipek/browse_thread/thread/ca47464ac4dbdb9f?pli=1
Выкачал исходники SipekSDK. В них упомянутой ссылки на WaveLibMixer вообще нет. Добавил в свой проект сборку SipekSDK, чтобы было проще отлаживаться. При отладке наткнулся на следующее.</p>
</blockquote>
<blockquote>
<p>Итак мы жмем на кнопку «позвонить», вызывается</p>
</blockquote>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">v_hCall</span> <span class="p">=</span> <span class="n">CallManager</span><span class="p">.</span><span class="nf">createOutboundCall</span><span class="p">(</span><span class="n">txtPhone</span><span class="p">.</span><span class="n">Text</span><span class="p">);</span>
</code></pre></div></div>
<blockquote>
<p>Проследовал по обработке OutboundCall вплоть до следующей строчки во враппере для pjsipDLL:</p>
</blockquote>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">SessionId</span> <span class="p">=</span> <span class="nf">dll_makeCall</span><span class="p">(</span><span class="n">Config</span><span class="p">.</span><span class="n">Accounts</span><span class="p">[</span><span class="n">accountId</span><span class="p">].</span><span class="n">Index</span><span class="p">,</span> <span class="n">sipuri</span><span class="p">);</span>
</code></pre></div></div>
<blockquote>
<p>Здесь получаю SessionId = -1, что приводит к окончанию обработки звонка. То есть под висту (7) почему-то не работает dll_makeCall из pjsipDLL. Попробовал погуглить эту беду ,пока безрезультатно. Может быть у кого-то запускается вся эта радость на висте или семерке? приму любую помощь.</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
05.05.2011 в 20:48
Вы native-дебаггером в pjsip.dll пробовали заходить?</p>
</blockquote>
<hr />
<blockquote>
<p>Дмитрий:
06.05.2011 в 12:38
Debug->Options->Native. Выставил галочки на Load DLL Imports и Enable RPC Debugging.
После этого я должен иметь возможность заглянуть дебаггером внутрь pjsipDLL? Может быть у меня урезанная студия (хотя нет – вроде Ultimate), но я пройти внутрь pjsipDLL не могу. Что я делаю не так?</p>
</blockquote>
<hr />
<blockquote>
<p>MrDekk:
06.05.2011 в 13:24
В настройках запускаемого шарпового проекта в закладке Debug внизу есть опция Enable Debuggers, там надо поставить галочку напротив Enable unmanaged code debugging. После этого вам нужны исходники pjsip, либо хотя бы pdb файл. Тогда у Вас будет возможность заглянуть внутрь Pjsip.dll</p>
</blockquote>
<hr />
<blockquote>
<p>Дмитрий:
07.06.2011 в 16:02
На всякий случай оставляю комментарий, если у кого будут похожие проблемы.
Итак беда с Виндоус 7 связана с pjsipDLL, которую мы скачиваем. Я выкачал исходники pjsipDLL и пересобрал библиотеку по указаниям отсюда http://sites.google.com/site/sipekvoip/Home/documentation/pjsipwrapper/pjsipwrapper-for-windows
После этого под семеркой нормально зазвонило.</p>
</blockquote>
<hr />
<blockquote>
<p>Богдан:
15.07.2013 в 17:56
Ответ на звонки полностью работоспособен!
Вы забыли реализовать свойство bool AAFlag.
Оно отвечает за автоответ, и проверяется при входящем звонке.
Если его реализовать все будет хорошо, и не нужно ничего в либе менять.</p>
</blockquote>
О Качестве кода2010-04-11T00:00:00+00:00http://mrdekk.ru/2010/04/11/code-quality<p>О Качестве кода</p>
<p>Не помню уже откуда, но попалась мне как то на глаза одна такая картинка про качество кода. Она гласит, что хорош тот код, у которого wtf/минута меньше всего :)</p>
<p><img src="/media/images/wtfperminute.jpg" alt="wtf per minute" /></p>
FreeBSD - подключаем виндовый диск2010-04-08T00:00:00+00:00http://mrdekk.ru/2010/04/08/windows-disk-in-freebsd<p>Как и обещал продолжаю цикл заметок о замене Windows на FreeBSD на почти серверной машине. Сегодняшняя наша цель – подключить виндовый раздел к фре. Сразу стоит отмести ту мысль, что можно было все перевести на ufs и не мучаться. Тут не все так просто, раздел этот размером 1Тб, с заполнением примерно но 80%. Второго такого винта не нашлось для временного перелива информации, поэтому была поставлена задача запустить его под фрей «как есть».</p>
<p>Проблема первая – непонятно какая на нем файловая система (всмысле FAT32 или NTFS). Создавался этот раздел давно и никто уже не помнит. Проблема решается просто – грузимся с любого виндового LiveCD и каким-нибудь Акронисом смотрим. У меня оказалась NTFS. Рекомендую сделать это заранее, т.к. я потратил определенное время пытаясь смонтировать этот винт как FAT32.</p>
<p>Далее нам надо чтобы ядро у нас поддерживало определенную функциональность. Делаем</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kldstat
</code></pre></div></div>
<p>и смотрим чтобы в выдаче был модуль geom_mbr.ko. Если его нет, делаем следующие вещи</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd</span> /usr/src/sys/modules/geom
<span class="nv">$ </span>make <span class="o">&&</span> make obj <span class="o">&&</span> make install clean
<span class="nv">$ </span>kldload geom_mbr
</code></pre></div></div>
<p>кроме того, идем в /boot/loader.conf и прописываем там автозапуск модуля</p>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">geom_mbr_load</span><span class="p">=</span><span class="s">"YES"</span>
</code></pre></div></div>
<p>После этого у нас есть возможность видеть разделы в /dev у меня виндовый раздел был ad12s2s1. Далее так как система у нас NTFS надо надо организовать поддержку NTFS. Для этого воспользуемся fusefs. Идем в /usr/ports, делаем</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd</span> /usr/ports/
<span class="nv">$ </span>make search <span class="nv">name</span><span class="o">=</span>’ntfs’
</code></pre></div></div>
<p>и идем в ту которая от fuse. после этого делаем стандартные</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>make install clean
</code></pre></div></div>
<p>После этого надо прописать fusefs в автозагрузку. Для этого в /etc/rc.conf добавляем</p>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># fusefs
</span><span class="py">fusefs_enable</span><span class="p">=</span><span class="s">»YES»</span>
</code></pre></div></div>
<p>и запускаем</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>/usr/loca/etc/rc.d/fusefs start
</code></pre></div></div>
<p>Монтируем том</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mkdir /d
<span class="nv">$ </span>ntfs-3g <span class="nt">-o</span> rw,locale<span class="o">=</span>ru_RU.UTF-8 /dev/ad12s2s1 /d
</code></pre></div></div>
<p>Чтобы том автоматически подцеплялся при старте системы добавляем строчку в /etc/rc.conf после fusefs</p>
<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># mount ntfs volume
</span><span class="err">/usr/local/bin/ntfs-3g</span> <span class="err">-o</span> <span class="err">rw,</span><span class="py">locale</span><span class="p">=</span><span class="s">ru_RU.UTF-8 /dev/ad12s2s1 /d</span>
</code></pre></div></div>
<p>После этого</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>shutdown <span class="nt">-r</span> now
</code></pre></div></div>
<p>И наслаждаемся. Теперь у нас в фряхе подцеплен виндовый ntfs раздел. Могут быть проблемы с UTF-8 в консоли. Но – это тема следующих статей</p>
Мытарства с FreeBSD2010-04-07T00:00:00+00:00http://mrdekk.ru/2010/04/07/freebsd8-big-problem<p>По производственной необходимости понадобилось поставить на новый компьютер FreeBSD 8.0. Вроде бы все ничего – уже тысячу раз ставили и все нормально. Ан нет. Сразу при старте появились ошибки вида</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>acd0: TIMEOUT READ_BIG FAILURE
</code></pre></div></div>
<p>Это так себя ведут некоторые контроллеры приводов. Пришлось устраивать танцы с бубнами и ковырять BIOS. Увы уже не помню что сделал чтоб заработало, поэтому привести не смогу.</p>
<p>После всего этого система поставилась и даже заработала. Подхватив всё имеющееся оборудование. Однако не тут то было. Оказалось что система игнорирует часть оперативной памяти. (В машинке установлено ее аж 4 Гига). С сообщением вроде</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>786432К of memory above 4GB ignored
</code></pre></div></div>
<p>Проблема скорее всего в том, что используется архитектура i386, а у нее ввиде 32-битности есть некоторые ограничения. Долгое общение с гуглом дало следующий результат – проблему можно попробовать решить двумя способами:</p>
<ol>
<li>Скомпилировать ядро с поддержкой PAE.</li>
<li>Поставить FreeBSD на архитектуре AMD64</li>
</ol>
<p>Второго варианта я как то сначала испугался, ибо то процессор у меня Intel Core 2 Duo E8400. Поэтому решил попробовать вариант первый. Сказано сделано, прописываем в конфиге ядра</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>options PAE
</code></pre></div></div>
<p>и вперед</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>make buildkernel
</code></pre></div></div>
<p>Сразу хочу огорчить. После этого память вроде стала использоваться вся, однако началось периодическое отпадывание аппаратуры, поэтому лично я этот режим бы не советовал.</p>
<p>После чего начал курить мануалы по amd64. Оказалось, что так она называется потому, что придумала эту архитектуру фирма AMD. Но на данный момент она поддерживается и процессорами Intel тоже. Мой Core 2 Duo судя по материалам поддерживается.</p>
<p>Чтож, заслал в скачку FreeBSD-8.0-Release-amd64. О результатах сообщу позже.</p>
<h4 id="updated">UPDATED.</h4>
<p>Установка прошла практически без сучка и задоринки. Система встала и начала работать. По ощущениям даже несколько быстрее. Оперативная память теперь видна и доступна вся.</p>
<p>Задача следующая – запустить под фрей раздел который раньше работал под WindowsXP. Пока непонятно – какая файловая система на нем (Fat32, Ntfs). Задача поставлена такая – нельзя его преобразовывать в ufs например, он должен обязательно остаться таким каким был, но на фряхе его надо использовать.</p>
Убираем надпись «Powered By…» в PrestaShop2010-03-15T00:00:00+00:00http://mrdekk.ru/2010/03/15/prestashop-poweredby<p>Сие конечно делать не хорошо, но если уж вам очень надо.</p>
<p>Чтобы убрать надпись «PoweredBy …» в PrestaShop, надо отредактировать файлик</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/modules/blockvariouslinks/blockvariouslinks.tpl
</code></pre></div></div>
<p>где найти и удалить соответствующую строку. После этого надпись появляться не будет.</p>
<p>Обратите внимание на используемую лицензию – она может запрещать делать подобные вещи.</p>
Проблема Subversion+Apache22010-03-02T00:00:00+00:00http://mrdekk.ru/2010/03/02/subversion-apache-problem<p>Сегодня появилась необходимость установить Subversion с доступом через Apache2. Установку Apache2 в данной статье рассматривать не буду. Subversion ставим следующим образом:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>сd /usr/ports/devel/subversion
<span class="nv">$ </span>make install clean
</code></pre></div></div>
<p>Далее прописываем в настройках виртуального хоста Apache 2.2 следующую конфигурацию</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code><Location /svnpublic>
DAV svn
SVNPath /dt/svn/public
AuthType Basic
AuthName "iLLi Public SVN repository"
AuthUserFile "/dt/svn/svn_public.auth"
AuthzSVNAccessFile "/dt/svn/svn_public.authz"
Require valid-user
</Location>
</code></pre></div></div>
<p>Перезагружаем Apache 2 и все бы вроде бы ничего не возникает досадная ошибка couldn’t check user. No user file… Не спешите расстраиваться – это все потому, что не подключено пару модулей, как</p>
<div class="language-apache highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">LoadModule</span> authn-file-module libexec/apache22/mod-authn-file.so
<span class="nc">LoadModule</span> authz-host-module libexec/apache22/mod-authz-host.so
<span class="nc">LoadModule</span> auth-basic-module libexec/apache22/mod-auth-basic.so
</code></pre></div></div>
<p>После перезагрузки Apache2 все работает как надо</p>
Kohana: Требования к серверу2010-03-01T00:00:00+00:00http://mrdekk.ru/2010/03/01/kohana-server-requirements<h3 id="Основные-требования">Основные требования:</h3>
<p>Впринципе кохана должна запускаться практически везде. Однако стоит придерживаться следующих требований:</p>
<ol>
<li>Сервер должен поддерживать Unicode</li>
<li>PHP версии не ниже 5.2.3</li>
<li>HTTP сервер. Гарантируется, что кохана будет работать с Apache 1.3+, Apache 2.0+ , lighthttpd, MS IIS. (Лично от себя IIS не советую)</li>
</ol>
<p>Если Вы хотите использовать кохану с базой данных, Вам также нужен сервер баз данных. У коханы имеются встроенные драйвера поддержки MySQL и PostgreSQL, также планируютсядополнительные для других СУБД.</p>
<hr />
<h3 id="Требуемые-расширения">Требуемые расширения:</h3>
<ol>
<li>PCRE должен быть скомпилирован с флагами –enable-utf8 –enable-unicode-properties для поддержки UTF8.</li>
<li>Для транслитерации UTF-8 требуется iconv</li>
<li>Для шифрования требуется mcrypt</li>
<li>Для некоторых библиотек ядра требуется SPL.</li>
</ol>
<h3 id="Рекомендуемые-расширения">Рекомендуемые расширения:</h3>
<ol>
<li>mbstring серьезно ускоряет производительность функций коханы связанных с UTF8. Однако, расширение mbstring не должно перегружать стандартные функции PHP для работы со строками!</li>
</ol>
Kohana: Базовая установка2010-03-01T00:00:00+00:00http://mrdekk.ru/2010/03/01/kohana-basic-install<p>В идеале кохана устанавливается быстро и просто, для этого надо проделать следующие действия:</p>
<ol>
<li>Скачать дистрибутив с официального сайта</li>
<li>Распаковать содержимое дистрибутива туда, что будет являться корнем сайта (веб-приложения, …).</li>
<li>Отредактировать файл глобальной конфигурации по адресу application/config/config.php, отразить в нем базовый путь к вашему сайту. (Если вдруг там такого файла не оказалось – можно взять из предыдущих дистрибутивов, вполне вероятно что подойдет. На момент написания статьи я пробовал дистрибутив 3.0.3, там такого файла не оказалось, пока работаю с файлом от 2.3.4).</li>
<li>
<p>В зависимости от платформы, возможно, потребуется назначить права. Выполните что-то вроде:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>find <span class="nb">.</span> <span class="nt">-type</span> d <span class="nt">-exec</span> chmod 0755 <span class="o">{</span> <span class="o">}</span> <span class="se">\;</span>
</code></pre></div> </div>
<p>из корневой директории коханы</p>
</li>
<li>Убедитесь что папки application/logs и application/cache доступны для записи. Выполните chmod 666.</li>
<li>Проверте вашу инсталляцию путем открытия url вашего сайта в вашем любимом браузере. Если Вы увидите страницу приветствия или надпись Hello, World! значит установка прошла успешно.</li>
</ol>
Открыл блог :)2010-02-26T00:00:00+00:00http://mrdekk.ru/2010/02/26/blog-opened<p>Ура, ура, ура! Открыл свой блог. Буду сюды вот писать что-нибудь интересное. Ну а пока всем здравствуйте!</p>