Jiaxianhua2016-05-04T06:40:18+00:00http://jiaxianhua.github.ioXianhua Jiajiaxianhua.comgithub 上的一个流行的编程书籍索引2015-09-26T00:00:00+00:00http://jiaxianhua.github.io/book/2015/09/26/free-programming-books-zh_cn-3<h2>原文</h2>
<hr />
<p><a href="https://github.com/vhf/free-programming-books/blob/master/free-programming-books-zh.md">https://github.com/vhf/free-programming-books/blob/master/free-programming-books-zh.md</a></p>
<h1>免费的编程中文书籍索引</h1>
<p>免费的编程中文书籍索引,欢迎投稿。</p>
<ul>
<li>国外程序员在 <a href="http://stackoverflow.com/a/1713/343194">stackoverflow</a> 推荐的程序员必读书籍,<a href="http://justjavac.com/other/2012/05/15/qualified-programmer-should-read-what-books.html" title="一个合格的程序员应该读过哪些书">中文版</a>。</li>
<li><a href="http://stackoverflow.com/q/38210/343194">stackoverflow</a> 上的程序员应该阅读的非编程类书籍有哪些? <a href="what-non-programming-books-should-programmers-read.md">中文版</a></li>
<li><a href="https://github.com/vhf/free-programming-books">github</a> 上的一个流行的编程书籍索引 <a href="https://github.com/vhf/free-programming-books/blob/master/free-programming-books-zh.md">中文版</a></li>
</ul>
<p>感谢 <a href="https://github.com/siberiawolf">@siberiawolf</a> 使用 Bootstrap 开发了网页版,地址:http://siberiawolf.com/free_programming/index.html</p>
<h2>参与交流</h2>
<p>欢迎大家将珍藏已久的经典免费书籍共享出来,您可以:</p>
<ul>
<li>使用 <a href="https://github.com/justjavac/free-programming-books-zh_CN/issues">Issues</a> 以及 Pull Request</li>
</ul>
<p>贡献者名单: https://github.com/justjavac/free-programming-books-zh_CN/graphs/contributors</p>
<h3>目录</h3>
<hr />
<ul>
<li><a href="#%E8%AF%AD%E8%A8%80%E6%97%A0%E5%85%B3">语言无关</a></li>
<li><a href="#%E5%9C%A8%E7%BA%BF%E6%95%99%E8%82%B2">在线教育</a></li>
<li><a href="#%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E6%96%B9%E6%B3%95">软件开发方法</a></li>
<li><a href="#%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F">操作系统</a></li>
<li><a href="#%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6">版本控制</a></li>
<li><a href="#%E6%95%B0%E6%8D%AE%E5%BA%93">数据库</a></li>
<li><a href="#%E6%99%BA%E8%83%BD%E7%B3%BB%E7%BB%9F">智能系统</a></li>
<li><a href="#%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F">正则表达式</a></li>
<li><a href="#c">C</a></li>
<li><a href="#c-1">C++</a></li>
<li><a href="#coffeescript">CoffeeScript</a></li>
<li><a href="#elasticsearch">Elasticsearch</a></li>
<li><a href="#erlang">Erlang</a></li>
<li><a href="#fortran">Fortran</a></li>
<li><a href="#golang">Golang</a></li>
<li><a href="#haskell">Haskell</a></li>
<li><a href="#html--css">HTML / CSS</a></li>
<li><a href="#javascript">JavaScript</a></li>
<li><a href="#latex">LaTeX</a></li>
<li><a href="#lisp">LISP</a></li>
<li><a href="#lua">Lua</a></li>
<li><a href="#markdown">Markdown</a></li>
<li><a href="#nodejs">Node.js</a></li>
<li><a href="#perl">Perl</a></li>
<li><a href="#php">PHP</a></li>
<li><a href="#python">Python</a></li>
<li><a href="#r">R</a></li>
<li><a href="#restructuredtext">reStructuredText</a></li>
<li><a href="#ruby">Ruby</a></li>
<li><a href="#scala">Scala</a></li>
<li><a href="#scheme">Scheme</a></li>
<li><a href="#shell">Shell</a></li>
<li><a href="#swift">Swift</a></li>
<li><a href="#vim">Vim</a></li>
<li><a href="#visual-prolog">Visual Prolog</a></li>
<li><a href="#web">Web</a></li>
</ul>
<h3>语言无关</h3>
<h4>在线教育</h4>
<ul>
<li><a href="https://www.codeschool.com/">CodeSchool</a></li>
<li><a href="http://www.codecademy.com/?locale_code=zh">Codecademy</a></li>
<li><a href="https://www.coursera.org/courses?orderby=upcoming&lngs=zh">Coursera</a></li>
<li><a href="http://learnxinyminutes.com/">Learn X in Y minutes</a> (数十种语言快速入门教程)</li>
<li><a href="http://ocw.mit.edu/courses/translated-courses/simplified-chinese/">MIT 公开课</a></li>
<li><a href="http://teamtreehouse.com/">TeamTreeHouse</a></li>
<li><a href="https://www.udacity.com/">Udacity</a></li>
<li><a href="https://www.xuetangx.com/">xuetangX</a></li>
<li><a href="http://www.imooc.com/course/list">慕课网</a>(丰富的移动端开发、php开发、web前端、html5教程以及css3视频教程等课程资源)</li>
<li><a href="http://www.jikexueyuan.com/">极客学院</a></li>
<li><a href="http://edu.51cto.com/">51CTO学院</a></li>
</ul>
<h4>软件开发方法</h4>
<ul>
<li><a href="https://github.com/justinyhuang/Functional-Programming-For-The-Rest-of-Us-Cn">傻瓜函数编程</a> (《Functional Programming For The Rest of Us》中文版)</li>
<li><a href="http://www.infoq.com/cn/minibooks/scrum-xp-from-the-trenches">硝烟中的 Scrum 和 XP</a></li>
</ul>
<h4>操作系统</h4>
<ul>
<li><a href="http://happypeter.github.io/LGCB/book/">Linux Guide for Complete Beginners</a></li>
<li><a href="http://cb.vu/unixtoolbox_zh_CN.xhtml">UNIX TOOLBOX</a></li>
<li><a href="http://vbird.dic.ksu.edu.tw/linux_basic/linux_basic.php">鸟哥的 Linux 私房菜 基础学习篇</a></li>
<li><a href="http://vbird.dic.ksu.edu.tw/linux_server/">鸟哥的 Linux 私房菜 服务器架设篇</a></li>
<li><a href="http://pages.cs.wisc.edu/~remzi/OSTEP/">Operating Systems: Three Easy Pieces</a></li>
<li><a href="https://www.gitbook.com/book/objectkuan/ucore-docs/details">uCore Lab: Operating System Course in Tsinghua University</a></li>
</ul>
<h4>版本控制</h4>
<ul>
<li><a href="http://rogerdudler.github.io/git-guide/index.zh.html">Git - 简易指南</a></li>
<li><a href="http://gitbook.liuhui998.com/">Git Community Book 中文版</a></li>
<li><a href="http://www-cs-students.stanford.edu/~blynn/gitmagic/intl/zh_cn/">Git magic</a></li>
<li><a href="http://gitref.justjavac.com/">Git 参考手册</a></li>
<li><a href="https://github.com/gotgit/gotgithub">Got GitHub</a></li>
<li><a href="http://git-scm.com/book/zh">Pro Git</a></li>
<li><a href="http://pcottle.github.io/learnGitBranching/">学习 Git 分支</a> (点击右下角按钮可切换至简体及正体中文)</li>
<li><a href="http://igit.linuxtoy.org/index.html">沉浸式学 Git</a></li>
</ul>
<h4>数据库</h4>
<ul>
<li><a href="http://www.redisbook.com">Redis 设计与实现</a></li>
<li><a href="https://github.com/justinyhuang/the-little-mongodb-book-cn">The Little MongoDB Book 中文版</a></li>
</ul>
<h4>智能系统</h4>
<ul>
<li><a href="https://github.com/gmszone/designiot">一步步搭建物联网系统</a></li>
</ul>
<h4>正则表达式</h4>
<ul>
<li><a href="http://deerchao.net/tutorials/regex/regex.htm">正则表达式30分钟入门教程</a></li>
</ul>
<h3>C</h3>
<ul>
<li><a href="http://c-faq-chn.sourceforge.net/ccfaq/ccfaq.html">C 语言常见问题集</a></li>
<li><a href="http://doc.lellansin.com/">C/C++ 学习教程</a></li>
<li><a href="https://github.com/limingth/NCCL">新概念 C 语言教程</a></li>
<li><a href="http://docs.linuxtone.org/ebooks/C&CPP/c/">Linux C 编程一站式学习</a></li>
</ul>
<h3>C++</h3>
<ul>
<li><a href="https://github.com/wuye9036/CppTemplateTutorial">C++ Template 进阶指南</a></li>
<li><a href="http://www.prglab.com/cms/">C++ 基础教程</a></li>
<li><a href="https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial">C++ 并发编程指南</a></li>
<li><a href="http://www.ituring.com.cn/book/1203">像计算机科学家一样思考(C++版)</a> (《How To Think Like a Computer Scientist: C++ Version》中文版)</li>
</ul>
<h3>CoffeeScript</h3>
<ul>
<li><a href="http://island205.github.io/coffeescript-cookbook.github.com/">CoffeeScript Cookbook</a></li>
<li><a href="http://coffee-script.org/">CoffeeScript 中文</a></li>
<li><a href="http://island205.github.io/tlboc/">CoffeeScript 中文手册</a> (《The Little Book on CoffeeScript》中文版)</li>
<li><a href="https://github.com/elrrrrrrr/coffeescript-style-guide/blob/master/README-ZH.md">CoffeeScript 编程风格指南</a></li>
</ul>
<h3>Elasticsearch</h3>
<ul>
<li><a href="https://github.com/looly/elasticsearch-definitive-guide-cn">Elasticsearch 权威指南</a> (《Elasticsearch the definitive guide》中文版)</li>
</ul>
<h3>Erlang</h3>
<ul>
<li><a href="https://github.com/liancheng/cpie-cn">Erlang 并发编程</a> (《Concurrent Programming in Erlang (Part I)》中文版)</li>
</ul>
<h3>Fortran</h3>
<ul>
<li><a href="http://micro.ustc.edu.cn/Fortran/ZJDing/">Fortran77和90/95编程入门</a></li>
</ul>
<h3>Golang</h3>
<ul>
<li><a href="https://github.com/astaxie/build-web-application-with-golang">Go Web 编程</a></li>
<li><a href="https://github.com/Unknwon/the-way-to-go_ZH_CN">Go 入门指南</a> (《The Way to Go》中文版)</li>
<li><a href="http://go-tour-zh.appsp0t.com/">Go 指南</a> (《A Tour of Go》中文版)</li>
<li><a href="https://github.com/Unknwon/go-fundamental-programming">Go 编程基础</a></li>
<li><a href="https://github.com/mikespook/Learning-Go-zh-cn">学习 Go 语言</a></li>
</ul>
<h3>Haskell</h3>
<ul>
<li><a href="http://learnyouahaskell-zh-tw.csie.org/">Haskell 趣学指南</a></li>
<li><a href="http://rwh.readthedocs.org/en/latest/">Real World Haskell 中文版</a></li>
</ul>
<h3>HTML / CSS</h3>
<ul>
<li><a href="http://www.w3school.com.cn/html5/">HTML5 教程</a></li>
<li><a href="http://zh.learnlayout.com/">学习 CSS 布局</a></li>
<li><a href="http://css.doyoe.com/">CSS参考手册</a></li>
</ul>
<h3>Javascript</h3>
<ul>
<li><a href="http://bonsaiden.github.io/JavaScript-Garden/zh/">Javascript Garden</a></li>
<li><a href="http://typeof.net/s/jsmech/">Javascript 原理</a></li>
<li><a href="http://javascript.ruanyifeng.com/">JavaScript 标准参考教程</a></li>
<li><a href="http://es6.ruanyifeng.com/">ECMAScript 6入门</a></li>
</ul>
<h3>LaTeX</h3>
<ul>
<li><a href="http://www.dralpha.com/zh/tech/tech.htm">LaTeX 笔记</a></li>
<li><a href="http://ctan.org/pkg/lshort-zh-cn">一份不太简短的 LaTeX2ε 介绍</a></li>
<li><a href="http://web.math.isu.edu.tw/yeh/HowTo/HowToTex/latex123.pdf">大家來學 LaTeX</a></li>
</ul>
<h3>LISP</h3>
<ul>
<li><a href="http://acl.readthedocs.org/en/latest/">ANSI Common Lisp 中文翻译版</a></li>
<li><a href="http://www.ituring.com.cn/minibook/862">Common Lisp 高级编程技术</a> (《On Lisp》中文版)</li>
</ul>
<h3>Lua</h3>
<ul>
<li><a href="http://www.w3cschool.cc/manual/lua53doc/contents.html">Lua 5.3 参考手册</a></li>
</ul>
<h3>Markdown</h3>
<ul>
<li><a href="http://wowubuntu.com/markdown/basic.html">Markdown 快速入门</a></li>
<li><a href="http://jianshu.io/p/7bd23251da0a">Markdown 简明教程</a></li>
<li><a href="http://wowubuntu.com/markdown/">Markdown 语法说明</a></li>
<li><a href="http://jianshu.io/p/q81RER">献给写作者的 Markdown 新手指南</a></li>
</ul>
<h3>Node.js</h3>
<ul>
<li><a href="http://www.nodebeginner.org/index-zh-cn.html">Node 入门</a></li>
<li><a href="https://www.gitbook.com/book/0532/nodejs/details">The NodeJS 中文文档</a>(社区翻译)</li>
<li><a href="http://nqdeng.github.io/7-days-nodejs/">七天学会NodeJS</a> 阿里出品,很好的入门资料</li>
</ul>
<h3>Perl</h3>
<ul>
<li><a href="https://github.com/fayland/chinese-perl-book">Master Perl Today</a></li>
<li><a href="http://www.cbi.pku.edu.cn/chinese/documents/perl/index.htm">Perl 5 教程</a></li>
<li><a href="http://www.yiibai.com/perl">Perl 教程</a></li>
<li><a href="https://github.com/horus/modern_perl_book">《Modern Perl》中文版</a></li>
</ul>
<h3>PHP</h3>
<ul>
<li><a href="http://www.php-internals.com/book/">深入理解 PHP 内核</a></li>
<li><a href="http://php.net/manual/zh/">PHP5中文手册</a></li>
<li><a href="http://www.walu.cc/phpbook/preface.md">PHP扩展开发及内核应用</a></li>
<li><a href="http://wusuopu.gitbooks.io/symfony2_tutorial/content">Symfony2 实例教程</a></li>
<li><a href="http://wulijun.github.io/php-the-right-way/">PHP 之道</a></li>
</ul>
<h3>Python</h3>
<ul>
<li><a href="http://djangobook.py3k.cn/2.0/">Django book 2.0</a></li>
<li><a href="http://docspy3zh.readthedocs.org/en/latest/">Python 3 文档(简体中文) 3.2.2 documentation</a></li>
<li><a href="http://woodpecker.org.cn/diveintopython/">深入 Python</a></li>
<li><a href="http://woodpecker.org.cn/diveintopython3/">深入 Python 3</a></li>
<li><a href="http://sebug.net/paper/books/LearnPythonTheHardWay/">笨办法学 Python</a></li>
<li><a href="http://woodpecker.org.cn/abyteofpython_cn/chinese/">简明 Python 教程</a> (《A Byte of Python》中文版)</li>
</ul>
<h3>R</h3>
<ul>
<li><a href="http://cran.r-project.org/doc/contrib/Liu-FAQ.pdf">153分钟学会 R </a></li>
<li><a href="http://cran.r-project.org/doc/contrib/Ding-R-intro_cn.pdf">R 导论</a> (《An Introduction to R》中文版)</li>
<li><a href="http://www.biosino.org/R/R-doc/files/R4beg_cn_2.0.pdf">《R for beginners》中文版</a></li>
<li><a href="http://yanping.me/shiny-tutorial/">用 R 构建 Shiny 应用程序</a> (《Building 'Shiny' Applications with R》中文版)</li>
<li><a href="http://cran.r-project.org/doc/contrib/Xu-Statistics_and_R.pdf">统计学与 R 读书笔记</a></li>
</ul>
<h3>reStructuredText</h3>
<ul>
<li><a href="http://www.pythondoc.com/sphinx/rest.html">reStructuredText 入门</a></li>
<li><a href="http://jwch.sdut.edu.cn/book/rst.html">reStructuredText 简明教程</a></li>
</ul>
<h3>Ruby</h3>
<ul>
<li><a href="https://github.com/JuanitoFatas/rails-style-guide/blob/master/README-zhCN.md">Rails 风格指南</a></li>
<li><a href="http://railstutorial-china.org/">Ruby on Rails Tutorial 原书第 2 版</a></li>
<li><a href="http://ihower.tw/rails4/">Ruby on Rails 实战圣经</a></li>
<li><a href="https://github.com/JuanitoFatas/ruby-style-guide/blob/master/README-zhCN.md">Ruby 风格指南</a></li>
<li><a href="http://lrthw.github.io/">笨方法学 Ruby</a></li>
</ul>
<h3>Scala</h3>
<ul>
<li><a href="http://twitter.github.io/effectivescala/index-cn.html">Effective Scala</a></li>
<li><a href="http://twitter.github.io/scala_school/zh_cn/index.html">Scala 课堂</a> (Twitter的Scala中文教程)</li>
<li><a href="https://www.gitbook.com/book/windor/beginners-guide-to-scala/details">Scala 初学者指南</a> (The Neophyte's Guide to Scala)</li>
</ul>
<h3>Scheme</h3>
<ul>
<li><a href="http://deathking.github.io/yast-cn/">Scheme 入门教程</a> (《Yet Another Scheme Tutorial》中文版)</li>
<li><a href="http://r6rs.mrliu.org/">算法语言Scheme修订<sup>6</sup>报告</a>(R<sup>6</sup>RS简体中文翻译)</li>
</ul>
<h3>Shell</h3>
<ul>
<li><a href="http://wiki.ubuntu.org.cn/Shell%E7%BC%96%E7%A8%8B%E5%9F%BA%E7%A1%80">Shell 编程基础</a></li>
<li><a href="https://github.com/qinjx/30min_guides/blob/master/shell.md">Shell 脚本编程30分钟入门</a></li>
<li><a href="http://billie66.github.io/TLCL/book/zh">The Linux Command Line 中文版</a></li>
</ul>
<h3>Swift</h3>
<ul>
<li><a href="https://www.gitbook.io/book/numbbbbb/-the-swift-programming-language-">《The Swift Programming Language》中文版</a></li>
</ul>
<h3>Vim</h3>
<ul>
<li><a href="http://www.study-area.org/tips/vim/index.html">大家來學 VIM</a></li>
<li><a href="http://man.chinaunix.net/newsoft/vi/doc/help.html">Vim Manual(中文版)</a></li>
</ul>
<h3>Visual Prolog</h3>
<ul>
<li><a href="http://wiki.visual-prolog.com/index.php?title=A_Beginners_Guide_to_Visual_Prolog_in_Chinese">Visual Prolog 7初学指南</a></li>
<li><a href="http://wiki.visual-prolog.com/index.php?title=Visual_Prolog_for_Tyros_in_Chinese">Visual Prolog 7边练边学</a></li>
</ul>
<h3>Web</h3>
<ul>
<li><a href="http://happypeter.github.io/tealeaf-http">HTTP 下午茶</a></li>
</ul>
程序员应该阅读的非编程类书籍有哪些2015-09-26T00:00:00+00:00http://jiaxianhua.github.io/book/2015/09/26/free-programming-books-zh_cn-2<h2>原文</h2>
<hr />
<p><a href="https://github.com/justjavac/free-programming-books-zh_CN/blob/master/what-non-programming-books-should-programmers-read.md">https://github.com/justjavac/free-programming-books-zh_CN/blob/master/what-non-programming-books-should-programmers-read.md</a></p>
<h1>免费的编程中文书籍索引</h1>
<p>免费的编程中文书籍索引,欢迎投稿。</p>
<ul>
<li>国外程序员在 <a href="http://stackoverflow.com/a/1713/343194">stackoverflow</a> 推荐的程序员必读书籍,<a href="http://justjavac.com/other/2012/05/15/qualified-programmer-should-read-what-books.html" title="一个合格的程序员应该读过哪些书">中文版</a>。</li>
<li><a href="http://stackoverflow.com/q/38210/343194">stackoverflow</a> 上的程序员应该阅读的非编程类书籍有哪些? <a href="what-non-programming-books-should-programmers-read.md">中文版</a></li>
<li><a href="https://github.com/vhf/free-programming-books">github</a> 上的一个流行的编程书籍索引 <a href="https://github.com/vhf/free-programming-books/blob/master/free-programming-books-zh.md">中文版</a></li>
</ul>
<p>感谢 <a href="https://github.com/siberiawolf">@siberiawolf</a> 使用 Bootstrap 开发了网页版,地址:http://siberiawolf.com/free_programming/index.html</p>
<h2>参与交流</h2>
<p>欢迎大家将珍藏已久的经典免费书籍共享出来,您可以:</p>
<ul>
<li>使用 <a href="https://github.com/justjavac/free-programming-books-zh_CN/issues">Issues</a> 以及 Pull Request</li>
</ul>
<p>贡献者名单: https://github.com/justjavac/free-programming-books-zh_CN/graphs/contributors</p>
<h3>程序员应该阅读的非编程类书籍有哪些</h3>
<p>在 stackoverflow 上有人提问 <a href="http://stackoverflow.com/q/38210/343194">程序员应该阅读的非编程类书籍有哪些?</a> 本来只想整理编程类书籍,
不过突然眼前一亮,发现了《The Art of War - Sun Tzu》回答者的推荐说明引用 Wikipedia 上的:</p>
<p>亚马逊提供免费的 Kindle 版读本:<a href="http://www.amazon.cn/gp/product/B00AA7KMKG/ref=as_li_ss_tl?ie=UTF8&camp=536&creative=3132&creativeASIN=B00AA7KMKG&linkCode=as2&tag=favbook-23">孙子兵法</a></p>
<blockquote><p>Much of the text is about how to fight wars without actually having to do battle:
it gives tips on how to outsmart one's opponent so that physical battle is not necessary.
As such, it has found application as a training guide for many competitive endeavors that do not involve actual combat.</p>
<p>This knowledge would surely be useful in the everyday "battles" we have to fight in and out of the office.
It's also filled with quotes you can impress your fellow programmers with... :)</p></blockquote>
<h2>《哥德尔、艾舍尔、巴赫——集异璧之大成》Gödel, Escher, Bach: an Eternal Golden Braid,</h2>
<p>这本书通常被称为<a href="http://www.amazon.cn/gp/product/B0049MPCAS/ref=as_li_ss_tl?ie=UTF8&camp=536&creative=3132&creativeASIN=B0049MPCAS&linkCode=as2&tag=favbook-23">《GEB》</a>,
它绝对是一本神书,一本奇书,一本神奇的书。在豆瓣读书的科普类排名中稳居第一。我在博客中,和即将出版的书中,也一而再,再而三的提及此书。</p>
<p>书有点儿厚,而且价格不菲,大概五六十吧。我也曾经不止一次的向朋友们推荐此书,并赠书此书。</p>
<p>作者也乘中文版出版之际,为自己取了一个雅致的汉名──侯世达(Douglas Richard Hofstadter)。侯世达应该是 Hofstadter 的音译。</p>
<p>如果你喜爱理科,此书必读。如果你是文科,那就读读《银河系漫游指南》。</p>
<h2>《银河系漫游指南》The Hitchhiker's Guide to the Galaxy</h2>
<p>亚马逊翻译为<a href="http://www.amazon.cn/gp/product/B00590XCO2/ref=as_li_ss_tl?ie=UTF8&camp=536&creative=3132&creativeASIN=B00590XCO2&linkCode=as2&tag=favbook-23">《银河系搭车客指南》</a>,
略带喜感。</p>
<p>突如其来的寂静笼罩了地球。</p>
<p>这事实上比噪音更加可怕。</p>
<p>有一会儿,什么也没有发生。</p>
<p>巨大的飞船一动不动地挂在空中,覆盖了地球上的每个国家。</p>
<p>在黯然退场之前,地球首先被改造成了最终极的声音重放器件,这是有史以来建造过的最伟大的播音系统。</p>
<p>但伴之而来的不是演奏会,不是音乐,没有开场号曲,而仅仅是一条简短的信息。</p>
<p>“地球人,请注意了。”
一个声音说,这声音堪称完美,仿佛来自四声道系统,完美得无懈可击,失真度低得能让勇敢的男人洒下眼泪。</p>
<p>“这里是银河超空间规划委员会。诸位无疑已经知道,银河系边远地区的开发规划要求建造一条穿过贵恒星系的超空间快速通道,令人遗憾的是,贵行星属于计划中预定毁灭的星球之一。毁灭过程将在略少于贵地球时间两分钟后开始。谢谢合作。”</p>
<h2>《人性的弱点》How to Win Friends and Influence People</h2>
<p><a href="http://www.amazon.cn/gp/product/B008F5WMEE/ref=as_li_ss_tl?ie=UTF8&camp=536&creative=3132&creativeASIN=B008F5WMEE&linkCode=as2&tag=favbook-23">《人性的弱点 Kindle版》</a>只售 2.9 元。</p>
<p><a href="http://www.amazon.cn/gp/product/B00119B1AM/ref=as_li_ss_tl?ie=UTF8&camp=536&creative=3132&creativeASIN=B00119B1AM&linkCode=as2&tag=favbook-23">《人性的弱点》</a>的作者戴尔·卡耐基,美国“成人教育之父”。
20世纪早期,美国经济陷入萧条,战争和贫困导致人们失去了对美好生活的愿望,而卡耐基独辟蹊径地开创了一套融演讲、推销、为人处世、智能开发于一体的教育方式,他运用社会学和心理学知识,对人性进行了深刻的探讨和分析。
《人性的弱点》讲述的许多普通人通过奋斗获得成功的真实故事,激励了无数陷和迷茫和困境的人,帮助他们重新找到了自己的人生。</p>
<p>接受卡耐基教育的有社会各界人士,其中不乏军政要员,甚至包括几位美国总结。
千千万万的人从卡耐基的教育中获益匪浅。</p>
<p>《人性的弱点》汇集了卡耐基的思想精华和最激动人心的内容,是作者最成功的励志经典,出版后立即获得了广大读者的欢迎,成为西方世界最持久的人文畅销书。
无数读者通过阅读和实践书中介绍的各种方法,不仅走出困境,有的还成为世人仰慕的杰出人士。
只要不断研读《人性的弱点全集》,相信你也可以发掘自己的无穷潜力,创造辉煌的人生。</p>
<h2>《别逗了,费曼先生!》Surely You're Joking, Mr. Feynman!</h2>
<p><a href="http://www.amazon.cn/gp/product/B009QVEA8M/ref=as_li_ss_tl?ie=UTF8&camp=536&creative=3132&creativeASIN=B009QVEA8M&linkCode=as2&tag=favbook-23">《别逗了,费曼先生》</a>是一本很棒的读物:挥霍无忌、惊世骇俗,却仍然温馨,很有人情味儿。</p>
<p>R·P·费曼,他因盘子电动力学方面的研究荣获诺贝尔物理学奖。
除了作为一个物理学家外,费曼在不同时期还曾是故事大王、艺术家、鼓手和密码破泽专家。</p>
<blockquote><p>“费曼的一生,或可比作连锁反应。从一点儿临界质量的灰质开始,这个生命向四面八方炸开,产生出热和光。”
——《时代》</p>
<p>“费曼以其才华和怪癖,在他的同事们中间,成了一个传奇人物——您在阅读本书的时候,不从头笑到尾,是很难的。”
——《新闻周刊》</p>
<p>“眉飞色舞,肆意笑闹……费曼的语言,生动活泼,直率真朴一真正令人耳目一新。”
——《芝加哥太阳报》</p>
<p>“如果您以为物理学或物理学家中间没有什么乐子一那么来会会费曼吧——一个用一团原子变戏法的最令人捧腹的伙计。”
——《联合日报》</p>
<p>“科学家都是枯燥无味之人,这样一种老生常谈,一本书就能打破,这本书就是。”
——《底特律自由报》</p></blockquote>
<h2>《尽管去做》Getting Things Done</h2>
<p>如果你增加听说过一个词——GTD,没错,就是这本书 Getting Things Done,还有一种译法是<a href="http://www.amazon.cn/gp/product/B00368C0FG/ref=as_li_ss_tl?ie=UTF8&camp=536&creative=3132&creativeASIN=B00368C0FG&linkCode=as2&tag=favbook-23">《搞定1:无压工作的艺术》</a>也很信、达,至于雅嘛,呵呵。</p>
<p>在今天这个信息量和工作量倍增的世界,一些老的工作方法已经失去了效用。
每一个职场中人或多或少都有这样的体验:压力重重;太多事情都理不清头绪;似乎永远被各种任务和目标追赶着……</p>
<p>时间管理大师戴维•艾伦将指导你走出规划和执行工作中的泥沼,通向高效、轻松的彼岸。
要想让事情井井有条,关键便是——从容、放松。</p>
<h2>《别让我思考》Don't Make Me Think</h2>
<p>先推荐一篇知乎上的文章:<a href="http://www.zhihu.com/question/20564451">Chrome 浏览器的哪些设计符合「Don't make me think」原则?</a></p>
<p>在豆瓣和亚马逊搜索了很久中文版,居然没找到,不得已求助 Google,原来被翻译成了<a href="http://www.amazon.cn/gp/product/B0011BTJV8/ref=as_li_ss_tl?ie=UTF8&camp=536&creative=3132&creativeASIN=B0011BTJV8&linkCode=as2&tag=favbook-23">《点石成金:访客至上的网页设计秘笈》</a>。</p>
<p>如果你在进行网站设计,为网站编程,或者管理网站,那么一定要读一读此书。</p>
<ul>
<li>有些网站看起来很杂乱;</li>
<li>有些网站能让你轻松地找到资料;</li>
<li>有些网站让你犹如置身迷宫,</li>
</ul>
<p>为什么网站的可用性会有如此大的反差?用户在访问网站时有怎样的心理?
遵循什么样的原则来设计网站才能吸引访客?
这本全球 Web 设计人员的必读经典会给出答案。</p>
<h2>《禅与摩托车维修艺术》Zen and the Art of Motorcycle Maintenance</h2>
<p>这是什么书?</p>
<p><a href="http://www.amazon.cn/gp/product/B005O4PUFC/ref=as_li_ss_tl?ie=UTF8&camp=536&creative=3132&creativeASIN=B005O4PUFC&linkCode=as2&tag=favbook-23">《禅与摩托车维修艺术》</a>:累积销量超过一千万册,美国大学“禅与现代美国文学”课程的必读参考书。</p>
<p>70年代的梭罗——罗伯特•M. 波西格,《时代》周刊评选20世纪70年代十本最有影响力的书之一。</p>
<p>《禅与摩托车维修艺术》主要内容简介:在一个炎热的夏天,父子两人和约翰夫妇骑摩托车从明尼苏达到加州,跨越美国大陆,旅行的过程与一个青年斐德洛研修科学技术与西方经典,寻求自我的解脱,以及探寻生命的意义的过程相互穿插。</p>
<p>一路上父亲以一场哲学肖陶扩的形式,将见到的自然景色,野外露营的经历,夜晚旅店的谈话,机车修护技术等等日常生活与西方从苏格拉底以来的理性哲学的深入浅出的阐述与评论相结合,进行了对形而上学传统的主客体二元论的反思,以及对科学与艺术,知识与价值,古典主义与浪漫主义,精神与物质,机械论与神秘主义,西方与东方等西方二分法划分下的事物间的关系的思考。</p>
<p>并潜入自己的过去,探寻在现代文明下自己精神的分裂的起源,完成了一次自我心灵与人类文明 的探索。</p>
<h2>《编码宝典》(Cryptonomicon)</h2>
<p>貌似没有中国版。</p>
国外程序员在 stackoverflow 推荐的程序员必读书籍2015-09-26T00:00:00+00:00http://jiaxianhua.github.io/book/2015/09/26/free-programming-books-zh_cn-1<h2>原文</h2>
<hr />
<p><a href="https://github.com/justjavac/free-programming-books-zh_CN">https://github.com/justjavac/free-programming-books-zh_CN</a></p>
<h1>免费的编程中文书籍索引</h1>
<p>免费的编程中文书籍索引,欢迎投稿。</p>
<ul>
<li>国外程序员在 <a href="http://stackoverflow.com/a/1713/343194">stackoverflow</a> 推荐的程序员必读书籍,<a href="http://justjavac.com/other/2012/05/15/qualified-programmer-should-read-what-books.html" title="一个合格的程序员应该读过哪些书">中文版</a>。</li>
<li><a href="http://stackoverflow.com/q/38210/343194">stackoverflow</a> 上的程序员应该阅读的非编程类书籍有哪些? <a href="what-non-programming-books-should-programmers-read.md">中文版</a></li>
<li><a href="https://github.com/vhf/free-programming-books">github</a> 上的一个流行的编程书籍索引 <a href="https://github.com/vhf/free-programming-books/blob/master/free-programming-books-zh.md">中文版</a></li>
</ul>
<p>感谢 <a href="https://github.com/siberiawolf">@siberiawolf</a> 使用 Bootstrap 开发了网页版,地址:http://siberiawolf.com/free_programming/index.html</p>
<h2>参与交流</h2>
<p>欢迎大家将珍藏已久的经典免费书籍共享出来,您可以:</p>
<ul>
<li>使用 <a href="https://github.com/justjavac/free-programming-books-zh_CN/issues">Issues</a> 以及 Pull Request</li>
</ul>
<p>贡献者名单: https://github.com/justjavac/free-programming-books-zh_CN/graphs/contributors</p>
<h2>目录</h2>
<ul>
<li><a href="#%E8%AF%AD%E8%A8%80%E6%97%A0%E5%85%B3%E7%B1%BB">语言无关类</a></li>
<li><a href="#%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F">操作系统</a></li>
<li><a href="#%E6%99%BA%E8%83%BD%E7%B3%BB%E7%BB%9F">智能系统</a></li>
<li><a href="#%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86">编译原理</a></li>
<li><a href="#%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6">计算机图形学</a></li>
<li><a href="#web%E6%9C%8D%E5%8A%A1%E5%99%A8">WEB服务器</a></li>
<li><a href="#%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6">版本控制</a></li>
<li><a href="#%E7%BC%96%E8%BE%91%E5%99%A8">编辑器</a></li>
<li><a href="#nosql">NoSQL</a></li>
<li><a href="#postgresql">PostgreSQL</a></li>
<li><a href="#mysql">MySQL</a></li>
<li><a href="#%E7%AE%A1%E7%90%86%E5%92%8C%E7%9B%91%E6%8E%A7">管理和监控</a></li>
<li><a href="#%E9%A1%B9%E7%9B%AE%E7%9B%B8%E5%85%B3">项目相关</a></li>
<li><a href="#%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F">设计模式</a></li>
<li><a href="#web">Web</a></li>
<li><a href="#%E5%A4%A7%E6%95%B0%E6%8D%AE">大数据</a></li>
<li><a href="#%E7%BC%96%E7%A8%8B%E8%89%BA%E6%9C%AF">编程艺术</a></li>
<li><p><a href="#%E5%85%B6%E5%AE%83">其它</a></p></li>
<li><p><a href="#%E8%AF%AD%E8%A8%80%E7%9B%B8%E5%85%B3%E7%B1%BB">语言相关类</a></p></li>
<li><a href="#android">Android</a></li>
<li><a href="#awk">AWK</a></li>
<li><a href="#cc">C/C++</a></li>
<li><a href="#css">CSS/HTML</a></li>
<li><a href="#dart">Dart</a></li>
<li><a href="#erlang">Erlang</a></li>
<li><a href="#fortran">Fortran</a></li>
<li><a href="#go">Go</a></li>
<li><a href="#groovy">Groovy</a></li>
<li><a href="#haskell">Haskell</a></li>
<li><a href="#ios">iOS</a></li>
<li><a href="#java">Java</a></li>
<li><a href="#javascript">JavaScript</a></li>
<li><a href="#latex">LaTeX</a></li>
<li><a href="#lisp">LISP</a></li>
<li><a href="#lua">Lua</a></li>
<li><a href="#perl">Perl</a></li>
<li><a href="#php">PHP</a></li>
<li><a href="#prolog">Prolog</a></li>
<li><a href="#python">Python</a></li>
<li><a href="#r">R</a></li>
<li><a href="#ruby">Ruby</a></li>
<li><a href="#rust">Rust</a></li>
<li><a href="#scala">Scala</a></li>
<li><a href="#shell">Shell</a></li>
<li><p><a href="#swift">Swift</a></p></li>
<li><p><a href="#%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E5%8F%8A%E5%85%B6%E5%AE%83">读书笔记及其它</a></p></li>
<li><a href="#%E6%B5%8B%E8%AF%95%E7%9B%B8%E5%85%B3">测试相关</a></li>
</ul>
<h2>语言无关类</h2>
<h3>操作系统</h3>
<ul>
<li><a href="http://i.linuxtoy.org/docs/guide/index.html">开源世界旅行手册</a></li>
<li><a href="http://vbird.dic.ksu.edu.tw/">鸟哥的Linux私房菜</a></li>
<li><a href="http://sourceforge.net/apps/trac/elpi/wiki/ALP">Linux 系统高级编程</a></li>
<li><a href="http://billie66.github.io/TLCL/index.html">The Linux Command Line</a> (中英文版)</li>
<li><a href="http://oss.org.cn/kernel-book/ldd3/index.html">Linux 设备驱动</a> (第三版)</li>
<li><a href="http://www.kerneltravel.net/kernel-book/%E6%B7%B1%E5%85%A5%E5%88%86%E6%9E%90Linux%E5%86%85%E6%A0%B8%E6%BA%90%E7%A0%81.html">深入分析Linux内核源码</a></li>
<li><a href="http://cb.vu/unixtoolbox_zh_CN.xhtml">UNIX TOOLBOX</a></li>
<li><a href="https://github.com/widuu/chinese_docker">Docker中文指南</a></li>
<li><a href="https://github.com/yeasy/docker_practice">Docker —— 从入门到实践</a></li>
<li><a href="http://yuedu.baidu.com/ebook/d817967416fc700abb68fca1">Docker入门实战</a></li>
<li><a href="http://freeradius.akagi201.org">FreeRADIUS新手入门</a></li>
<li><a href="http://aaaaaashu.gitbooks.io/mac-dev-setup/content/">Mac 开发配置手册</a></li>
<li><a href="https://www.freebsd.org/doc/zh_CN/books/handbook/index.html">FreeBSD 使用手册</a></li>
<li><a href="http://billie66.github.io/TLCL/book/">Linux 命令行(中文版)</a></li>
<li><a href="http://works.jinbuguo.com/lfs/lfs62/index.html">Linux 构建指南</a></li>
<li><a href="https://github.com/me115/linuxtools_rst">Linux工具快速教程</a></li>
<li><a href="http://tinylab.gitbooks.io/linux-doc">Linux Documentation (中文版)</a></li>
<li><a href="http://tinylab.gitbooks.io/elinux">嵌入式 Linux 知识库 (eLinux.org 中文版)</a></li>
<li><a href="https://github.com/tobegit3hub/understand_linux_process">理解Linux进程</a></li>
<li><a href="https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md">命令行的艺术</a></li>
</ul>
<h4>智能系统</h4>
<ul>
<li><a href="https://github.com/gmszone/designiot">一步步搭建物联网系统</a></li>
</ul>
<h3>编译原理</h3>
<ul>
<li><a href="https://github.com/DeathKing/Learning-SICP">《计算机程序的结构和解释》公开课 翻译项目</a></li>
</ul>
<h3>计算机图形学</h3>
<ul>
<li><a href="https://github.com/zilongshanren/opengl-tutorials">OpenGL 教程</a></li>
</ul>
<h3>WEB服务器</h3>
<ul>
<li><a href="http://tengine.taobao.org/book/index.html">Nginx开发从入门到精通</a> (淘宝团队出品)</li>
<li><a href="http://www.ttlsa.com/nginx/nginx-stu-pdf/">Nginx教程从入门到精通</a>(PDF版本,运维生存时间出品)</li>
<li><a href="http://works.jinbuguo.com/apache/menu22/index.html">Apache 中文手册</a></li>
</ul>
<h3>版本控制</h3>
<ul>
<li><a href="http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000">Git教程</a> (本文由 <a href="http://weibo.com/liaoxuefeng">@廖雪峰</a> 创作,如果觉得本教程对您有帮助,可以去 <a href="https://itunes.apple.com/cn/app/git-jiao-cheng/id876420437">iTunes</a> 购买)</li>
<li><a href="http://rogerdudler.github.io/git-guide/index.zh.html">git - 简易指南</a></li>
<li><a href="http://backlogtool.com/git-guide/cn/">猴子都能懂的GIT入门</a></li>
<li><a href="http://gitref.justjavac.com">Git 参考手册</a></li>
<li><a href="http://git-scm.com/book/zh">Pro Git</a></li>
<li><a href="https://www.gitbook.com/book/0532/progit/details">Pro Git 中文版</a> (整理在gitbook上)</li>
<li><a href="http://www-cs-students.stanford.edu/~blynn/gitmagic/intl/zh_cn/">Git Magic</a></li>
<li><a href="http://www.worldhello.net/gotgithub/index.html">GotGitHub</a></li>
<li><a href="http://gitbook.liuhui998.com/index.html">Git Community Book 中文版</a></li>
<li><a href="http://mercurial.selenic.com/wiki/ChineseTutorial">Mercurial 使用教程</a></li>
<li><a href="http://bucunzai.net/hginit/">HgInit (中文版)</a></li>
<li><a href="http://igit.linuxtoy.org/">沉浸式学 Git</a></li>
<li><a href="https://github.com/flyhigher139/Git-Cheat-Sheet">Git-Cheat-Sheet</a> (感谢 @flyhigher139 翻译了中文版)</li>
<li><a href="http://snowdream86.gitbooks.io/github-cheat-sheet/content/zh/index.html">GitHub秘籍</a></li>
<li><a href="https://github.com/waylau/github-help">Github帮助文档</a></li>
<li><a href="http://danielkummer.github.io/git-flow-cheatsheet/index.zh_CN.html">git-flow 备忘清单</a></li>
<li><a href="http://svnbook.red-bean.com/nightly/zh/index.html">svn 手册</a></li>
</ul>
<h3>编辑器</h3>
<ul>
<li><a href="http://exvim.github.io/docs-zh/intro/">exvim--vim 改良成IDE项目</a></li>
<li><a href="http://learnvimscriptthehardway.onefloweroneworld.com/">笨方法学Vimscript 中译本</a></li>
<li><a href="https://github.com/vimcn/vimcdoc">Vim中文文档</a></li>
<li><a href="https://github.com/yangyangwithgnu/use_vim_as_ide">所需即所获:像 IDE 一样使用 vim</a></li>
<li><a href="https://github.com/aqua7regia/tmux-Productive-Mouse-Free-Development_zh">tmux:高效的全键盘开发工具</a></li>
</ul>
<h3>NoSQL</h3>
<ul>
<li><a href="http://www.yankay.com/wp-content/NoSql_Database_Note.html">NoSQL数据库笔谈</a> (<a href="http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/NoSQL%E6%95%B0%E6%8D%AE%E5%BA%93%E7%AC%94%E8%B0%88v2.pdf">PDF</a>)</li>
<li><a href="http://redisbook.com/">Redis 设计与实现</a></li>
<li><a href="http://www.redisdoc.com/">Redis 命令参考</a></li>
<li><a href="https://github.com/huangz1990/redis-3.0-annotated">带有详细注释的 Redis 3.0 代码</a></li>
<li><a href="https://github.com/huangz1990/annotated_redis_source">带有详细注释的 Redis 2.6 代码</a></li>
<li><a href="https://github.com/justinyhuang/the-little-mongodb-book-cn/blob/master/mongodb.md">The Little MongoDB Book</a></li>
<li><a href="https://github.com/JasonLai256/the-little-redis-book/blob/master/cn/redis.md">The Little Redis Book</a></li>
<li><a href="http://docs.neo4j.org.cn/">Neo4j 简体中文手册 v1.8</a></li>
<li><a href="http://neo4j.tw/">Neo4j .rb 中文資源</a></li>
<li><a href="http://disquebook.com">Disque 使用教程</a></li>
</ul>
<h3>PostgreSQL</h3>
<ul>
<li><a href="http://works.jinbuguo.com/postgresql/menu823/index.html">PostgreSQL 8.2.3 中文文档</a></li>
<li><a href="http://www.postgres.cn/docs/9.3/index.html">PostgreSQL 9.3.1 中文文档</a></li>
</ul>
<h3>MySQL</h3>
<ul>
<li><a href="http://blog.codinglabs.org/articles/theory-of-mysql-index.html">MySQL索引背后的数据结构及算法原理</a></li>
<li><a href="http://www.cnblogs.com/mr-wid/archive/2013/05/09/3068229.html">21分钟MySQL入门教程</a></li>
</ul>
<h3>管理和监控</h3>
<ul>
<li><a href="http://kibana.logstash.es">ELKstack 中文指南</a></li>
<li><a href="http://udn.yyuap.com/doc/mastering-elasticsearch/">Mastering Elasticsearch(中文版)</a></li>
<li><a href="https://www.gitbook.com/book/fuxiaopang/learnelasticsearch/details">ElasticSearch 权威指南</a></li>
<li><a href="http://es.xiaoleilu.com">Elasticsearch 权威指南(中文版)</a></li>
<li><a href="http://udn.yyuap.com/doc/mastering-elasticsearch/">Logstash 最佳实践</a></li>
<li><a href="http://bbs.konotes.org/workdoc/puppet-27/">Puppet 2.7 Cookbook 中文版</a></li>
</ul>
<h3>项目相关</h3>
<ul>
<li><a href="http://article.yeeyan.org/view/2251/94882">持续集成(第二版)</a> (译言网)</li>
<li><a href="http://www.ibm.com/developerworks/cn/java/j-ap/">让开发自动化系列专栏</a></li>
<li><a href="http://www.ibm.com/developerworks/cn/java/j-cq/">追求代码质量</a></li>
<li><a href="https://github.com/fool2fish/selenium-doc">selenium 中文文档</a></li>
<li><a href="http://local.joelonsoftware.com/wiki/Chinese_(Simplified)">Joel谈软件</a></li>
<li><a href="http://local.joelonsoftware.com/wiki/%E9%A6%96%E9%A0%81">約耳談軟體(Joel on Software)</a></li>
<li><a href="https://github.com/waylau/Gradle-2-User-Guide">Gradle 2 用户指南</a></li>
<li><a href="http://yuedu.baidu.com/ebook/f23af265998fcc22bcd10da2">Gradle 中文使用文档</a></li>
<li><a href="https://github.com/ecomfe/spec">编码规范</a></li>
<li><a href="http://www.ituring.com.cn/book/1143">开源软件架构</a></li>
<li><a href="http://docs.huihoo.com/gnu/linux/gmake.html">GNU make 指南</a></li>
<li><a href="http://www.yayu.org/book/gnu_make/">GNU make 中文手册</a></li>
</ul>
<h3>设计模式</h3>
<ul>
<li><a href="https://github.com/me115/design_patterns">图说设计模式</a></li>
<li><a href="http://blog.csdn.net/lovelion/article/details/17517213">史上最全设计模式导学目录</a></li>
</ul>
<h3>Web</h3>
<ul>
<li><a href="http://www.20thingsilearned.com/zh-CN/home">关于浏览器和网络的 20 项须知</a></li>
<li><a href="http://knowledge.ecomfe.com/">前端知识体系</a></li>
<li><a href="http://jinlong.github.io/2013/08/29/devtoolsecrets/">浏览器开发工具的秘密</a></li>
<li><a href="https://github.com/CN-Chrome-DevTools/CN-Chrome-DevTools">Chrome 开发者工具中文手册</a></li>
<li><a href="http://open.chrome.360.cn/extension_dev/overview.html">Chrome扩展开发文档</a></li>
<li><a href="http://www.gruntjs.net/">Grunt中文文档</a></li>
<li><a href="http://www.gulpjs.com.cn/docs/">gulp中文文档</a></li>
<li><a href="https://github.com/nimojs/gulp-book">Gulp 入门指南</a></li>
<li><a href="http://yeomanjs.org/">Yeoman中文文档</a></li>
<li><a href="https://github.com/AlloyTeam/Mars">移动Web前端知识库</a></li>
<li><a href="http://deerchao.net/tutorials/regex/regex.htm">正则表达式30分钟入门教程</a></li>
<li><a href="https://github.com/fouber/blog/issues/2">前端开发体系建设日记</a></li>
<li><a href="https://github.com/hoosin/mobile-web-favorites">移动前端开发收藏夹</a></li>
<li><a href="https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md">JSON风格指南</a></li>
<li><a href="https://github.com/bolasblack/http-api-guide">HTTP 接口设计指北</a></li>
<li><a href="https://github.com/hacke2/hacke2.github.io/issues/1">前端资源分享(一)</a></li>
<li><a href="https://github.com/hacke2/hacke2.github.io/issues/3">前端资源分享(二)</a></li>
<li><a href="http://coderlmn.github.io/code-standards/">前端代码规范 及 最佳实践</a></li>
<li><a href="http://www.flygon.net/archives/427">w3school教程整理</a></li>
<li><a href="http://man.lupaworld.com/content/network/wireshark/index.html">Wireshark用户手册</a></li>
<li><a href="https://community.emc.com/thread/194901">一站式学习Wireshark</a></li>
<li><a href="http://happypeter.github.io/tealeaf-http/">HTTP 下午茶</a></li>
<li><a href="http://yuedu.baidu.com/ebook/478d1a62376baf1ffc4fad99?pn=1">HTTP/2.0 中文翻译</a></li>
<li><a href="https://www.gitbook.com/book/ye11ow/http2-explained/details">http2讲解</a></li>
<li><a href="https://www.gitbook.com/book/juntao/3-web-designs-in-3-weeks/details">3 Web Designs in 3 Weeks</a></li>
</ul>
<h3>大数据</h3>
<ul>
<li><a href="https://github.com/Flowerowl/Big-Data-Resources">大数据/数据挖掘/推荐系统/机器学习相关资源</a></li>
<li><a href="https://github.com/jizhang/guidetodatamining">面向程序员的数据挖掘指南</a></li>
<li><a href="https://code.csdn.net/CODE_Translation/spark_matei_phd">大型集群上的快速和通用数据处理架构</a></li>
<li><a href="https://github.com/linyiqun/DataMiningAlgorithm">数据挖掘中经典的算法实现和详细的注释</a></li>
</ul>
<h2>编程艺术</h2>
<ul>
<li><a href="http://huyuefeng.me/intro-to-prog/">编程入门指南</a></li>
<li><a href="https://github.com/julycoding/The-Art-Of-Programming-by-July">程序员编程艺术</a></li>
<li><a href="http://www.oschina.net/translate/what-every-programmer-should-know-about-memory-part1?print">每个程序员都应该了解的内存知识(译)</a>【第一部分】</li>
<li><a href="http://read.douban.com/ebook/4972883/">取悦的工序:如何理解游戏</a> (豆瓣阅读,免费书籍)</li>
<li><a href="http://xiaobeicn.gitbooks.io/programming-skills-summary/">编程技巧总汇</a></li>
</ul>
<h2>其它</h2>
<ul>
<li><a href="http://softwaredownload.gitbooks.io/openwrt-fanqiang/">OpenWrt智能、自动、透明翻墙路由器教程</a></li>
<li><a href="https://community.emc.com/docs/DOC-16067">SAN 管理入门系列</a></li>
<li><a href="http://sketchcn.com/sketch-chinese-user-manual.html#introduce">Sketch 中文手册</a></li>
</ul>
<h2>语言相关类</h2>
<h3>Android</h3>
<ul>
<li><a href="http://www.apkbus.com/design/index.html">Android Design(中文版)</a></li>
<li>Google Material Design 正體中文版 (<a href="http://wcc723.gitbooks.io/google_design_translate/content/style-icons.html">译本一</a> <a href="https://github.com/1sters/material_design_zh">译本二</a>)</li>
<li><a href="http://hukai.me/android-training-course-in-chinese/index.html">Google Android官方培训课程中文版</a></li>
<li><a href="http://stormzhang.github.io/android/2014/07/07/learn-android-from-rookie/">Android学习之路</a></li>
<li><a href="https://github.com/bboyfeiyu/android-tech-frontier">Android开发技术前线(android-tech-frontier)</a></li>
<li><a href="https://github.com/FX-Max/Point-of-Android">Point-of-Android</a> Android 一些重要知识点解析整理</li>
</ul>
<h3>AWK</h3>
<ul>
<li><a href="https://github.com/wuzhouhui/awk">awk程序设计语言</a></li>
</ul>
<h3>C/C++</h3>
<ul>
<li><a href="http://tinylab.gitbooks.io/cbook">C 语言编程透视</a></li>
<li><a href="https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial">C++ 并发编程指南</a> (<a href="http://weibo.com/1702076100">@傅海平ICT</a>)</li>
<li><a href="http://akaedu.github.io/book/">Linux C编程一站式学习</a> (宋劲杉, 北京亚嵌教育研究中心)</li>
<li><a href="https://github.com/leeyiw/cgdb-manual-in-chinese">CGDB中文手册</a></li>
<li><a href="https://github.com/hellogcc/100-gdb-tips/blob/master/src/index.md">100个gdb小技巧</a></li>
<li><a href="https://github.com/hellogcc/100-gcc-tips/blob/master/src/index.md">100个gcc小技巧</a></li>
<li><a href="https://github.com/anjuke/zguide-cn">ZMQ 指南</a></li>
<li><a href="http://www.ituring.com.cn/book/1203">How to Think Like a Computer Scientist</a> (中英文版)</li>
<li><a href="http://scc.qibebt.cas.cn/docs/linux/base/%B8%FA%CE%D2%D2%BB%C6%F0%D0%B4Makefile-%B3%C2%F0%A9.pdf">跟我一起写Makefile(PDF)</a></li>
<li><a href="http://www.yayu.org/book/gnu_make/">GNU make中文手册</a></li>
<li><a href="http://docs.huihoo.com/gnu/linux/gmake.html">GNU make 指南</a></li>
<li><a href="http://zh-google-styleguide.readthedocs.org/en/latest/google-cpp-styleguide/contents/">Google C++ 风格指南</a></li>
<li><a href="https://github.com/andycai/cprimer">C/C++ Primer</a> (by @andycai)</li>
<li><a href="http://www.nowamagic.net/librarys/books/contents/c">简单易懂的C魔法</a></li>
<li><a href="http://sewm.pku.edu.cn/src/paradise/reference/CMake%20Practice.pdf">Cmake 实践</a> (PDF版)</li>
<li><a href="http://www.sunistudio.com/cppfaq/">C++ FAQ LITE(中文版)</a></li>
<li><a href="https://github.com/Mooophy/Cpp-Primer">C++ Primer 5th Answers</a></li>
<li><a href="http://chenxiaowei.gitbooks.io/cpp_concurrency_in_action/">C++ 并发编程(基于C++11)</a></li>
<li><a href="http://www.kuqin.com/qtdocument/tutorial.html">QT 教程</a></li>
<li><a href="https://github.com/wuye9036/CppTemplateTutorial">C++ Template 进阶指南</a></li>
</ul>
<h3>CSS</h3>
<ul>
<li><a href="http://zh.learnlayout.com/">学习CSS布局</a></li>
<li><a href="https://github.com/chadluo/CSS-Guidelines/blob/master/README.md">通用 CSS 笔记、建议与指导</a></li>
<li><a href="http://css.doyoe.com/">CSS参考手册</a></li>
<li><a href="http://yanxyz.github.io/emmet-docs/">Emmet 文档</a></li>
<li><a href="http://alloyteam.github.io/CodeGuide/">前端代码规范</a> (腾讯 AlloyTeam 团队)</li>
<li><a href="http://codeguide.bootcss.com/">HTML和CSS编码规范</a></li>
<li><a href="http://sass-guidelin.es/zh/">Sass Guidelines 中文</a></li>
<li><a href="https://github.com/waylau/css3-tutorial">CSS3 Tutorial 《CSS3 教程》</a></li>
</ul>
<h3>Dart</h3>
<ul>
<li><a href="http://dart.lidian.info/wiki/Language_Tour">Dart 语言导览</a></li>
</ul>
<h3>Erlang</h3>
<ul>
<li><a href="http://xn--21erlang-p00o82pmp3o.github.io/">21天学通Erlang</a></li>
</ul>
<h3>Fortran</h3>
<ul>
<li><a href="http://micro.ustc.edu.cn/Fortran/ZJDing/">Fortran77和90/95编程入门</a></li>
</ul>
<h3>Go</h3>
<ul>
<li><a href="https://github.com/Unknwon/go-fundamental-programming">Go编程基础</a></li>
<li><a href="https://github.com/Unknwon/the-way-to-go_ZH_CN">Go入门指南</a></li>
<li><a href="http://mikespook.com/learning-go/">学习Go语言</a> (<a href="http://xxiyy.qiniudn.com/%E5%AD%A6%E4%B9%A0%20Go%20%E8%AF%AD%E8%A8%80(Golang).pdf?download">PDF</a>)</li>
<li><a href="https://github.com/astaxie/build-web-application-with-golang">Go Web 编程</a> (此书已经出版,希望开发者们去购买,支持作者的创作)</li>
<li><a href="https://github.com/astaxie/Go-in-Action">Go实战开发</a> (当我收录此项目时,作者已经写完第三章,如果读完前面章节觉得有帮助,可以给作者<a href="https://me.alipay.com/astaxie">捐赠</a>,以鼓励作者的继续创作)</li>
<li><a href="https://github.com/astaxie/NPWG_zh">Network programming with Go 中文翻译版本</a></li>
<li><a href="http://www.hellogcc.org/effective_go.html">Effective Go</a></li>
<li><a href="https://github.com/polaris1119/The-Golang-Standard-Library-by-Example">Go 语言标准库</a></li>
<li><a href="http://gorevel.cn/docs/manual/index.html">Revel 框架手册</a></li>
<li><a href="http://blog.csdn.net/dc_726/article/details/46565241">Java程序员的Golang入门指南</a></li>
<li><a href="https://github.com/hyper-carrot/go_command_tutorial">Go命令教程</a></li>
<li><a href="https://github.com/achun/Go-Blog-In-Action">Go语言博客实践</a></li>
<li><a href="https://github.com/golang-china/golangdoc.translations">Go 官方文档翻译</a></li>
</ul>
<h3>Groovy</h3>
<ul>
<li><a href="http://www.ibm.com/developerworks/cn/java/j-pg/">实战 Groovy 系列</a></li>
</ul>
<h3>Haskell</h3>
<ul>
<li><a href="http://rwh.readthedocs.org/en/latest/">Real World Haskell 中文版</a></li>
<li><a href="http://fleurer-lee.com/lyah/">Haskell趣学指南</a></li>
</ul>
<h3>iOS</h3>
<ul>
<li><a href="https://github.com/qinjx/30min_guides/blob/master/ios.md">iOS开发60分钟入门</a></li>
<li><a href="http://isux.tencent.com/ios-human-interface-guidelines-ui-design-basics-ios7.html">iOS7人机界面指南</a></li>
<li><a href="http://zh-google-styleguide.readthedocs.org/en/latest/google-objc-styleguide/">Google Objective-C Style Guide 中文版</a></li>
<li><a href="http://wileam.com/iphone-6-screen-cn/">iPhone 6 屏幕揭秘</a></li>
<li><a href="http://nilsun.github.io/apple-watch/">Apple Watch开发初探</a></li>
<li><a href="https://developer.apple.com/library/ios/referencelibrary/GettingStarted/RoadMapiOSCh/index.html">马上着手开发 iOS 应用程序</a></li>
<li><a href="https://github.com/jkyin/Subtitle">网易斯坦福大学公开课:iOS 7应用开发字幕文件</a></li>
</ul>
<h3>Java</h3>
<ul>
<li><a href="https://github.com/waylau/apache-shiro-1.2.x-reference">Apache Shiro 用户指南</a></li>
<li><a href="https://github.com/waylau/Jersey-2.x-User-Guide">Jersey 2.x 用户指南</a></li>
<li><a href="https://github.com/waylau/spring-framework-4-reference">Spring Framework 4.x参考文档</a></li>
<li><a href="https://github.com/qibaoguang/Spring-Boot-Reference-Guide">Spring Boot参考指南</a> (翻译中)</li>
<li><a href="http://mybatis.github.io/mybatis-3/zh/index.html">MyBatis中文文档</a></li>
<li><a href="https://github.com/waylau/RestDemo">用jersey构建REST服务</a></li>
<li><a href="https://github.com/waylau/activiti-5.x-user-guide">Activiti 5.x 用户指南</a></li>
<li><a href="http://www.hawstein.com/posts/google-java-style.html">Google Java编程风格指南</a></li>
<li><a href="https://github.com/waylau/netty-4-user-guide">Netty 4.x 用户指南</a></li>
<li><a href="https://github.com/waylau/essential-netty-in-action">Netty 实战(精髓)</a></li>
<li><a href="https://github.com/waylau/rest-in-action">REST 实战</a></li>
<li><a href="https://github.com/waylau/java-code-conventions">Java 编码规范</a></li>
<li><a href="https://github.com/waylau/apache-mina-2.x-user-guide">Apache MINA 2 用户指南</a></li>
<li><a href="https://github.com/waylau/h2-database-doc">H2 Database 教程</a></li>
</ul>
<h3>JavaScript</h3>
<ul>
<li><a href="http://bq69.com/blog/articles/script/868/google-javascript-style-guide.html">Google JavaScript 代码风格指南</a></li>
<li><a href="https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md">Google JSON 风格指南</a></li>
<li><a href="https://github.com/adamlu/javascript-style-guide">Airbnb JavaScript 规范</a></li>
<li><a href="http://javascript.ruanyifeng.com/">JavaScript 标准参考教程(alpha)</a></li>
<li><a href="http://pij.robinqu.me/">Javascript编程指南</a> (<a href="https://github.com/RobinQu/Programing-In-Javascript">源码</a>)</li>
<li><a href="https://github.com/justjavac/12-javascript-quirks">javascript 的 12 个怪癖</a></li>
<li><a href="http://bonsaiden.github.io/JavaScript-Garden/zh/">JavaScript 秘密花园</a></li>
<li><a href="http://icodeit.org/jsccp/">JavaScript核心概念及实践</a> (PDF) (此书已由人民邮电出版社出版发行,但作者依然免费提供PDF版本,希望开发者们去购买,支持作者)</li>
<li><a href="https://github.com/jayli/javascript-patterns">《JavaScript 模式》</a> “JavaScript patterns”中译本</li>
<li><a href="http://justjavac.com/named-function-expressions-demystified.html">命名函数表达式探秘</a> (注:原文由<a href="http://www.cn-cuckoo.com">为之漫笔</a>翻译,原始地址无法打开,所以此处地址为我博客上的备份)</li>
<li><a href="http://www.oschina.net/translate/learning-javascript-design-patterns">学用 JavaScript 设计模式</a> (开源中国)</li>
<li><a href="http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html">深入理解JavaScript系列</a></li>
<li><a href="http://es6.ruanyifeng.com/">ECMAScript 6 入门</a> (作者:阮一峰)</li>
<li><a href="http://liubin.github.io/promises-book/">JavaScript Promise迷你书</a></li>
<li><a href="https://github.com/getify/You-Dont-Know-JS">You-Dont-Know-JS</a> (深入JavaScript语言核心机制的系列图书)</li>
<li>jQuery</li>
<li><a href="http://www.cn-cuckoo.com/deconstructed/jquery.html">jQuery 解构</a></li>
<li><a href="http://www.nowamagic.net/librarys/books/contents/jquery">简单易懂的JQuery魔法</a></li>
<li><a href="http://i5ting.github.io/How-to-write-jQuery-plugin/build/jquery.plugin.html">How to write jQuery plugin</a></li>
<li>Node.js</li>
<li><a href="http://www.nodebeginner.org/index-zh-cn.html">Node入门</a></li>
<li><a href="http://nqdeng.github.io/7-days-nodejs/">七天学会NodeJS</a></li>
<li><a href="https://github.com/nodejs-tw/nodejs-wiki-book">Nodejs Wiki Book</a> (繁体中文)</li>
<li><a href="http://expressjs.jser.us/">express.js 中文文档</a></li>
<li><a href="https://github.com/turingou/koa-guide">koa 中文文档</a></li>
<li><a href="https://github.com/nswbmw/N-blog">使用 Express + MongoDB 搭建多人博客</a></li>
<li><a href="http://javascript.ruanyifeng.com/nodejs/express.html">Express框架</a></li>
<li><a href="https://github.com/alsotang/node-lessons">Node.js 包教不包会</a></li>
<li><a href="https://www.npmjs.org/package/learnyounode-zh-cn">Learn You The Node.js For Much Win! (中文版)</a></li>
<li><a href="http://i5ting.github.io/node-debug-tutorial/">Node debug 三法三例</a></li>
<li><a href="https://www.gitbook.com/book/0532/nodejs/details">nodejs中文文档</a></li>
<li>underscore.js</li>
<li><a href="http://learningcn.com/underscore/">Underscore.js中文文档</a></li>
<li>backbone.js</li>
<li><a href="http://www.the5fire.com/backbone-js-tutorials-pdf-download.html">backbone.js入门教程</a> (PDF)</li>
<li><a href="https://github.com/the5fire/backbonejs-learning-note">Backbone.js入门教程第二版</a></li>
<li><a href="http://feliving.github.io/developing-backbone-applications">Developing Backbone.js Applications(中文版)</a></li>
<li>AngularJS</li>
<li><a href="https://github.com/mgechev/angularjs-style-guide/blob/master/README-zh-cn.md">AngularJS最佳实践和风格指南</a></li>
<li><a href="https://github.com/peiransun/angularjs-cn">AngularJS中译本</a></li>
<li><a href="https://github.com/zensh/AngularjsTutorial_cn">AngularJS入门教程</a></li>
<li><a href="https://github.com/xufei/Make-Your-Own-AngularJS/blob/master/01.md">构建自己的AngularJS</a></li>
<li><a href="http://www.waylau.com/build-angularjs-app-with-yeoman-in-windows/">在Windows环境下用Yeoman构建AngularJS项目</a></li>
<li>Zepto.js</li>
<li><a href="http://mweb.baidu.com/zeptoapi/">Zepto.js 中文文档</a></li>
<li>Sea.js</li>
<li><a href="http://island205.github.io/HelloSea.js/">Hello Sea.js</a></li>
<li>React.js</li>
<li><a href="http://reactjs.cn/">React.js 中文文档</a></li>
<li><a href="https://github.com/fakefish/react-webpack-cookbook">React webpack-cookbook</a></li>
<li><a href="http://fraserxu.me/intro-to-react/">React 入门教程</a></li>
<li>impress.js</li>
<li><a href="https://github.com/kokdemo/impress.js-tutorial-in-Chinese">impress.js的中文教程</a></li>
<li>CoffeeScript</li>
<li><a href="http://island205.github.io/coffeescript-cookbook.github.com/">CoffeeScript Cookbook</a></li>
<li><a href="http://island205.github.io/tlboc/">The Little Book on CoffeeScript中文版</a></li>
<li><a href="https://github.com/geekplux/coffeescript-style-guide">CoffeeScript 编码风格指南</a></li>
<li>ExtJS</li>
<li><a href="http://extjs-doc-cn.github.io/ext4api/">Ext4.1.0 中文文档</a></li>
<li>Meteor</li>
<li><a href="http://zh.discovermeteor.com/">Discover Meteor</a></li>
<li><a href="http://www.ituring.com.cn/minibook/950">Chrome扩展及应用开发</a></li>
</ul>
<h3>LaTeX</h3>
<ul>
<li><a href="http://liam0205.me/2014/09/08/latex-introduction/">一份其实很短的 LaTeX 入门文档</a></li>
<li><a href="http://www.mohu.org/info/lshort-cn.pdf">一份不太简短的 LATEX 2ε 介绍</a> (PDF版)</li>
</ul>
<h3>LISP</h3>
<ul>
<li>Common Lisp</li>
<li><a href="http://acl.readthedocs.org/en/latest/">ANSI Common Lisp 中文翻譯版</a></li>
<li><a href="http://www.ituring.com.cn/minibook/862">On Lisp 中文翻译版本</a></li>
<li>Scheme</li>
<li><a href="http://deathking.github.io/yast-cn/">Yet Another Scheme Tutorial Scheme入门教程</a></li>
<li><a href="http://songjinghe.github.io/TYS-zh-translation/">Scheme语言简明教程</a></li>
<li>Racket</li>
<li><a href="https://github.com/tyrchen/racket-book">Racket book</a></li>
</ul>
<h3>Lua</h3>
<ul>
<li><a href="https://github.com/andycai/luaprimer">Lua编程入门</a></li>
<li><a href="http://www.codingnow.com/2000/download/lua_manual.html">Lua 5.1 参考手册 中文翻译</a></li>
<li><a href="http://cloudwu.github.io/lua53doc/">Lua 5.3 参考手册 中文翻译</a></li>
<li><a href="http://www.codingnow.com/temp/readinglua.pdf">Lua源码欣赏</a></li>
<li><a href="http://book.luaer.cn/">lua程序设计</a></li>
</ul>
<h3>Perl</h3>
<ul>
<li><a href="https://github.com/horus/modern_perl_book">Modern Perl 中文版</a></li>
<li><a href="http://perl.linuxtoy.org/">Perl 程序员应该知道的事</a></li>
</ul>
<h3>PHP</h3>
<ul>
<li><a href="http://www.laruence.com/2010/06/21/1608.html">PHP调试技术手册</a>(PDF)</li>
<li><a href="http://www.blogkun.com/project.html">XDebug 2中文手册(译)</a> (CHM)</li>
<li>PHP之道:php-the-right-way (<a href="http://wulijun.github.io/php-the-right-way/">@wulijun版</a> <a href="http://laravel-china.github.io/php-the-right-way/">PHPHub版</a>)</li>
<li><a href="https://github.com/justjavac/PHP-Best-Practices-zh_CN">PHP 最佳实践</a></li>
<li><a href="http://ryancao.gitbooks.io/php-developer-prepares/content/">PHP 开发者实践</a></li>
<li><a href="https://github.com/reeze/tipi">深入理解PHP内核</a></li>
<li><a href="http://www.walu.cc/phpbook/">PHP扩展开发及内核应用</a></li>
<li><a href="http://codeigniter.org.cn/user_guide/index.html">CodeIgniter 用户指南</a></li>
<li><a href="http://www.golaravel.com/docs/">Laravel4 中文文档</a></li>
<li><a href="https://github.com/huanghua581/laravel-getting-started">Laravel 入门</a></li>
<li><a href="http://symfony-docs-chs.readthedocs.org/en/latest/">Symfony2中文文档</a> (未译完)</li>
<li><a href="http://phalcon.5iunix.net/">Phalcon中文文档</a>(翻译进行中)</li>
<li><a href="http://yiibook.com//doc">YiiBook几本Yii框架的在线教程</a></li>
<li><a href="http://www.digpage.com/">深入理解 Yii 2.0</a></li>
<li><a href="http://www.yiichina.com/">Yii 框架中文文檔</a></li>
<li><a href="http://www.nowamagic.net/librarys/books/contents/php">简单易懂的PHP魔法</a></li>
<li><a href="https://github.com/LinkedDestiny/swoole-doc">swoole文档及入门教程</a></li>
<li><a href="http://www.phpcomposer.com">Composer 中文网</a></li>
<li><a href="http://minimee.org/php/slim">Slim 中文文档</a></li>
<li><a href="http://lumen.laravel-china.org/">Lumen 中文文档</a></li>
</ul>
<h3>Prolog</h3>
<ul>
<li><a href="http://fengdidi.github.io/blog/2011/11/15/qian-yan/">笨办法学Prolog</a></li>
</ul>
<h3>Python</h3>
<ul>
<li><a href="http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000">小白的Python教程</a></li>
<li><a href="http://woodpecker.org.cn/abyteofpython_cn/chinese/">简明Python教程</a></li>
<li><a href="http://looly.gitbooks.io/python-basic">零基础学Python</a></li>
<li><a href="http://lovelypython.readthedocs.org/en/latest/">可爱的 Python </a></li>
<li><a href="http://www.pythondoc.com/pythontutorial27/index.html">Python 2.7 官方教程中文版</a></li>
<li><a href="http://www.pythondoc.com/pythontutorial3/index.html">Python 3.3 官方教程中文版</a></li>
<li><a href="https://github.com/yidao620c/python3-cookbook">《Python Cookbook》 3rd Edition 翻译</a></li>
<li><a href="http://sebug.net/paper/books/dive-into-python3/">深入 Python 3</a></li>
<li><a href="https://code.google.com/p/zhong-wiki/wiki/PEP8">PEP8 Python代码风格规范</a></li>
<li><a href="http://zh-google-styleguide.readthedocs.org/en/latest/google-python-styleguide/">Google Python 风格指南 中文版</a></li>
<li><a href="http://liam0205.me/2013/11/02/Python-tutorial-zh_cn/">Python入门教程</a> (<a href="http://liam0205.me/attachment/Python/The_Python_Tutorial_zh-cn.pdf">PDF</a>)</li>
<li><a href="http://article.yeeyan.org/view/311527/287706">Python的神奇方法指南</a></li>
<li><a href="http://sebug.net/paper/books/LearnPythonTheHardWay/">笨办法学 Python</a> (<a href="http://liam0205.me/attachment/Python/PyHardWay/Learn_Python_The_Hard_Way_zh-cn.pdf">PDF</a>版下载)</li>
<li><a href="http://django-chinese-docs.readthedocs.org/en/latest/">Django 1.5 文档中文版</a> 正在翻译中</li>
<li><a href="http://django-1-7-doc.coding.io/">Diango 1.7 文档中文版</a> 正在翻译中,目前只翻译了目录</li>
<li><a href="https://github.com/brantyoung/zh-django-best-practices">Django 最佳实践</a></li>
<li><a href="http://andrew-liu.gitbooks.io/django-blog/">Django搭建简易博客教程</a></li>
<li><a href="http://djangobook.py3k.cn/2.0/">The Django Book 中文版</a></li>
<li><a href="http://webpy.org/tutorial3.zh-cn">web.py 0.3 新手指南</a></li>
<li><a href="http://webpy.org/cookbook/index.zh-cn">Web.py Cookbook 简体中文版</a></li>
<li><a href="http://woodpecker.org.cn/diveintopython/">Dive Into Python 中文版</a></li>
<li><a href="https://associates.amazon.cn/gp/associates/network/main.html">Bottle 文档中文版</a> (需翻墙)</li>
<li><a href="http://docs.jinkan.org/docs/flask/">Flask 文档中文版</a></li>
<li><a href="http://docs.jinkan.org/docs/jinja2/">Jinja2 文档中文版</a></li>
<li><a href="http://werkzeug-docs-cn.readthedocs.org/zh_CN/latest/">Werkzeug 文档中文版</a></li>
<li><a href="http://spacewander.github.io/explore-flask-zh">Flask之旅</a></li>
<li><a href="http://demo.pythoner.com/itt2zh/index.html">Introduction to Tornado 中文翻译</a></li>
<li><a href="http://pan.baidu.com/s/1qW4pvnY">Python自然语言处理中文版</a> (感谢陈涛同学的翻译,也谢谢 <a href="https://github.com/shwley">@shwley</a> 联系了作者)</li>
<li><a href="http://liam0205.me/2014/09/11/matplotlib-tutorial-zh-cn/">Python 绘图库 matplotlib 官方指南中文翻译</a></li>
<li><a href="http://scrapy-chs.readthedocs.org/zh_CN/latest/">Scrapy 0.25 文档</a></li>
<li><a href="https://github.com/carfly/thinkpython-cn">ThinkPython</a></li>
<li><a href="http://www.cnblogs.com/vamei/archive/2012/09/13/2682778.html">Python快速教程</a></li>
<li><a href="http://wiki.ubuntu.org.cn/Python%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97">Python 正则表达式操作指南</a></li>
<li><a href="http://www.crifan.com/files/doc/docbook/python_beginner_tutorial/release/html/python_beginner_tutorial.html">python初级教程:入门详解</a></li>
<li><a href="http://likebeta.gitbooks.io/twisted-intro-cn/">Twisted 与异步编程入门</a></li>
<li><a href="http://textgrocery.readthedocs.org/zh/latest/index.html">TextGrocery 中文 API</a> ( 基于svm算法的一个短文本分类 Python 库 )</li>
<li><a href="http://requests-docs-cn.readthedocs.org/zh_CN/latest/">Requests: HTTP for Humans</a></li>
<li><a href="http://pillow-cn.readthedocs.org/en/latest/#">Pillow 中文文档</a></li>
<li><a href="http://pymotwcn.readthedocs.org/en/latest/index.html">PyMOTW 中文版</a></li>
<li><a href="http://data.digitser.net/zh-CN/python_index.html">Python 官方文档中文版</a></li>
<li><a href="http://fabric-chs.readthedocs.org">Fabric 中文文档</a></li>
</ul>
<h3>R</h3>
<ul>
<li><a href="https://github.com/yihui/r-ninja">R语言忍者秘笈</a></li>
</ul>
<h3>Ruby</h3>
<ul>
<li><a href="https://github.com/JuanitoFatas/ruby-style-guide/blob/master/README-zhCN.md">Ruby 风格指南</a></li>
<li><a href="https://github.com/JuanitoFatas/rails-style-guide/blob/master/README-zhCN.md">Rails 风格指南</a></li>
<li><a href="http://lrthw.github.io/">笨方法學 Ruby</a></li>
<li><a href="http://guides.ruby-china.org/">Ruby on Rails 指南</a></li>
<li><a href="http://ihower.tw/rails4/index.html">Ruby on Rails 實戰聖經</a></li>
<li><a href="http://railstutorial-china.org/">Ruby on Rails Tutorial 原书第 3 版</a> (本书网页版免费提供,电子版以 PDF、EPub 和 Mobi 格式提供购买,仅售 9.9 美元)</li>
<li><a href="http://rails-practice.com/content/index.html">Rails 实践</a></li>
<li><a href="http://wusuopu.gitbooks.io/write-ruby-extension-with-c/content/">编写Ruby的C拓展</a></li>
<li><a href="https://ruby-china.org/topics/22386">Ruby 源码解读</a></li>
<li><a href="http://deathking.github.io/metaprogramming-in-ruby/">Ruby中的元编程</a></li>
</ul>
<h3>Rust</h3>
<ul>
<li><a href="https://www.gitbook.com/book/kaisery/rust-book-chinese/details">rust book 中文翻译</a></li>
</ul>
<h3>Scala</h3>
<ul>
<li><a href="http://twitter.github.io/scala_school/zh_cn/index.html">Scala课堂</a> (Twitter的Scala中文教程)</li>
<li><a href="http://twitter.github.io/effectivescala/index-cn.html">Effective Scala</a>(Twitter的Scala最佳实践的中文翻译)</li>
<li><a href="http://zh.scala-tour.com/">Scala指南</a></li>
</ul>
<h3>Shell</h3>
<ul>
<li><a href="https://github.com/qinjx/30min_guides/blob/master/shell.md">Shell脚本编程30分钟入门</a></li>
<li><a href="http://blog.sae.sina.com.cn/archives/3606">Bash脚本15分钟进阶教程</a></li>
<li><a href="https://github.com/me115/linuxtools_rst">Linux工具快速教程</a></li>
<li><a href="https://github.com/wzb56/13_questions_of_shell">shell十三问</a></li>
<li><a href="http://tinylab.gitbooks.io/shellbook">Shell编程范例</a></li>
</ul>
<h3>Swift</h3>
<ul>
<li><a href="http://numbbbbb.github.io/the-swift-programming-language-in-chinese/">The Swift Programming Language 中文版</a></li>
<li><a href="http://dev.swiftguide.cn">Swift 语言指南</a></li>
<li><a href="https://github.com/x140yu/Developing_iOS_8_Apps_With_Swift">Stanford 公开课,Developing iOS 8 Apps with Swift 字幕翻译文件</a></li>
</ul>
<h3>读书笔记及其它</h3>
<ul>
<li><a href="https://github.com/fool2fish/dragon-book-exercise-answers">编译原理(紫龙书)中文第2版习题答案</a></li>
<li><a href="http://hawstein.com/posts/make-thiner-programming-pearls.html">把《编程珠玑》读薄</a></li>
<li><a href="https://github.com/XiaolongJason/ReadingNote/blob/master/Effective%20C%2B%2B/Effective%20C%2B%2B.md">Effective C++读书笔记</a></li>
<li><a href="https://github.com/qyuhen/book">Golang 学习笔记、Python 学习笔记、C 学习笔记</a> (PDF)</li>
<li><a href="https://github.com/code4craft/jsoup-learning">Jsoup 学习笔记</a></li>
<li><a href="https://github.com/lzjun567/note">学习笔记: Vim、Python、memcached</a></li>
<li><a href="http://www.ituring.com.cn/activity/details/2004">图灵开放书翻译计划--C++、Python、Java等</a></li>
<li><a href="http://g.yeeyan.org/books/2095">蒂姆·奥莱利随笔</a> (由译言网翻译,电子版免费)</li>
<li><a href="http://coer.zju.edu.cn/liu/octave-tutorial-cn.pdf">Octave 入门</a> (PDF版)</li>
<li><a href="http://sicp.readthedocs.org/en/latest/">SICP 解题集</a></li>
<li><a href="https://github.com/hacke2/hacke2.github.io/issues/2">精彩博客集合</a></li>
<li><a href="http://www.xiaoleilu.com/regex-guide/">正则表达式简明参考</a></li>
<li><a href="https://github.com/sparanoid/chinese-copywriting-guidelines">中文文案排版指北</a></li>
<li><a href="http://ganquan.info/standard-c/">Standard C 语言标准函数库速查 (Cheat Sheet)</a></li>
<li><a href="http://gh.amio.us/git-cheatsheet-chs/">Git Cheatsheet Chs</a></li>
<li><a href="https://github.com/qibaoguang/Study-Step-by-Step/blob/master/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/javascript_the_good_parts.md">JavaScript语言精粹</a></li>
<li><a href="http://producingoss.com/zh/">制造开源软件</a></li>
<li><a href="http://www.dianbo.org/9238/stone/tiwendezhihui.htm">提问的智慧</a></li>
<li><a href="https://github.com/LearnShare/Learning-Markdown">Markdown 入门参考</a></li>
<li><a href="https://github.com/stanzgy/wiki/blob/master/markup/asciidoc-guide.asciidoc">AsciiDoc简明指南</a></li>
<li><a href="http://love-oriented.com/pack/">背包问题九讲</a></li>
<li><a href="https://github.com/qiwsir/ITArticles">老齐的技术资料</a></li>
<li><a href="https://github.com/JacksonTian/fks">前端技能汇总</a></li>
<li><a href="https://github.com/zhuangbiaowei/learn-with-open-source">借助开源项目,学习软件开发</a></li>
<li><a href="https://github.com/h5bp/Front-end-Developer-Interview-Questions/tree/master/Translations/Chinese">前端工作面试问题</a></li>
<li><a href="https://www.gitbook.com/book/yuanbin/algorithm/details">leetcode/lintcode题解/算法学习笔记</a></li>
</ul>
<h3>测试相关</h3>
<ul>
<li><a href="http://appium.io/slate/cn/v1.2.0/">移动APP自动化测试优秀框架Appium API Reference V1.2.0 CN</a></li>
</ul>
swift 关键字和符号2015-09-22T00:00:00+00:00http://jiaxianhua.github.io/swift/2015/09/22/swift-keyworks<h1>keywords and identifiers</h1>
<hr />
<p>Keywords used in declarations:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span><span class="p">,</span> <span class="kd">deinit</span><span class="p">,</span> <span class="kd">enum</span><span class="p">,</span> <span class="kd">extension</span><span class="p">,</span> <span class="kd">func</span><span class="p">,</span> <span class="kd">import</span><span class="p">,</span> <span class="kd">init</span><span class="p">,</span> <span class="k">inout</span><span class="p">,</span> <span class="kd">internal</span><span class="p">,</span> <span class="kd">let</span><span class="p">,</span> <span class="k">operator</span><span class="p">,</span> <span class="kd">private</span><span class="p">,</span> <span class="kd">protocol</span><span class="p">,</span> <span class="kd">public</span><span class="p">,</span> <span class="kd">static</span><span class="p">,</span> <span class="kd">struct</span><span class="p">,</span> <span class="kd">subscript</span><span class="p">,</span> <span class="kd">typealias</span><span class="p">,</span> <span class="kd">var</span></code></pre></figure></p>
<p>Keywords used in statements:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">break</span><span class="p">,</span> <span class="k">case</span><span class="p">,</span> <span class="k">continue</span><span class="p">,</span> <span class="k">default</span><span class="p">,</span> <span class="k">defer</span><span class="p">,</span> <span class="k">do</span><span class="p">,</span> <span class="k">else</span><span class="p">,</span> <span class="k">fallthrough</span><span class="p">,</span> <span class="k">for</span><span class="p">,</span> <span class="k">guard</span><span class="p">,</span> <span class="k">if</span><span class="p">,</span> <span class="k">in</span><span class="p">,</span> <span class="k">repeat</span><span class="p">,</span> <span class="k">return</span><span class="p">,</span> <span class="k">switch</span><span class="p">,</span> <span class="k">where</span><span class="p">,</span> <span class="k">while</span></code></pre></figure></p>
<p>Keywords used in expressions and types:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">as</span><span class="p">,</span> <span class="k">catch</span><span class="p">,</span> <span class="k">dynamicType</span><span class="p">,</span> <span class="kc">false</span><span class="p">,</span> <span class="k">is</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="k">rethrows</span><span class="p">,</span> <span class="k">super</span><span class="p">,</span> <span class="k">self</span><span class="p">,</span> <span class="k">Self</span><span class="p">,</span> <span class="k">throw</span><span class="p">,</span> <span class="k">throws</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="k">try</span><span class="p">,</span> <span class="k"><strong>COLUMN</strong></span><span class="p">,</span> <span class="k"><strong>FILE</strong></span><span class="p">,</span> <span class="k"><strong>FUNCTION</strong></span><span class="p">,</span> <span class="k"><strong>LINE</strong></span></code></pre></figure></p>
<p>Keywords used in patterns:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="n">_</span></code></pre></figure></p>
<p>Keywords reserved in particular contexts:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">as</span><span class="n">sociativity</span><span class="p">,</span> <span class="n">convenience</span><span class="p">,</span> <span class="kd">dynamic</span><span class="p">,</span> <span class="k">didSet</span><span class="p">,</span> <span class="kd">final</span><span class="p">,</span> <span class="k">get</span><span class="p">,</span> <span class="k">infix</span><span class="p">,</span> <span class="n">indirect</span><span class="p">,</span> <span class="kd">lazy</span><span class="p">,</span> <span class="k">left</span><span class="p">,</span> <span class="k">mutating</span><span class="p">,</span> <span class="k">none</span><span class="p">,</span> <span class="k">nonmutating</span><span class="p">,</span> <span class="kd">optional</span><span class="p">,</span> <span class="k">override</span><span class="p">,</span> <span class="k">postfix</span><span class="p">,</span> <span class="k">precedence</span><span class="p">,</span> <span class="k">prefix</span><span class="p">,</span> <span class="kt">Protocol</span><span class="p">,</span> <span class="kd">required</span><span class="p">,</span> <span class="k">right</span><span class="p">,</span> <span class="k">set</span><span class="p">,</span> <span class="k">Type</span><span class="p">,</span> <span class="k">unowned</span><span class="p">,</span> <span class="k">weak</span><span class="p">,</span> <span class="k">willSet</span></code></pre></figure></p>
<blockquote><p>Outside the context in which they appear in the grammar, they can be used as identifiers.</p></blockquote>
<p>The following tokens are reserved as punctuation and can’t be used as custom operators:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="p">(,</span> <span class="p">),</span> <span class="p">{,</span> <span class="p">},</span> <span class="p">[,</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> <span class="o">=</span><span class="p">,</span> <span class="err">@</span><span class="p">,</span> <span class="err">#</span><span class="p">,</span> <span class="o">&</span> <span class="p">(</span><span class="k">as</span> <span class="n">a</span> <span class="k">prefix</span> <span class="k">operator</span><span class="p">),</span> <span class="o">-></span><span class="p">,</span> <span class="err">`</span><span class="p">,</span> <span class="p">?,</span> <span class="n">and</span> <span class="o">!</span> <span class="p">(</span><span class="k">as</span> <span class="n">a</span> <span class="k">postfix</span> <span class="k">operator</span><span class="p">)</span></code></pre></figure></p>
<h1>关键字和符号</h1>
<hr />
<p>下面这些被保留的关键字(keywords)不允许用作标识符,除非被反引号转义,具体描述请参考 标识符。</p>
<ul>
<li>用在声明中的关键字:</li>
</ul>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span><span class="err">、</span><span class="kd">deinit</span><span class="err">、</span><span class="kd">enum</span><span class="err">、</span><span class="kd">extension</span><span class="err">、</span><span class="kd">func</span><span class="err">、</span><span class="kd">import</span><span class="err">、</span><span class="kd">init</span><span class="err">、</span><span class="kd">let</span><span class="err">、</span><span class="kd">protocol</span><span class="err">、</span><span class="kd">static</span><span class="err">、</span><span class="kd">struct</span><span class="err">、</span><span class="kd">subscript</span><span class="err">、</span><span class="kd">typealias</span><span class="err">、</span><span class="kd">var</span></code></pre></figure></p>
<ul>
<li>用在语句中的关键字:</li>
</ul>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">break</span><span class="err">、</span><span class="k">case</span><span class="err">、</span><span class="k">continue</span><span class="err">、</span><span class="k">default</span><span class="err">、</span><span class="k">do</span><span class="err">、</span><span class="k">else</span><span class="err">、</span><span class="k">fallthrough</span><span class="err">、</span><span class="k">if</span><span class="err">、</span><span class="k">in</span><span class="err">、</span><span class="k">for</span><span class="err">、</span><span class="k">return</span><span class="err">、</span><span class="k">switch</span><span class="err">、</span><span class="k">where</span><span class="err">、</span><span class="k">while</span></code></pre></figure></p>
<ul>
<li>用在表达式和类型中的关键字:</li>
</ul>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">as</span><span class="err">、</span><span class="k">dynamicType</span><span class="err">、</span><span class="k">is</span><span class="err">、</span><span class="k">new</span><span class="err">、</span><span class="k">super</span><span class="err">、</span><span class="k">self</span><span class="err">、</span><span class="k">Self</span><span class="err">、</span><span class="k">Type</span><span class="err">、</span><span class="k"><strong>COLUMN</strong></span><span class="err">、</span><span class="k"><strong>FILE</strong></span><span class="err">、</span><span class="k"><strong>FUNCTION</strong></span><span class="err">、</span><span class="k"><strong>LINE</strong></span></code></pre></figure></p>
<ul>
<li>用在模式中的关键字:</li>
</ul>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="n">_</span></code></pre></figure></p>
<ul>
<li>特定上下文中被保留的关键字:</li>
</ul>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">as</span><span class="n">sociativity</span><span class="err">、</span><span class="k">didSet</span><span class="err">、</span><span class="k">get</span><span class="err">、</span><span class="k">infix</span><span class="err">、</span><span class="k">inout</span><span class="err">、</span><span class="k">left</span><span class="err">、</span><span class="k">mutating</span><span class="err">、</span><span class="k">none</span><span class="err">、</span><span class="k">nonmutating</span><span class="err">、</span><span class="k">operator</span><span class="err">、</span><span class="k">override</span><span class="err">、</span><span class="k">postfix</span><span class="err">、</span><span class="k">precedence</span><span class="err">、</span><span class="k">prefix</span><span class="err">、</span><span class="k">right</span><span class="err">、</span><span class="k">set</span><span class="err">、</span><span class="k">unowned</span><span class="err">、</span><span class="kd">unowned(safe)</span><span class="err">、</span><span class="kd">unowned(unsafe)</span><span class="err">、</span><span class="k">weak</span><span class="err">、</span><span class="k">willSet</span></code></pre></figure></p>
<blockquote><p>这些关键字在特定上下文之外可以被用于标识符。</p></blockquote>
<ul>
<li>以下标记被当作保留符号,不能用于自定义操作符:</li>
</ul>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"> <span class="p">(</span> <span class="err">、</span> <span class="p">)</span> <span class="err">、</span> <span class="p">{</span> <span class="err">、</span> <span class="p">}</span> <span class="err">、</span> <span class="p">[</span> <span class="err">、</span> <span class="p">]</span> <span class="err">、</span> <span class="o">.</span> <span class="err">、</span> <span class="p">,</span> <span class="err">、</span> <span class="p">:</span> <span class="err">、</span> <span class="p">;</span> <span class="err">、</span> <span class="o">=</span> <span class="err">、</span> <span class="err">@</span> <span class="err">、</span> <span class="err">#</span> <span class="err">、</span> <span class="o">&</span><span class="p">(</span><span class="n">作为前缀操作符</span><span class="p">)</span> <span class="err">、</span> <span class="o">-></span> <span class="err">、<code>`</span> <span class="err">、</span> <span class="p">?</span> <span class="n">和</span> <span class="o">!</span><span class="p">(</span><span class="n">作为后缀操作符</span><span class="p">)</span><span class="err"></code>。</span></code></pre></figure></p>
The Swift Programming Language (Swift 2) -7 Generics2015-09-21T00:00:00+00:00http://jiaxianhua.github.io/swift/2015/09/21/the-swift-programming-language-swift-2-(7)-generics<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//: ## Generics</span>
<span class="c1">//:</span>
<span class="c1">//: Write a name inside angle brackets to make a generic function or type.</span>
<span class="c1">//:</span>
<span class="kd">func</span> <span class="n">repeatItem</span><span class="o"><</span><span class="kt">Item</span><span class="o">></span><span class="p">(</span><span class="nv">item</span><span class="p">:</span> <span class="kt">Item</span><span class="p">,</span> <span class="nv">numberOfTimes</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">Item</span><span class="p">]</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">result</span> <span class="o">=</span> <span class="p"><a href=""></span><span class="kt">Item</span><span class="p"></a></span>
<span class="k">for</span> <span class="n">_</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..<</span><span class="n">numberOfTimes</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">item</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">repeatItem</span><span class="p">(</span><span class="s">"knock"</span><span class="p">,</span> <span class="nv">numberOfTimes</span><span class="p">:</span><span class="mi">4</span><span class="p">)</span></p>
<p><span class="c1">//: You can make generic forms of functions and methods, as well as classes, enumerations, and structures.</span>
<span class="c1">//:</span>
<span class="c1">// Reimplement the Swift standard library's optional type</span>
<span class="kd">enum</span> <span class="kt">OptionalValue</span><span class="o"><</span><span class="kt">Wrapped</span><span class="o">></span> <span class="p">{</span>
<span class="k">case</span> <span class="kt">None</span>
<span class="k">case</span> <span class="kt">Some</span><span class="p">(</span><span class="kt">Wrapped</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">possibleInteger</span><span class="p">:</span> <span class="kt">OptionalValue</span><span class="o"><</span><span class="kt">Int</span><span class="o">></span> <span class="o">=</span> <span class="o">.</span><span class="kt">None</span>
<span class="n">possibleInteger</span> <span class="o">=</span> <span class="o">.</span><span class="kt">Some</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span></p>
<p><span class="c1">//: Use <code>where</code> after the type name to specify a list of requirements—for example, to require the type to implement a protocol, to require two types to be the same, or to require a class to have a particular superclass.</span>
<span class="c1">//:</span>
<span class="kd">func</span> <span class="n">anyCommonElements</span> <span class="o"><</span><span class="kt">T</span><span class="p">:</span> <span class="kt">SequenceType</span><span class="p">,</span> <span class="kt">U</span><span class="p">:</span> <span class="kt">SequenceType</span> <span class="k">where</span> <span class="kt">T</span><span class="o">.</span><span class="kt">Generator</span><span class="o">.</span><span class="kt">Element</span><span class="p">:</span> <span class="kt">Equatable</span><span class="p">,</span> <span class="kt">T</span><span class="o">.</span><span class="kt">Generator</span><span class="o">.</span><span class="kt">Element</span> <span class="o">==</span> <span class="kt">U</span><span class="o">.</span><span class="kt">Generator</span><span class="o">.</span><span class="kt">Element</span><span class="o">></span> <span class="p">(</span><span class="nv">lhs</span><span class="p">:</span> <span class="kt">T</span><span class="p">,</span> <span class="n">_</span> <span class="nv">rhs</span><span class="p">:</span> <span class="kt">U</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="n">lhsItem</span> <span class="k">in</span> <span class="n">lhs</span> <span class="p">{</span>
<span class="k">for</span> <span class="n">rhsItem</span> <span class="k">in</span> <span class="n">rhs</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">lhsItem</span> <span class="o">==</span> <span class="n">rhsItem</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">true</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="nf">anyCommonElements</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">],</span> <span class="p">[</span><span class="mi">3</span><span class="p">])</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Modify the <code>anyCommonElements(_:_:)</code> function to make a function that returns an array of the elements that any two sequences have in common.</span>
<span class="c1">//:</span>
<span class="c1">//: Writing <code>&lt;T: Equatable&gt;</code> is the same as writing <code>&lt;T where T: Equatable&gt;</code>.</span>
<span class="c1">//:</span></p>
<p><span class="c1">//: <a href="@previous">Previous</a></span></code></pre></figure></p>
The Swift Programming Language (Swift 2) -6 Protocols and Extensions2015-09-21T00:00:00+00:00http://jiaxianhua.github.io/swift/2015/09/21/the-swift-programming-language-swift-2-(6)-protocols-and-Extensions<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//: ## Protocols and Extensions</span>
<span class="c1">//:</span>
<span class="c1">//: Use <code>protocol</code> to declare a protocol.</span>
<span class="c1">//:</span>
<span class="kd">protocol</span> <span class="kt">ExampleProtocol</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">simpleDescription</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="k">mutating</span> <span class="kd">func</span> <span class="nf">adjust</span><span class="p">()</span>
<span class="p">}</span></p>
<p><span class="c1">//: Classes, enumerations, and structs can all adopt protocols.</span>
<span class="c1">//:</span>
<span class="kd">class</span> <span class="kt">SimpleClass</span><span class="p">:</span> <span class="kt">ExampleProtocol</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">simpleDescription</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"A very simple class."</span>
<span class="k">var</span> <span class="nv">anotherProperty</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">69105</span>
<span class="kd">func</span> <span class="nf">adjust</span><span class="p">()</span> <span class="p">{</span>
<span class="n">simpleDescription</span> <span class="o">+=</span> <span class="s">" Now 100% adjusted."</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">a</span> <span class="o">=</span> <span class="kt">SimpleClass</span><span class="p">()</span>
<span class="n">a</span><span class="o">.</span><span class="nf">adjust</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">aDescription</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">simpleDescription</span></p>
<p><span class="kd">struct</span> <span class="kt">SimpleStructure</span><span class="p">:</span> <span class="kt">ExampleProtocol</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">simpleDescription</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"A simple structure"</span>
<span class="k">mutating</span> <span class="kd">func</span> <span class="nf">adjust</span><span class="p">()</span> <span class="p">{</span>
<span class="n">simpleDescription</span> <span class="o">+=</span> <span class="s">" (adjusted)"</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">b</span> <span class="o">=</span> <span class="kt">SimpleStructure</span><span class="p">()</span>
<span class="n">b</span><span class="o">.</span><span class="nf">adjust</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">bDescription</span> <span class="o">=</span> <span class="n">b</span><span class="o">.</span><span class="n">simpleDescription</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Write an enumeration that conforms to this protocol.</span>
<span class="c1">//:</span>
<span class="c1">//: Notice the use of the <code>mutating</code> keyword in the declaration of <code>SimpleStructure</code> to mark a method that modifies the structure. The declaration of <code>SimpleClass</code> doesn’t need any of its methods marked as mutating because methods on a class can always modify the class.</span>
<span class="c1">//:</span>
<span class="c1">//: Use <code>extension</code> to add functionality to an existing type, such as new methods and computed properties. You can use an extension to add protocol conformance to a type that is declared elsewhere, or even to a type that you imported from a library or framework.</span>
<span class="c1">//:</span>
<span class="kd">extension</span> <span class="kt">Int</span><span class="p">:</span> <span class="kt">ExampleProtocol</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">simpleDescription</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">return</span> <span class="s">"The number </span><span class="se">(</span><span class="k">self</span><span class="se">)</span><span class="s">"</span>
<span class="p">}</span>
<span class="k">mutating</span> <span class="kd">func</span> <span class="nf">adjust</span><span class="p">()</span> <span class="p">{</span>
<span class="k">self</span> <span class="o">+=</span> <span class="mi">42</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="mi">7</span><span class="o">.</span><span class="n">simpleDescription</span><span class="p">)</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Write an extension for the <code>Double</code> type that adds an <code>absoluteValue</code> property.</span>
<span class="c1">//:</span>
<span class="c1">//: You can use a protocol name just like any other named type—for example, to create a collection of objects that have different types but that all conform to a single protocol. When you work with values whose type is a protocol type, methods outside the protocol definition are not available.</span>
<span class="c1">//:</span>
<span class="k">let</span> <span class="nv">protocolValue</span><span class="p">:</span> <span class="kt">ExampleProtocol</span> <span class="o">=</span> <span class="n">a</span>
<span class="nf">print</span><span class="p">(</span><span class="n">protocolValue</span><span class="o">.</span><span class="n">simpleDescription</span><span class="p">)</span>
<span class="c1">// print(protocolValue.anotherProperty) // Uncomment to see the error</span></p>
<p><span class="c1">//: Even though the variable <code>protocolValue</code> has a runtime type of <code>SimpleClass</code>, the compiler treats it as the given type of <code>ExampleProtocol</code>. This means that you can’t accidentally access methods or properties that the class implements in addition to its protocol conformance.</span>
<span class="c1">//:</span></p>
<p><span class="c1">//: <a href="@previous">Previous</a> | <a href="@next">Next</a></span></code></pre></figure></p>
The Swift Programming Language (Swift 2) -5 Objects and Classes2015-09-21T00:00:00+00:00http://jiaxianhua.github.io/swift/2015/09/21/the-swift-programming-language-swift-2-(5)-objects-and-classes<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//: ## Protocols and Extensions</span>
<span class="c1">//:</span>
<span class="c1">//: Use <code>protocol</code> to declare a protocol.</span>
<span class="c1">//:</span>
<span class="kd">protocol</span> <span class="kt">ExampleProtocol</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">simpleDescription</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="k">mutating</span> <span class="kd">func</span> <span class="nf">adjust</span><span class="p">()</span>
<span class="p">}</span></p>
<p><span class="c1">//: Classes, enumerations, and structs can all adopt protocols.</span>
<span class="c1">//:</span>
<span class="kd">class</span> <span class="kt">SimpleClass</span><span class="p">:</span> <span class="kt">ExampleProtocol</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">simpleDescription</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"A very simple class."</span>
<span class="k">var</span> <span class="nv">anotherProperty</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">69105</span>
<span class="kd">func</span> <span class="nf">adjust</span><span class="p">()</span> <span class="p">{</span>
<span class="n">simpleDescription</span> <span class="o">+=</span> <span class="s">" Now 100% adjusted."</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">a</span> <span class="o">=</span> <span class="kt">SimpleClass</span><span class="p">()</span>
<span class="n">a</span><span class="o">.</span><span class="nf">adjust</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">aDescription</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="n">simpleDescription</span></p>
<p><span class="kd">struct</span> <span class="kt">SimpleStructure</span><span class="p">:</span> <span class="kt">ExampleProtocol</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">simpleDescription</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">"A simple structure"</span>
<span class="k">mutating</span> <span class="kd">func</span> <span class="nf">adjust</span><span class="p">()</span> <span class="p">{</span>
<span class="n">simpleDescription</span> <span class="o">+=</span> <span class="s">" (adjusted)"</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">b</span> <span class="o">=</span> <span class="kt">SimpleStructure</span><span class="p">()</span>
<span class="n">b</span><span class="o">.</span><span class="nf">adjust</span><span class="p">()</span>
<span class="k">let</span> <span class="nv">bDescription</span> <span class="o">=</span> <span class="n">b</span><span class="o">.</span><span class="n">simpleDescription</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Write an enumeration that conforms to this protocol.</span>
<span class="c1">//:</span>
<span class="c1">//: Notice the use of the <code>mutating</code> keyword in the declaration of <code>SimpleStructure</code> to mark a method that modifies the structure. The declaration of <code>SimpleClass</code> doesn’t need any of its methods marked as mutating because methods on a class can always modify the class.</span>
<span class="c1">//:</span>
<span class="c1">//: Use <code>extension</code> to add functionality to an existing type, such as new methods and computed properties. You can use an extension to add protocol conformance to a type that is declared elsewhere, or even to a type that you imported from a library or framework.</span>
<span class="c1">//:</span>
<span class="kd">extension</span> <span class="kt">Int</span><span class="p">:</span> <span class="kt">ExampleProtocol</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">simpleDescription</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">return</span> <span class="s">"The number </span><span class="se">(</span><span class="k">self</span><span class="se">)</span><span class="s">"</span>
<span class="p">}</span>
<span class="k">mutating</span> <span class="kd">func</span> <span class="nf">adjust</span><span class="p">()</span> <span class="p">{</span>
<span class="k">self</span> <span class="o">+=</span> <span class="mi">42</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="mi">7</span><span class="o">.</span><span class="n">simpleDescription</span><span class="p">)</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Write an extension for the <code>Double</code> type that adds an <code>absoluteValue</code> property.</span>
<span class="c1">//:</span>
<span class="c1">//: You can use a protocol name just like any other named type—for example, to create a collection of objects that have different types but that all conform to a single protocol. When you work with values whose type is a protocol type, methods outside the protocol definition are not available.</span>
<span class="c1">//:</span>
<span class="k">let</span> <span class="nv">protocolValue</span><span class="p">:</span> <span class="kt">ExampleProtocol</span> <span class="o">=</span> <span class="n">a</span>
<span class="nf">print</span><span class="p">(</span><span class="n">protocolValue</span><span class="o">.</span><span class="n">simpleDescription</span><span class="p">)</span>
<span class="c1">// print(protocolValue.anotherProperty) // Uncomment to see the error</span></p>
<p><span class="c1">//: Even though the variable <code>protocolValue</code> has a runtime type of <code>SimpleClass</code>, the compiler treats it as the given type of <code>ExampleProtocol</code>. This means that you can’t accidentally access methods or properties that the class implements in addition to its protocol conformance.</span>
<span class="c1">//:</span></p>
<p><span class="c1">//: <a href="@previous">Previous</a> | <a href="@next">Next</a></span></code></pre></figure></p>
The Swift Programming Language (Swift 2) -4 Enumerations and Structures2015-09-21T00:00:00+00:00http://jiaxianhua.github.io/swift/2015/09/21/the-swift-programming-language-swift-2-(4)-enumerations-and-structures<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//: ## Enumerations and Structures</span>
<span class="c1">//:</span>
<span class="c1">//: Use <code>enum</code> to create an enumeration. Like classes and all other named types, enumerations can have methods associated with them.</span>
<span class="c1">//:</span>
<span class="kd">enum</span> <span class="kt">Rank</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">case</span> <span class="kt">Ace</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">case</span> <span class="kt">Two</span><span class="p">,</span> <span class="kt">Three</span><span class="p">,</span> <span class="kt">Four</span><span class="p">,</span> <span class="kt">Five</span><span class="p">,</span> <span class="kt">Six</span><span class="p">,</span> <span class="kt">Seven</span><span class="p">,</span> <span class="kt">Eight</span><span class="p">,</span> <span class="kt">Nine</span><span class="p">,</span> <span class="kt">Ten</span>
<span class="k">case</span> <span class="kt">Jack</span><span class="p">,</span> <span class="kt">Queen</span><span class="p">,</span> <span class="kt">King</span>
<span class="kd">func</span> <span class="nf">simpleDescription</span><span class="p">()</span> <span class="o">-></span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">switch</span> <span class="k">self</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">Ace</span><span class="p">:</span>
<span class="k">return</span> <span class="s">"ace"</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">Jack</span><span class="p">:</span>
<span class="k">return</span> <span class="s">"jack"</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">Queen</span><span class="p">:</span>
<span class="k">return</span> <span class="s">"queen"</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">King</span><span class="p">:</span>
<span class="k">return</span> <span class="s">"king"</span>
<span class="k">default</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">String</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">rawValue</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">let</span> <span class="nv">ace</span> <span class="o">=</span> <span class="kt">Rank</span><span class="o">.</span><span class="kt">Ace</span>
<span class="k">let</span> <span class="nv">aceRawValue</span> <span class="o">=</span> <span class="n">ace</span><span class="o">.</span><span class="n">rawValue</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Write a function that compares two <code>Rank</code> values by comparing their raw values.</span>
<span class="c1">//:</span>
<span class="c1">//: In the example above, the raw-value type of the enumeration is <code>Int</code>, so you only have to specify the first raw value. The rest of the raw values are assigned in order. You can also use strings or floating-point numbers as the raw type of an enumeration. Use the <code>rawValue</code> property to access the raw value of an enumeration member.</span>
<span class="c1">//:</span>
<span class="c1">//: Use the <code>init?(rawValue:)</code> initializer to make an instance of an enumeration from a raw value.</span>
<span class="c1">//:</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">convertedRank</span> <span class="o">=</span> <span class="kt">Rank</span><span class="p">(</span><span class="nv">rawValue</span><span class="p">:</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">threeDescription</span> <span class="o">=</span> <span class="n">convertedRank</span><span class="o">.</span><span class="nf">simpleDescription</span><span class="p">()</span>
<span class="p">}</span></p>
<p><span class="c1">//: The member values of an enumeration are actual values, not just another way of writing their raw values. In fact, in cases where there isn’t a meaningful raw value, you don’t have to provide one.</span>
<span class="c1">//:</span>
<span class="kd">enum</span> <span class="kt">Suit</span> <span class="p">{</span>
<span class="k">case</span> <span class="kt">Spades</span><span class="p">,</span> <span class="kt">Hearts</span><span class="p">,</span> <span class="kt">Diamonds</span><span class="p">,</span> <span class="kt">Clubs</span>
<span class="kd">func</span> <span class="nf">simpleDescription</span><span class="p">()</span> <span class="o">-></span> <span class="kt">String</span> <span class="p">{</span>
<span class="k">switch</span> <span class="k">self</span> <span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">Spades</span><span class="p">:</span>
<span class="k">return</span> <span class="s">"spades"</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">Hearts</span><span class="p">:</span>
<span class="k">return</span> <span class="s">"hearts"</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">Diamonds</span><span class="p">:</span>
<span class="k">return</span> <span class="s">"diamonds"</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">Clubs</span><span class="p">:</span>
<span class="k">return</span> <span class="s">"clubs"</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">let</span> <span class="nv">hearts</span> <span class="o">=</span> <span class="kt">Suit</span><span class="o">.</span><span class="kt">Hearts</span>
<span class="k">let</span> <span class="nv">heartsDescription</span> <span class="o">=</span> <span class="n">hearts</span><span class="o">.</span><span class="nf">simpleDescription</span><span class="p">()</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Add a <code>color()</code> method to <code>Suit</code> that returns “black” for spades and clubs, and returns “red” for hearts and diamonds.</span>
<span class="c1">//:</span>
<span class="c1">//: Notice the two ways that the <code>Hearts</code> member of the enumeration is referred to above: When assigning a value to the <code>hearts</code> constant, the enumeration member <code>Suit.Hearts</code> is referred to by its full name because the constant doesn’t have an explicit type specified. Inside the switch, the enumeration member is referred to by the abbreviated form <code>.Hearts</code> because the value of <code>self</code> is already known to be a suit. You can use the abbreviated form anytime the value’s type is already known.</span>
<span class="c1">//:</span>
<span class="c1">//: Use <code>struct</code> to create a structure. Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference.</span>
<span class="c1">//:</span>
<span class="kd">struct</span> <span class="kt">Card</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">rank</span><span class="p">:</span> <span class="kt">Rank</span>
<span class="k">var</span> <span class="nv">suit</span><span class="p">:</span> <span class="kt">Suit</span>
<span class="kd">func</span> <span class="nf">simpleDescription</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">"The </span><span class="se">(</span><span class="n">rank</span><span class="o">.</span><span class="nf">simpleDescription</span><span class="p">()</span><span class="se">)</span><span class="s"> of </span><span class="se">(</span><span class="n">suit</span><span class="o">.</span><span class="nf">simpleDescription</span><span class="p">()</span><span class="se">)</span><span class="s">"</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">let</span> <span class="nv">threeOfSpades</span> <span class="o">=</span> <span class="kt">Card</span><span class="p">(</span><span class="nv">rank</span><span class="p">:</span> <span class="o">.</span><span class="kt">Three</span><span class="p">,</span> <span class="nv">suit</span><span class="p">:</span> <span class="o">.</span><span class="kt">Spades</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">threeOfSpadesDescription</span> <span class="o">=</span> <span class="n">threeOfSpades</span><span class="o">.</span><span class="nf">simpleDescription</span><span class="p">()</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Add a method to <code>Card</code> that creates a full deck of cards, with one card of each combination of rank and suit.</span>
<span class="c1">//:</span>
<span class="c1">//: An instance of an enumeration member can have values associated with the instance. Instances of the same enumeration member can have different values associated with them. You provide the associated values when you create the instance. Associated values and raw values are different: The raw value of an enumeration member is the same for all of its instances, and you provide the raw value when you define the enumeration.</span>
<span class="c1">//:</span>
<span class="c1">//: For example, consider the case of requesting the sunrise and sunset time from a server. The server either responds with the information or it responds with some error information.</span>
<span class="c1">//:</span>
<span class="kd">enum</span> <span class="kt">ServerResponse</span> <span class="p">{</span>
<span class="k">case</span> <span class="kt">Result</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="k">case</span> <span class="kt">Error</span><span class="p">(</span><span class="kt">String</span><span class="p">)</span>
<span class="p">}</span></p>
<p><span class="k">let</span> <span class="nv">success</span> <span class="o">=</span> <span class="kt">ServerResponse</span><span class="o">.</span><span class="kt">Result</span><span class="p">(</span><span class="s">"6:00 am"</span><span class="p">,</span> <span class="s">"8:09 pm"</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">failure</span> <span class="o">=</span> <span class="kt">ServerResponse</span><span class="o">.</span><span class="kt">Error</span><span class="p">(</span><span class="s">"Out of cheese."</span><span class="p">)</span></p>
<p><span class="k">switch</span> <span class="n">success</span> <span class="p">{</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="kt">Result</span><span class="p">(</span><span class="n">sunrise</span><span class="p">,</span> <span class="n">sunset</span><span class="p">):</span>
<span class="k">let</span> <span class="nv">serverResponse</span> <span class="o">=</span> <span class="s">"Sunrise is at </span><span class="se">(</span><span class="n">sunrise</span><span class="se">)</span><span class="s"> and sunset is at </span><span class="se">(</span><span class="n">sunset</span><span class="se">)</span><span class="s">."</span>
<span class="k">case</span> <span class="kd">let</span> <span class="o">.</span><span class="kt">Error</span><span class="p">(</span><span class="n">error</span><span class="p">):</span>
<span class="k">let</span> <span class="nv">serverResponse</span> <span class="o">=</span> <span class="s">"Failure... </span><span class="se">(</span><span class="n">error</span><span class="se">)</span><span class="s">"</span>
<span class="p">}</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Add a third case to <code>ServerResponse</code> and to the switch.</span>
<span class="c1">//:</span>
<span class="c1">//: Notice how the sunrise and sunset times are extracted from the <code>ServerResponse</code> value as part of matching the value against the switch cases.</span>
<span class="c1">//:</span></p>
<p><span class="c1">//: <a href="@previous">Previous</a> | <a href="@next">Next</a></span></code></pre></figure></p>
The Swift Programming Language (Swift 2) -3 Function and Closures2015-09-21T00:00:00+00:00http://jiaxianhua.github.io/swift/2015/09/21/the-swift-programming-language-swift-2-(3)-functions-and-Closures<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//: ## Functions and Closures</span>
<span class="c1">//:</span>
<span class="c1">//: Use <code>func</code> to declare a function. Call a function by following its name with a list of arguments in parentheses. Use <code>-&gt;</code> to separate the parameter names and types from the function’s return type.</span>
<span class="c1">//:</span>
<span class="kd">func</span> <span class="nf">greet</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">day</span><span class="p">:</span> <span class="kt">String</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 </span><span class="se">(</span><span class="n">name</span><span class="se">)</span><span class="s">, today is </span><span class="se">(</span><span class="n">day</span><span class="se">)</span><span class="s">."</span>
<span class="p">}</span>
<span class="nf">greet</span><span class="p">(</span><span class="s">"Bob"</span><span class="p">,</span> <span class="nv">day</span><span class="p">:</span> <span class="s">"Tuesday"</span><span class="p">)</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Remove the <code>day</code> parameter. Add a parameter to include today’s lunch special in the greeting.</span>
<span class="c1">//:</span>
<span class="c1">//: Use a tuple to make a compound value—for example, to return multiple values from a function. The elements of a tuple can be referred to either by name or by number.</span>
<span class="c1">//:</span>
<span class="kd">func</span> <span class="nf">calculateStatistics</span><span class="p">(</span><span class="nv">scores</span><span class="p">:</span> <span class="p">[</span><span class="kt">Int</span><span class="p">])</span> <span class="o">-></span> <span class="p">(</span><span class="nv">min</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">max</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">sum</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">min</span> <span class="o">=</span> <span class="n">scores</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">var</span> <span class="nv">max</span> <span class="o">=</span> <span class="n">scores</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">var</span> <span class="nv">sum</span> <span class="o">=</span> <span class="mi">0</span></p>
<pre><code><span class="k">for</span> <span class="n">score</span> <span class="k">in</span> <span class="n">scores</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">score</span> <span class="o">&gt;</span> <span class="n">max</span> <span class="p">{</span>
<span class="n">max</span> <span class="o">=</span> <span class="n">score</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">score</span> <span class="o">&lt;</span> <span class="n">min</span> <span class="p">{</span>
<span class="n">min</span> <span class="o">=</span> <span class="n">score</span>
<span class="p">}</span>
<span class="n">sum</span> <span class="o">+=</span> <span class="n">score</span>
<span class="p">}</span>
<span class="nf">return</span> <span class="p">(</span><span class="n">min</span><span class="p">,</span> <span class="n">max</span><span class="p">,</span> <span class="n">sum</span><span class="p">)</span>
</code></pre>
<p><span class="p">}</span>
<span class="k">let</span> <span class="nv">statistics</span> <span class="o">=</span> <span class="nf">calculateStatistics</span><span class="p">([</span><span class="mi">5</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">3</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="n">statistics</span><span class="o">.</span><span class="n">sum</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="n">statistics</span><span class="o">.</span><span class="mi">2</span><span class="p">)</span></p>
<p><span class="c1">//: Functions can also take a variable number of arguments, collecting them into an array.</span>
<span class="c1">//:</span>
<span class="kd">func</span> <span class="nf">sumOf</span><span class="p">(</span><span class="nv">numbers</span><span class="p">:</span> <span class="kt">Int</span><span class="o">...</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">sum</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">number</span> <span class="k">in</span> <span class="n">numbers</span> <span class="p">{</span>
<span class="n">sum</span> <span class="o">+=</span> <span class="n">number</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">sum</span>
<span class="p">}</span>
<span class="nf">sumOf</span><span class="p">()</span>
<span class="nf">sumOf</span><span class="p">(</span><span class="mi">42</span><span class="p">,</span> <span class="mi">597</span><span class="p">,</span> <span class="mi">12</span><span class="p">)</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Write a function that calculates the average of its arguments.</span>
<span class="c1">//:</span>
<span class="c1">//: Functions can be nested. Nested functions have access to variables that were declared in the outer function. You can use nested functions to organize the code in a function that is long or complex.</span>
<span class="c1">//:</span>
<span class="kd">func</span> <span class="nf">returnFifteen</span><span class="p">()</span> <span class="o">-></span> <span class="kt">Int</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">y</span> <span class="o">=</span> <span class="mi">10</span>
<span class="kd">func</span> <span class="nf">add</span><span class="p">()</span> <span class="p">{</span>
<span class="n">y</span> <span class="o">+=</span> <span class="mi">5</span>
<span class="p">}</span>
<span class="nf">add</span><span class="p">()</span>
<span class="k">return</span> <span class="n">y</span>
<span class="p">}</span>
<span class="nf">returnFifteen</span><span class="p">()</span></p>
<p><span class="c1">//: Functions are a first-class type. This means that a function can return another function as its value.</span>
<span class="c1">//:</span>
<span class="kd">func</span> <span class="nf">makeIncrementer</span><span class="p">()</span> <span class="o">-></span> <span class="p">(</span><span class="kt">Int</span> <span class="o">-></span> <span class="kt">Int</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">func</span> <span class="nf">addOne</span><span class="p">(</span><span class="nv">number</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="mi">1</span> <span class="o">+</span> <span class="n">number</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">addOne</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">increment</span> <span class="o">=</span> <span class="nf">makeIncrementer</span><span class="p">()</span>
<span class="nf">increment</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span></p>
<p><span class="c1">//: A function can take another function as one of its arguments.</span>
<span class="c1">//:</span>
<span class="kd">func</span> <span class="nf">hasAnyMatches</span><span class="p">(</span><span class="nv">list</span><span class="p">:</span> <span class="p">[</span><span class="kt">Int</span><span class="p">],</span> <span class="nv">condition</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">-></span> <span class="kt">Bool</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="n">item</span> <span class="k">in</span> <span class="n">list</span> <span class="p">{</span>
<span class="k">if</span> <span class="nf">condition</span><span class="p">(</span><span class="n">item</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">true</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">lessThanTen</span><span class="p">(</span><span class="nv">number</span><span class="p">:</span> <span class="kt">Int</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">number</span> <span class="o"><</span> <span class="mi">10</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">numbers</span> <span class="o">=</span> <span class="p">[</span><span class="mi">20</span><span class="p">,</span> <span class="mi">19</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">12</span><span class="p">]</span>
<span class="nf">hasAnyMatches</span><span class="p">(</span><span class="n">numbers</span><span class="p">,</span> <span class="nv">condition</span><span class="p">:</span> <span class="n">lessThanTen</span><span class="p">)</span></p>
<p><span class="c1">//: Functions are actually a special case of closures: blocks of code that can be called later. The code in a closure has access to things like variables and functions that were available in the scope where the closure was created, even if the closure is in a different scope when it is executed—you saw an example of this already with nested functions. You can write a closure without a name by surrounding code with braces (<code>{}</code>). Use <code>in</code> to separate the arguments and return type from the body.</span>
<span class="c1">//:</span>
<span class="n">numbers</span><span class="o">.</span><span class="nf">map</span><span class="p">({</span>
<span class="p">(</span><span class="nv">number</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="k">in</span>
<span class="k">let</span> <span class="nv">result</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">number</span>
<span class="k">return</span> <span class="n">result</span>
<span class="p">})</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Rewrite the closure to return zero for all odd numbers.</span>
<span class="c1">//:</span>
<span class="c1">//: You have several options for writing closures more concisely. When a closure’s type is already known, such as the callback for a delegate, you can omit the type of its parameters, its return type, or both. Single statement closures implicitly return the value of their only statement.</span>
<span class="c1">//:</span>
<span class="k">let</span> <span class="nv">mappedNumbers</span> <span class="o">=</span> <span class="n">numbers</span><span class="o">.</span><span class="nf">map</span><span class="p">({</span> <span class="n">number</span> <span class="k">in</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">number</span> <span class="p">})</span>
<span class="nf">print</span><span class="p">(</span><span class="n">mappedNumbers</span><span class="p">)</span></p>
<p><span class="c1">//: You can refer to parameters by number instead of by name—this approach is especially useful in very short closures. A closure passed as the last argument to a function can appear immediately after the parentheses. When a closure is the only argument to a function, you can omit the parentheses entirely.</span>
<span class="c1">//:</span>
<span class="k">let</span> <span class="nv">sortedNumbers</span> <span class="o">=</span> <span class="n">numbers</span><span class="o">.</span><span class="n">sort</span> <span class="p">{</span> <span class="nv">$0</span> <span class="o">></span> <span class="nv">$1</span> <span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="n">sortedNumbers</span><span class="p">)</span></p>
<p><span class="c1">//: <a href="@previous">Previous</a> | <a href="@next">Next</a></span></code></pre></figure></p>
The Swift Programming Language (Swift 2) -2 Control FLow2015-09-21T00:00:00+00:00http://jiaxianhua.github.io/swift/2015/09/21/the-swift-programming-language-swift-2-(2)-control-flow<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//: ## Control Flow</span>
<span class="c1">//:</span>
<span class="c1">//: Use <code>if</code> and <code>switch</code> to make conditionals, and use <code>for</code>-<code>in</code>, <code>for</code>, <code>while</code>, and <code>repeat</code>-<code>while</code> to make loops. Parentheses around the condition or loop variable are optional. Braces around the body are required.</span>
<span class="c1">//:</span>
<span class="k">let</span> <span class="nv">individualScores</span> <span class="o">=</span> <span class="p">[</span><span class="mi">75</span><span class="p">,</span> <span class="mi">43</span><span class="p">,</span> <span class="mi">103</span><span class="p">,</span> <span class="mi">87</span><span class="p">,</span> <span class="mi">12</span><span class="p">]</span>
<span class="k">var</span> <span class="nv">teamScore</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">score</span> <span class="k">in</span> <span class="n">individualScores</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">score</span> <span class="o">></span> <span class="mi">50</span> <span class="p">{</span>
<span class="n">teamScore</span> <span class="o">+=</span> <span class="mi">3</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">teamScore</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="n">teamScore</span><span class="p">)</span></p>
<p><span class="c1">//: In an <code>if</code> statement, the conditional must be a Boolean expression—this means that code such as <code>if score { ... }</code> is an error, not an implicit comparison to zero.</span>
<span class="c1">//:</span>
<span class="c1">//: You can use <code>if</code> and <code>let</code> together to work with values that might be missing. These values are represented as optionals. An optional value either contains a value or contains <code>nil</code> to indicate that a value is missing. Write a question mark (<code>?</code>) after the type of a value to mark the value as optional.</span>
<span class="c1">//:</span>
<span class="k">var</span> <span class="nv">optionalString</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="o">=</span> <span class="s">"Hello"</span>
<span class="nf">print</span><span class="p">(</span><span class="n">optionalString</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span></p>
<p><span class="k">var</span> <span class="nv">optionalName</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="o">=</span> <span class="s">"John Appleseed"</span>
<span class="k">var</span> <span class="nv">greeting</span> <span class="o">=</span> <span class="s">"Hello!"</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">name</span> <span class="o">=</span> <span class="n">optionalName</span> <span class="p">{</span>
<span class="n">greeting</span> <span class="o">=</span> <span class="s">"Hello, </span><span class="se">(</span><span class="n">name</span><span class="se">)</span><span class="s">"</span>
<span class="p">}</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Change <code>optionalName</code> to <code>nil</code>. What greeting do you get? Add an <code>else</code> clause that sets a different greeting if <code>optionalName</code> is <code>nil</code>.</span>
<span class="c1">//:</span>
<span class="c1">//: If the optional value is <code>nil</code>, the conditional is <code>false</code> and the code in braces is skipped. Otherwise, the optional value is unwrapped and assigned to the constant after <code>let</code>, which makes the unwrapped value available inside the block of code.</span>
<span class="c1">//:</span>
<span class="c1">//: Switches support any kind of data and a wide variety of comparison operations—they aren’t limited to integers and tests for equality.</span>
<span class="c1">//:</span>
<span class="k">let</span> <span class="nv">vegetable</span> <span class="o">=</span> <span class="s">"red pepper"</span>
<span class="k">switch</span> <span class="n">vegetable</span> <span class="p">{</span>
<span class="k">case</span> <span class="s">"celery"</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Add some raisins and make ants on a log."</span><span class="p">)</span>
<span class="k">case</span> <span class="s">"cucumber"</span><span class="p">,</span> <span class="s">"watercress"</span><span class="p">:</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"That would make a good tea sandwich."</span><span class="p">)</span>
<span class="k">case</span> <span class="k">let</span> <span class="nv">x</span> <span class="k">where</span> <span class="n">x</span><span class="o">.</span><span class="nf">hasSuffix</span><span class="p">(</span><span class="s">"pepper"</span><span class="p">):</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Is it a spicy </span><span class="se">(</span><span class="n">x</span><span class="se">)</span><span class="s">?"</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">"Everything tastes good in soup."</span><span class="p">)</span>
<span class="p">}</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Try removing the default case. What error do you get?</span>
<span class="c1">//:</span>
<span class="c1">//: Notice how <code>let</code> can be used in a pattern to assign the value that matched that part of a pattern to a constant.</span>
<span class="c1">//:</span>
<span class="c1">//: After executing the code inside the switch case that matched, the program exits from the switch statement. Execution doesn’t continue to the next case, so there is no need to explicitly break out of the switch at the end of each case’s code.</span>
<span class="c1">//:</span>
<span class="c1">//: You use <code>for</code>-<code>in</code> to iterate over items in a dictionary by providing a pair of names to use for each key-value pair. Dictionaries are an unordered collection, so their keys and values are iterated over in an arbitrary order.</span>
<span class="c1">//:</span>
<span class="k">let</span> <span class="nv">interestingNumbers</span> <span class="o">=</span> <span class="p">[</span>
<span class="s">"Prime"</span><span class="p">:</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">13</span><span class="p">],</span>
<span class="s">"Fibonacci"</span><span class="p">:</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="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">8</span><span class="p">],</span>
<span class="s">"Square"</span><span class="p">:</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">16</span><span class="p">,</span> <span class="mi">25</span><span class="p">],</span>
<span class="p">]</span>
<span class="k">var</span> <span class="nv">largest</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="p">(</span><span class="n">kind</span><span class="p">,</span> <span class="n">numbers</span><span class="p">)</span> <span class="k">in</span> <span class="n">interestingNumbers</span> <span class="p">{</span>
<span class="k">for</span> <span class="n">number</span> <span class="k">in</span> <span class="n">numbers</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">number</span> <span class="o">></span> <span class="n">largest</span> <span class="p">{</span>
<span class="n">largest</span> <span class="o">=</span> <span class="n">number</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="n">largest</span><span class="p">)</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Add another variable to keep track of which kind of number was the largest, as well as what that largest number was.</span>
<span class="c1">//:</span>
<span class="c1">//: Use <code>while</code> to repeat a block of code until a condition changes. The condition of a loop can be at the end instead, ensuring that the loop is run at least once.</span>
<span class="c1">//:</span>
<span class="k">var</span> <span class="nv">n</span> <span class="o">=</span> <span class="mi">2</span>
<span class="k">while</span> <span class="n">n</span> <span class="o"><</span> <span class="mi">100</span> <span class="p">{</span>
<span class="n">n</span> <span class="o">=</span> <span class="n">n</span> <span class="o">*</span> <span class="mi">2</span>
<span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="n">n</span><span class="p">)</span></p>
<p><span class="k">var</span> <span class="nv">m</span> <span class="o">=</span> <span class="mi">2</span>
<span class="k">repeat</span> <span class="p">{</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">m</span> <span class="o">*</span> <span class="mi">2</span>
<span class="p">}</span> <span class="k">while</span> <span class="n">m</span> <span class="o"><</span> <span class="mi">100</span>
<span class="nf">print</span><span class="p">(</span><span class="n">m</span><span class="p">)</span></p>
<p><span class="c1">//: You can keep an index in a loop—either by using <code>..&lt;</code> to make a range of indexes or by writing an explicit initialization, condition, and increment. These two loops do the same thing:</span>
<span class="c1">//:</span>
<span class="k">var</span> <span class="nv">firstForLoop</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..<</span><span class="mi">4</span> <span class="p">{</span>
<span class="n">firstForLoop</span> <span class="o">+=</span> <span class="n">i</span>
<span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="n">firstForLoop</span><span class="p">)</span></p>
<p><span class="k">var</span> <span class="nv">secondForLoop</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="k">var</span> <span class="nv">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">4</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span> <span class="p">{</span>
<span class="n">secondForLoop</span> <span class="o">+=</span> <span class="n">i</span>
<span class="p">}</span>
<span class="nf">print</span><span class="p">(</span><span class="n">secondForLoop</span><span class="p">)</span></p>
<p><span class="c1">//: Use <code>..&lt;</code> to make a range that omits its upper value, and use <code>...</code> to make a range that includes both values.</span>
<span class="c1">//:</span></p>
<p><span class="c1">//: <a href="@previous">Previous</a> | <a href="@next">Next</a></span></code></pre></figure></p>
The Swift Programming Language (Swift 2) -1 Simple Values2015-09-21T00:00:00+00:00http://jiaxianhua.github.io/swift/2015/09/21/the-swift-programming-language-swift-2-(1)-simple-values<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//: # A Swift Tour</span>
<span class="c1">//:</span>
<span class="c1">//: Tradition suggests that the first program in a new language should print the words “Hello, world!” on the screen. In Swift, this can be done in a single line:</span>
<span class="c1">//:</span>
<span class="nf">print</span><span class="p">(</span><span class="s">"Hello, world!"</span><span class="p">)</span></p>
<p><span class="c1">//: If you have written code in C or Objective-C, this syntax looks familiar to you—in Swift, this line of code is a complete program. You don’t need to import a separate library for functionality like input/output or string handling. Code written at global scope is used as the entry point for the program, so you don’t need a <code>main()</code> function. You also don’t need to write semicolons at the end of every statement.</span>
<span class="c1">//:</span>
<span class="c1">//: This tour gives you enough information to start writing code in Swift by showing you how to accomplish a variety of programming tasks. Don’t worry if you don’t understand something—everything introduced in this tour is explained in detail in the rest of this book.</span>
<span class="c1">//:</span>
<span class="c1">//: ## Simple Values</span>
<span class="c1">//:</span>
<span class="c1">//: Use <code>let</code> to make a constant and <code>var</code> to make a variable. The value of a constant doesn’t need to be known at compile time, but you must assign it a value exactly once. This means you can use constants to name a value that you determine once but use in many places.</span>
<span class="c1">//:</span>
<span class="k">var</span> <span class="nv">myVariable</span> <span class="o">=</span> <span class="mi">42</span>
<span class="n">myVariable</span> <span class="o">=</span> <span class="mi">50</span>
<span class="k">let</span> <span class="nv">myConstant</span> <span class="o">=</span> <span class="mi">42</span></p>
<p><span class="c1">//: A constant or variable must have the same type as the value you want to assign to it. However, you don’t always have to write the type explicitly. Providing a value when you create a constant or variable lets the compiler infer its type. In the example above, the compiler infers that <code>myVariable</code> is an integer because its initial value is an integer.</span>
<span class="c1">//:</span>
<span class="c1">//: If the initial value doesn’t provide enough information (or if there is no initial value), specify the type by writing it after the variable, separated by a colon.</span>
<span class="c1">//:</span>
<span class="k">let</span> <span class="nv">implicitInteger</span> <span class="o">=</span> <span class="mi">70</span>
<span class="k">let</span> <span class="nv">implicitDouble</span> <span class="o">=</span> <span class="mf">70.0</span>
<span class="k">let</span> <span class="nv">explicitDouble</span><span class="p">:</span> <span class="kt">Double</span> <span class="o">=</span> <span class="mi">70</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Create a constant with an explicit type of <code>Float</code> and a value of <code>4</code>.</span>
<span class="c1">//:</span>
<span class="c1">//: Values are never implicitly converted to another type. If you need to convert a value to a different type, explicitly make an instance of the desired type.</span>
<span class="c1">//:</span>
<span class="k">let</span> <span class="nv">label</span> <span class="o">=</span> <span class="s">"The width is "</span>
<span class="k">let</span> <span class="nv">width</span> <span class="o">=</span> <span class="mi">94</span>
<span class="k">let</span> <span class="nv">widthLabel</span> <span class="o">=</span> <span class="n">label</span> <span class="o">+</span> <span class="kt">String</span><span class="p">(</span><span class="n">width</span><span class="p">)</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Try removing the conversion to <code>String</code> from the last line. What error do you get?</span>
<span class="c1">//:</span>
<span class="c1">//: There’s an even simpler way to include values in strings: Write the value in parentheses, and write a backslash (<code>\</code>) before the parentheses. For example:</span>
<span class="c1">//:</span>
<span class="k">let</span> <span class="nv">apples</span> <span class="o">=</span> <span class="mi">3</span>
<span class="k">let</span> <span class="nv">oranges</span> <span class="o">=</span> <span class="mi">5</span>
<span class="k">let</span> <span class="nv">appleSummary</span> <span class="o">=</span> <span class="s">"I have </span><span class="se">(</span><span class="n">apples</span><span class="se">)</span><span class="s"> apples."</span>
<span class="k">let</span> <span class="nv">fruitSummary</span> <span class="o">=</span> <span class="s">"I have </span><span class="se">(</span><span class="n">apples</span> <span class="o">+</span> <span class="n">oranges</span><span class="se">)</span><span class="s"> pieces of fruit."</span></p>
<p><span class="c1">//: > <strong>Experiment</strong>:</span>
<span class="c1">//: > Use <code>\()</code> to include a floating-point calculation in a string and to include someone’s name in a greeting.</span>
<span class="c1">//:</span>
<span class="c1">//: Create arrays and dictionaries using brackets (<code>[]</code>), and access their elements by writing the index or key in brackets. A comma is allowed after the last element.</span>
<span class="c1">//:</span>
<span class="k">var</span> <span class="nv">shoppingList</span> <span class="o">=</span> <span class="p">[</span><span class="s">"catfish"</span><span class="p">,</span> <span class="s">"water"</span><span class="p">,</span> <span class="s">"tulips"</span><span class="p">,</span> <span class="s">"blue paint"</span><span class="p">]</span>
<span class="n">shoppingList</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="s">"bottle of water"</span></p>
<p><span class="k">var</span> <span class="nv">occupations</span> <span class="o">=</span> <span class="p">[</span>
<span class="s">"Malcolm"</span><span class="p">:</span> <span class="s">"Captain"</span><span class="p">,</span>
<span class="s">"Kaylee"</span><span class="p">:</span> <span class="s">"Mechanic"</span><span class="p">,</span>
<span class="p">]</span>
<span class="n">occupations</span><span class="p">[</span><span class="s">"Jayne"</span><span class="p">]</span> <span class="o">=</span> <span class="s">"Public Relations"</span></p>
<p><span class="c1">//: To create an empty array or dictionary, use the initializer syntax.</span>
<span class="c1">//:</span>
<span class="k">let</span> <span class="nv">emptyArray</span> <span class="o">=</span> <span class="p"><a href=""></span><span class="kt">String</span><span class="p"></a></span>
<span class="k">let</span> <span class="nv">emptyDictionary</span> <span class="o">=</span> <span class="p"><a href=""></span><span class="kt">String</span><span class="p">:</span> <span class="kt">Float</span><span class="p"></a></span></p>
<p><span class="c1">//: If type information can be inferred, you can write an empty array as <code>[]</code> and an empty dictionary as <code>[:]</code>—for example, when you set a new value for a variable or pass an argument to a function.</span>
<span class="c1">//:</span>
<span class="n">shoppingList</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">occupations</span> <span class="o">=</span> <span class="p">[:]</span></p>
<p><span class="c1">//: See <a href="License">License</a> for this sample's licensing information.</span>
<span class="c1">//: </span>
<span class="c1">//: <a href="@next">Next</a></span></code></pre></figure></p>
iOS 开发日志2015-09-14T00:00:00+00:00http://jiaxianhua.github.io/podcast/2015/09/14/ios-dev-log<h2>iOS 开发日志</h2>
<hr />
<p>历经九九八十一期的磨难,我的播客 《iOS 开发日志》 终于成功登上了 苹果的 Podcast。</p>
<p><a href="https://itunes.apple.com/cn/podcast/ios-kai-fa-ri-zhi/id1039229999?mt=2">https://itunes.apple.com/cn/podcast/ios-kai-fa-ri-zhi/id1039229999?mt=2</a></p>
<p>喜欢的朋友欢迎订阅噢!</p>
<p>gihub仓库地址:</p>
<p><a href="https://github.com/jiaxianhua/iOSDevLog">https://github.com/jiaxianhua/iOSDevLog</a></p>
synthesia2015-09-07T00:00:00+00:00http://jiaxianhua.github.io/synthesia/2015/09/07/synthesia<h2>synthesia</h2>
<hr />
<p><a href="http://www.synthesiagame.com/">http://www.synthesiagame.com/</a></p>
<p><a href="http://www.synthesiagame.com/download">http://www.synthesiagame.com/download</a></p>
<p>midi</p>
<p><a href="http://synthesia.ys168.com">http://synthesia.ys168.com</a></p>
<h2>install linux</h2>
<hr />
<p>VMware Fusion</p>
<p>ubuntu meta: <a href="http://ubuntu-mate.org/vivid/">http://ubuntu-mate.org/vivid/</a></p>
<blockquote><p>Mac (PowerPC) and IBM-PPC (POWER5) : NGr. not recognized.</p>
<p>ubuntu-mate-15.04-desktop-amd64.iso : OK.</p></blockquote>
<h2>Install VMware Tools</h2>
<hr />
<ol>
<li>Software & Update</li>
<li>Ubuntu Software</li>
<li>Download from : http://mirrors.aliyun.com/ubuntu</li>
<li>Close</li>
<li>Reload</li>
</ol>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>sudo apt-get install build-essential
<span class="gp">$ </span>copy /media/<span class="k">${</span><span class="nv">user</span><span class="k">}</span>/VMWare<span class="se">\ </span>Tools /tmp/
<span class="gp">$ </span>sudo tar xzvf <span class="k">*</span>.tar.gz
<span class="gp">$ </span><span class="nb">cd </span>vmware-tools-distrib
<span class="gp">$ </span>sudo ./vmware-install.pl
... Would you like to <span class="nb">enable </span>VMware automatic kernel modules?[no] yes</code></pre></figure></p>
<p>restart x or compulater</p>
<h2>install synthesia</h2>
<hr />
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>mkdir github
<span class="gp">$ </span><span class="nb">cd</span> !<span class="err">$</span>
<span class="gp">$ </span>sudo apt-get install git
<span class="gp">$ </span>git clone https://github.com/linthesia/linthesia.git
<span class="gp">$ </span><span class="nb">cd </span>linthesia
<span class="gp">$ </span>cat README
<span class="gp">$ </span>cat BUILD-DEPENDS</p>
<p>libgtkmm-2.4-dev
libgconfmm-2.6-dev
libgtkglextmm-x11-1.2-dev
libasound2-dev</p>
<p><span class="gp">$ </span>sudo apt-get install <span class="sb"><code></span>cat BUILD-DEPENDS<span class="sb"></code></span>
<span class="gp">$ </span>sudo apt-get install autoconf
<span class="gp">$ </span>autoreconf -ivf
<span class="gp">$ </span>sudo apt-get install libtool
<span class="gp">$ </span>autoreconf -ivf</p>
<p><span class="gp">$ </span>mkdir build
<span class="gp">$ </span><span class="nb">cd </span>build <span class="c"># Isolate compilation to speed future compilations</span>
<span class="gp">$ </span>../configure</p>
<p><span class="gp">$ </span>make <span class="o">&&</span> sudo make install</code></pre></figure></p>
<h2>use linthesia</h2>
<ol>
<li>$ linthesia</li>
<li>select "*.mid" from "/use/local/share/linthesia/music/Popular/Gymnopedia.mid"</li>
<li>download <em>.mid, </em>.midi</li>
</ol>
<h2>other</h2>
<hr />
<p>No voice ? e ?</p>
个人常用软件及配置2015-09-05T00:00:00+00:00http://jiaxianhua.github.io/2015/09/05/software-and-configuration<p>OS X Software</p>
<h2>Xcode</h2>
<hr />
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>xcode-select --print-path
<span class="gp">$ </span>sudo xcode-select -switch /Developer/Applications/Xcode-Beta.app
<span class="gp">$ </span>xcode-select --print-path</code></pre></figure></p>
<h2>iterm2</h2>
<p><a href="http://www.iterm2.com">http://www.iterm2.com</a></p>
<h2>on-my-zsh</h2>
<hr />
<p><a href="http://ohmyz.sh">http://ohmyz.sh</a></p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">sh -c <span class="s2">"</span><span class="k">$(</span>curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh<span class="k">)</span><span class="s2">"</span></code></pre></figure></p>
<h2>Homebrew</h2>
<hr />
<p><a href="http://brew.sh">http://brew.sh</a></p>
<p>Install Homebrew</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">ruby -e <span class="s2">"</span><span class="k">$(</span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install<span class="k">)</span><span class="s2">"</span></code></pre></figure></p>
<h2>ruby version manager</h2>
<hr />
<p><a href="http://www.rvm.io">http://www.rvm.io</a></p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$curl</span> -sSL https://get.rvm.io | bash -s stable</code></pre></figure></p>
<h2>替换系统 vim</h2>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>brew install vim
<span class="gp">$ </span>vim .zshrc</p>
<p><span class="nv">$PATH</span><span class="o">=</span>/usr/local/Cellar/vim/7.4.826/bin:<span class="nv">$PATH</span></code></pre></figure></p>
<h2>vim 配置</h2>
<hr />
<p>github地址:<a href="https://github.com/spf13/spf13-vim">https://github.com/spf13/spf13-vim</a></p>
<h2>其它</h2>
<p><img src="/assets/img/ios/software/1.png" alt="1" /></p>
<p><img src="/assets/img/ios/software/2.png" alt="2" /></p>
iOS Dev Log2015-09-01T00:00:00+00:00http://jiaxianhua.github.io/iosdevlog/2015/09/01/ios-dev-log<h1>iOS Dev Log</h1>
<hr />
<h2><em>Version Control</em></h2>
<hr />
<h2><strong>git</strong></h2>
<p><a href="http://www.git-scm.com/">http://www.git-scm.com/</a></p>
<p><a href="https://github.com">https://github.com</a></p>
<h2><strong>SourceTree</strong></h2>
<p><a href="https://www.sourcetreeapp.com/">https://www.sourcetreeapp.com/</a></p>
<h2><em>.gitignore</em></h2>
<p><a href="https://www.gitignore.io">https://www.gitignore.io</a></p>
<p><a href="https://www.gitignore.io/api/code,objective-c,swift,osx">https://www.gitignore.io/api/code,objective-c,swift,osx</a></p>
<h2>Social Networking</h2>
<hr />
<h2><em>Weibo</em></h2>
<p><strong>@gnuhua</strong></p>
<p><a href="http://weibo.com/iOSDevLog">http://weibo.com/iOSDevLog</a></p>
<h2><em>WeChat Official Accounts</em></h2>
<p><strong>iOSDevLog</strong></p>
<p><img src="/assets/img/qrcode_430.jpg" alt="qrcode" /></p>
<h2><em>Blog</em></h2>
<p><a href="http://jiaxh.com">http://jiaxh.com</a></p>
<hr />
haskell 20482015-08-15T00:00:00+00:00http://jiaxianhua.github.io/haskell/2015/08/15/haskell-5<h2>英文原文:<a href="http://gregorulm.com/2048-in-90-lines-haskell/">Implementing the game 2048 in less than 90 lines of Haskell</a></h2>
<h2>译文地址:<a href="http://www.oschina.net/translate/2048-in-90-lines-haskell"> 用 90 行 Haskell 代码实现 2048 游戏</a></h2>
<hr />
<p>源码地址:
<a href="https://raw.githubusercontent.com/gregorulm/h2048/master/h2048.hs">https://raw.githubusercontent.com/gregorulm/h2048/master/h2048.hs</a></p>
<p><figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="cm">{-</p>
<p>h2048
A Haskell implementation of 2048.</p>
<p>Gregor Ulm</p>
<p>last update:
2014-06-18</p>
<p>Please consult the file README for further information
on this program.</p>
<p>-}</span></p>
<p><span class="kr">import</span> <span class="nn">Prelude</span> <span class="k">hiding</span> <span class="p">(</span><span class="kt">Left</span><span class="p">,</span> <span class="kt">Right</span><span class="p">)</span>
<span class="kr">import</span> <span class="nn">Data.Char</span> <span class="p">(</span><span class="nf">toLower</span><span class="p">)</span>
<span class="kr">import</span> <span class="nn">Data.List</span>
<span class="kr">import</span> <span class="nn">System.IO</span>
<span class="kr">import</span> <span class="nn">System.Random</span>
<span class="kr">import</span> <span class="nn">Text.Printf</span></p>
<p><span class="kr">data</span> <span class="kt">Move</span> <span class="o">=</span> <span class="kt">Up</span> <span class="o">|</span> <span class="kt">Down</span> <span class="o">|</span> <span class="kt">Left</span> <span class="o">|</span> <span class="kt">Right</span>
<span class="kr">type</span> <span class="kt">Grid</span> <span class="o">=</span> <span class="p">[[</span><span class="kt">Int</span><span class="p">]]</span></p>
<p><span class="n">start</span> <span class="o">::</span> <span class="kt">IO</span> <span class="kt">Grid</span>
<span class="n">start</span> <span class="o">=</span> <span class="kr">do</span> <span class="n">grid'</span> <span class="o"><-</span> <span class="n">addTile</span> <span class="o">$</span> <span class="n">replicate</span> <span class="mi">4</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="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span>
<span class="n">addTile</span> <span class="n">grid'</span></p>
<p><span class="n">merge</span> <span class="o">::</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">Int</span><span class="p">]</span>
<span class="n">merge</span> <span class="n">xs</span> <span class="o">=</span> <span class="n">merged</span> <span class="o">++</span> <span class="n">padding</span>
<span class="kr">where</span> <span class="n">padding</span> <span class="o">=</span> <span class="n">replicate</span> <span class="p">(</span><span class="n">length</span> <span class="n">xs</span> <span class="o">-</span> <span class="n">length</span> <span class="n">merged</span><span class="p">)</span> <span class="mi">0</span>
<span class="n">merged</span> <span class="o">=</span> <span class="n">combine</span> <span class="o">$</span> <span class="n">filter</span> <span class="p">(</span><span class="o">/=</span> <span class="mi">0</span><span class="p">)</span> <span class="n">xs</span>
<span class="n">combine</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">y</span><span class="o">:</span><span class="n">xs</span><span class="p">)</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">x</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">:</span> <span class="n">combine</span> <span class="n">xs</span>
<span class="o">|</span> <span class="n">otherwise</span> <span class="o">=</span> <span class="n">x</span> <span class="o">:</span> <span class="n">combine</span> <span class="p">(</span><span class="n">y</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span>
<span class="n">combine</span> <span class="n">x</span> <span class="o">=</span> <span class="n">x</span></p>
<p><span class="n">move</span> <span class="o">::</span> <span class="kt">Move</span> <span class="o">-></span> <span class="kt">Grid</span> <span class="o">-></span> <span class="kt">Grid</span>
<span class="n">move</span> <span class="kt">Left</span> <span class="o">=</span> <span class="n">map</span> <span class="n">merge</span>
<span class="n">move</span> <span class="kt">Right</span> <span class="o">=</span> <span class="n">map</span> <span class="p">(</span><span class="n">reverse</span> <span class="o">.</span> <span class="n">merge</span> <span class="o">.</span> <span class="n">reverse</span><span class="p">)</span>
<span class="n">move</span> <span class="kt">Up</span> <span class="o">=</span> <span class="n">transpose</span> <span class="o">.</span> <span class="n">move</span> <span class="kt">Left</span> <span class="o">.</span> <span class="n">transpose</span>
<span class="n">move</span> <span class="kt">Down</span> <span class="o">=</span> <span class="n">transpose</span> <span class="o">.</span> <span class="n">move</span> <span class="kt">Right</span> <span class="o">.</span> <span class="n">transpose</span></p>
<p><span class="n">getZeroes</span> <span class="o">::</span> <span class="kt">Grid</span> <span class="o">-></span> <span class="p">[(</span><span class="kt">Int</span><span class="p">,</span> <span class="kt">Int</span><span class="p">)]</span>
<span class="n">getZeroes</span> <span class="n">grid</span> <span class="o">=</span> <span class="n">filter</span> <span class="p">(</span><span class="nf">\</span><span class="p">(</span><span class="n">row</span><span class="p">,</span> <span class="n">col</span><span class="p">)</span> <span class="o">-></span> <span class="p">(</span><span class="n">grid</span><span class="o">!!</span><span class="n">row</span><span class="p">)</span><span class="o">!!</span><span class="n">col</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="n">coordinates</span>
<span class="kr">where</span> <span class="n">singleRow</span> <span class="n">n</span> <span class="o">=</span> <span class="n">zip</span> <span class="p">(</span><span class="n">replicate</span> <span class="mi">4</span> <span class="n">n</span><span class="p">)</span> <span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">3</span><span class="p">]</span>
<span class="n">coordinates</span> <span class="o">=</span> <span class="n">concatMap</span> <span class="n">singleRow</span> <span class="p">[</span><span class="mi">0</span><span class="o">..</span><span class="mi">3</span><span class="p">]</span></p>
<p><span class="n">setSquare</span> <span class="o">::</span> <span class="kt">Grid</span> <span class="o">-></span> <span class="p">(</span><span class="kt">Int</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="o">-></span> <span class="kt">Grid</span>
<span class="n">setSquare</span> <span class="n">grid</span> <span class="p">(</span><span class="n">row</span><span class="p">,</span> <span class="n">col</span><span class="p">)</span> <span class="n">val</span> <span class="o">=</span> <span class="n">pre</span> <span class="o">++</span> <span class="p">[</span><span class="n">mid</span><span class="p">]</span> <span class="o">++</span> <span class="n">post</span>
<span class="kr">where</span> <span class="n">pre</span> <span class="o">=</span> <span class="n">take</span> <span class="n">row</span> <span class="n">grid</span>
<span class="n">mid</span> <span class="o">=</span> <span class="n">take</span> <span class="n">col</span> <span class="p">(</span><span class="n">grid</span><span class="o">!!</span><span class="n">row</span><span class="p">)</span> <span class="o">++</span> <span class="p">[</span><span class="n">val</span><span class="p">]</span> <span class="o">++</span> <span class="n">drop</span> <span class="p">(</span><span class="n">col</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">(</span><span class="n">grid</span><span class="o">!!</span><span class="n">row</span><span class="p">)</span>
<span class="n">post</span> <span class="o">=</span> <span class="n">drop</span> <span class="p">(</span><span class="n">row</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="n">grid</span></p>
<p><span class="n">isMoveLeft</span> <span class="o">::</span> <span class="kt">Grid</span> <span class="o">-></span> <span class="kt">Bool</span>
<span class="n">isMoveLeft</span> <span class="n">grid</span> <span class="o">=</span> <span class="n">sum</span> <span class="n">allChoices</span> <span class="o">></span> <span class="mi">0</span>
<span class="kr">where</span> <span class="n">allChoices</span> <span class="o">=</span> <span class="n">map</span> <span class="p">(</span><span class="n">length</span> <span class="o">.</span> <span class="n">getZeroes</span> <span class="o">.</span> <span class="n">flip</span> <span class="n">move</span> <span class="n">grid</span><span class="p">)</span> <span class="n">directions</span>
<span class="n">directions</span> <span class="o">=</span> <span class="p">[</span><span class="kt">Left</span><span class="p">,</span> <span class="kt">Right</span><span class="p">,</span> <span class="kt">Up</span><span class="p">,</span> <span class="kt">Down</span><span class="p">]</span></p>
<p><span class="n">printGrid</span> <span class="o">::</span> <span class="kt">Grid</span> <span class="o">-></span> <span class="kt">IO</span> <span class="nb">()</span>
<span class="n">printGrid</span> <span class="n">grid</span> <span class="o">=</span> <span class="kr">do</span>
<span class="n">putStr</span> <span class="s">"</span><span class="se">\ESC</span><span class="s">[2J</span><span class="se">\ESC</span><span class="s">[2J</span><span class="se">\n</span><span class="s">"</span> <span class="c1">-- clears the screen</span>
<span class="n">mapM_</span> <span class="p">(</span><span class="n">putStrLn</span> <span class="o">.</span> <span class="n">showRow</span><span class="p">)</span> <span class="n">grid</span></p>
<p><span class="n">showRow</span> <span class="o">::</span> <span class="p">[</span><span class="kt">Int</span><span class="p">]</span> <span class="o">-></span> <span class="kt">String</span>
<span class="n">showRow</span> <span class="o">=</span> <span class="n">concatMap</span> <span class="p">(</span><span class="n">printf</span> <span class="s">"%5d"</span><span class="p">)</span></p>
<p><span class="n">moves</span> <span class="o">::</span> <span class="p">[(</span><span class="kt">Char</span><span class="p">,</span> <span class="kt">Move</span><span class="p">)]</span>
<span class="n">moves</span> <span class="o">=</span> <span class="n">keys</span> <span class="s">"wasd"</span> <span class="o">++</span> <span class="n">keys</span> <span class="s">"chtn"</span>
<span class="kr">where</span> <span class="n">keys</span> <span class="n">chars</span> <span class="o">=</span> <span class="n">zip</span> <span class="n">chars</span> <span class="p">[</span><span class="kt">Up</span><span class="p">,</span> <span class="kt">Left</span><span class="p">,</span> <span class="kt">Down</span><span class="p">,</span> <span class="kt">Right</span><span class="p">]</span></p>
<p><span class="n">captureMove</span> <span class="o">::</span> <span class="kt">IO</span> <span class="kt">Move</span>
<span class="n">captureMove</span> <span class="o">=</span> <span class="kr">do</span>
<span class="n">inp</span> <span class="o"><-</span> <span class="n">getChar</span>
<span class="kr">case</span> <span class="n">lookup</span> <span class="p">(</span><span class="n">toLower</span> <span class="n">inp</span><span class="p">)</span> <span class="n">moves</span> <span class="kr">of</span>
<span class="kt">Just</span> <span class="n">x</span> <span class="o">-></span> <span class="n">return</span> <span class="n">x</span>
<span class="kt">Nothing</span> <span class="o">-></span> <span class="kr">do</span> <span class="n">putStrLn</span> <span class="s">"Use WASD or CHTN as input"</span>
<span class="n">captureMove</span></p>
<p><span class="n">check2048</span> <span class="o">::</span> <span class="kt">Grid</span> <span class="o">-></span> <span class="kt">Bool</span>
<span class="n">check2048</span> <span class="n">grid</span> <span class="o">=</span> <span class="kt">[]</span> <span class="o">/=</span> <span class="n">filter</span> <span class="p">(</span><span class="o">==</span> <span class="mi">2048</span><span class="p">)</span> <span class="p">(</span><span class="n">concat</span> <span class="n">grid</span><span class="p">)</span></p>
<p><span class="n">addTile</span> <span class="o">::</span> <span class="kt">Grid</span> <span class="o">-></span> <span class="kt">IO</span> <span class="kt">Grid</span>
<span class="n">addTile</span> <span class="n">grid</span> <span class="o">=</span> <span class="kr">do</span>
<span class="kr">let</span> <span class="n">candidates</span> <span class="o">=</span> <span class="n">getZeroes</span> <span class="n">grid</span>
<span class="n">pick</span> <span class="o"><-</span> <span class="n">choose</span> <span class="n">candidates</span>
<span class="n">val</span> <span class="o"><-</span> <span class="n">choose</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">4</span><span class="p">]</span>
<span class="kr">let</span> <span class="n">new_grid</span> <span class="o">=</span> <span class="n">setSquare</span> <span class="n">grid</span> <span class="n">pick</span> <span class="n">val</span>
<span class="n">return</span> <span class="n">new_grid</span></p>
<p><span class="n">choose</span> <span class="o">::</span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-></span> <span class="kt">IO</span> <span class="n">a</span>
<span class="n">choose</span> <span class="n">xs</span> <span class="o">=</span> <span class="kr">do</span>
<span class="n">i</span> <span class="o"><-</span> <span class="n">randomRIO</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">length</span> <span class="n">xs</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">return</span> <span class="p">(</span><span class="n">xs</span> <span class="o">!!</span> <span class="n">i</span><span class="p">)</span></p>
<p><span class="n">newGrid</span> <span class="o">::</span> <span class="kt">Grid</span> <span class="o">-></span> <span class="kt">IO</span> <span class="kt">Grid</span>
<span class="n">newGrid</span> <span class="n">grid</span> <span class="o">=</span> <span class="kr">do</span>
<span class="n">m</span> <span class="o"><-</span> <span class="n">captureMove</span>
<span class="kr">let</span> <span class="n">new_grid</span> <span class="o">=</span> <span class="n">move</span> <span class="n">m</span> <span class="n">grid</span>
<span class="n">return</span> <span class="n">new_grid</span></p>
<p><span class="n">gameLoop</span> <span class="o">::</span> <span class="kt">Grid</span> <span class="o">-></span> <span class="kt">IO</span> <span class="nb">()</span>
<span class="n">gameLoop</span> <span class="n">grid</span>
<span class="o">|</span> <span class="n">isMoveLeft</span> <span class="n">grid</span> <span class="o">=</span> <span class="kr">do</span>
<span class="n">printGrid</span> <span class="n">grid</span>
<span class="kr">if</span> <span class="n">check2048</span> <span class="n">grid</span>
<span class="kr">then</span> <span class="n">putStrLn</span> <span class="s">"You won!"</span>
<span class="kr">else</span> <span class="kr">do</span> <span class="n">new_grid</span> <span class="o"><-</span> <span class="n">newGrid</span> <span class="n">grid</span>
<span class="kr">if</span> <span class="n">grid</span> <span class="o">/=</span> <span class="n">new_grid</span>
<span class="kr">then</span> <span class="kr">do</span> <span class="n">new</span> <span class="o"><-</span> <span class="n">addTile</span> <span class="n">new_grid</span>
<span class="n">gameLoop</span> <span class="n">new</span>
<span class="kr">else</span> <span class="n">gameLoop</span> <span class="n">grid</span>
<span class="o">|</span> <span class="n">otherwise</span> <span class="o">=</span> <span class="kr">do</span>
<span class="n">printGrid</span> <span class="n">grid</span>
<span class="n">putStrLn</span> <span class="s">"Game over"</span></p>
<p><span class="n">main</span> <span class="o">::</span> <span class="kt">IO</span> <span class="nb">()</span>
<span class="n">main</span> <span class="o">=</span> <span class="kr">do</span>
<span class="n">hSetBuffering</span> <span class="n">stdin</span> <span class="kt">NoBuffering</span>
<span class="n">grid</span> <span class="o"><-</span> <span class="n">start</span>
<span class="n">gameLoop</span> <span class="n">grid</span></code></pre></figure></p>
haskell quicksort2015-08-14T00:00:00+00:00http://jiaxianhua.github.io/haskell/2015/08/14/haskell-4<p><figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">quicksort</span> <span class="o">::</span> <span class="p">(</span><span class="kt">Ord</span> <span class="n">a</span><span class="p">)</span> <span class="o">=></span> <span class="p">[</span><span class="n">a</span><span class="p">]</span> <span class="o">-></span> <span class="p">[</span><span class="n">a</span><span class="p">]</span>
<span class="n">quicksort</span> <span class="kt">[]</span> <span class="o">=</span> <span class="kt">[]</span>
<span class="n">quicksort</span> <span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="o">=</span>
<span class="kr">let</span> <span class="n">smallerOrEqual</span> <span class="o">=</span> <span class="p">[</span><span class="n">a</span> <span class="o">|</span> <span class="n">a</span> <span class="o"><-</span> <span class="n">xs</span><span class="p">,</span> <span class="n">a</span> <span class="o"><=</span> <span class="n">x</span><span class="p">]</span>
<span class="n">larger</span> <span class="o">=</span> <span class="p">[</span><span class="n">a</span> <span class="o">|</span> <span class="n">a</span> <span class="o"><-</span> <span class="n">xs</span><span class="p">,</span> <span class="n">a</span> <span class="o">></span> <span class="n">x</span><span class="p">]</span>
<span class="kr">in</span> <span class="n">quicksort</span> <span class="n">smallerOrEqual</span> <span class="o">++</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span> <span class="o">++</span> <span class="n">quicksort</span> <span class="n">larger</span></code></pre></figure></p>
<p><img src="/assets/img/haskell/1.png" alt="1" /></p>
<p>The above diagram illustrates how quicksort works on our example. When we want to sort [5,1,9,4,6,7,3], we decide that the first element is our pivot. Then we sandwich it in between [1,4,3] and [9,6,7]. Once we’ve done that, we sort [1,4,3] and [9,6,7] by using the same approach.</p>
<p>To sort [1,4,3], we choose the first element, 1, as the pivot and we make a list of elements that are less than or equal to 1. That turns out to be the empty list, [], because 1 is the smallest element in [1,4,3]. The elements larger than 1 go to its right, so that’s [4,3]. Again, [4,3] is sorted in the same way. It too will eventually be broken up into empty lists and put back together.</p>
<p>The algorithm then returns to the right side of 1, which has the empty list on its left side. Suddenly, we have [1,3,4], which is sorted. This is kept on the left side of the 5.</p>
<p>Once the elements on the right side of the 5 are sorted in the same way, we will have a completely sorted list: [1,3,4,5,6,7,9].</p>
haskell ghci2015-08-13T00:00:00+00:00http://jiaxianhua.github.io/haskell/2015/08/13/haskell-3<p>First, let’s start GHC’s interactive mode and call some functions, so we can get a very basic feel for Haskell. Open a terminal and type ghci. You will be greeted with something like this:</p>
<p><figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="kt">GHCi</span><span class="p">,</span> <span class="n">version</span> <span class="mf">6.12</span><span class="o">.</span><span class="mi">3</span><span class="o">:</span> <span class="n">http</span><span class="o">://</span><span class="n">www</span><span class="o">.</span><span class="n">haskell</span><span class="o">.</span><span class="n">org</span><span class="o">/</span><span class="n">ghc</span><span class="o">/</span> <span class="o">:?</span> <span class="n">for</span> <span class="n">help</span>
<span class="kt">Loading</span> <span class="n">package</span> <span class="n">ghc</span><span class="o">-</span><span class="n">prim</span> <span class="o">...</span> <span class="n">linking</span> <span class="o">...</span> <span class="n">done</span><span class="o">.</span>
<span class="kt">Loading</span> <span class="n">package</span> <span class="n">integer</span><span class="o">-</span><span class="n">gmp</span> <span class="o">...</span> <span class="n">linking</span> <span class="o">...</span> <span class="n">done</span><span class="o">.</span>
<span class="kt">Loading</span> <span class="n">package</span> <span class="n">base</span> <span class="o">...</span> <span class="n">linking</span> <span class="o">...</span> <span class="n">done</span><span class="o">.</span>
<span class="kt">Loading</span> <span class="n">package</span> <span class="n">ffi</span><span class="o">-</span><span class="mf">1.0</span> <span class="o">...</span> <span class="n">linking</span> <span class="o">...</span> <span class="n">done</span><span class="o">.</span></code></pre></figure></p>
<blockquote><p>NOTE GHCi’s default prompt is Prelude>, but we’ll be using ghci> as our prompt for the ex- amples in this book. To make your prompt match the book’s, enter <strong>:set prompt "ghci> "</strong> into GHCi. If you don’t want to do this every time you run GHCi, create a file called .ghci in your home folder and set its contents to <strong>:set prompt "ghci> "</strong>.</p></blockquote>
<p>Congratulations, you’re in GHCi! Now let’s try some simple arithmetic:</p>
<p><figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">ghci</span><span class="o">></span> <span class="mi">2</span> <span class="o">+</span> <span class="mi">15</span>
<span class="mi">17</span>
<span class="n">ghci</span><span class="o">></span> <span class="mi">49</span> <span class="o">*</span> <span class="mi">100</span>
<span class="mi">4900</span>
<span class="n">ghci</span><span class="o">></span> <span class="mi">1892</span> <span class="o">-</span> <span class="mi">1472</span>
<span class="mi">420</span>
<span class="n">ghci</span><span class="o">></span> <span class="mi">5</span> <span class="o">/</span> <span class="mi">2</span>
<span class="mf">2.5</span></p>
<p><span class="n">ghci</span><span class="o">></span> <span class="p">(</span><span class="mi">50</span> <span class="o"><em></span> <span class="mi">100</span><span class="p">)</span> <span class="o">-</span> <span class="mi">4999</span>
<span class="mi">1</span>
<span class="n">ghci</span><span class="o">></span> <span class="mi">50</span> <span class="o"></em></span> <span class="mi">100</span> <span class="o">-</span> <span class="mi">4999</span>
<span class="mi">1</span>
<span class="n">ghci</span><span class="o">></span> <span class="mi">50</span> <span class="o">*</span> <span class="p">(</span><span class="mi">100</span> <span class="o">-</span> <span class="mi">4999</span><span class="p">)</span>
<span class="o">-</span><span class="mi">244950</span></p>
<p><span class="n">ghci</span><span class="o">></span> <span class="kt">True</span> <span class="o">&&</span> <span class="kt">False</span>
<span class="kt">False</span>
<span class="n">ghci</span><span class="o">></span> <span class="kt">True</span> <span class="o">&&</span> <span class="kt">True</span>
<span class="kt">True</span>
<span class="n">ghci</span><span class="o">></span> <span class="kt">False</span> <span class="o">||</span> <span class="kt">True</span>
<span class="kt">True</span>
<span class="n">ghci</span><span class="o">></span> <span class="n">not</span> <span class="kt">False</span>
<span class="kt">True</span>
<span class="n">ghci</span><span class="o">></span> <span class="n">not</span> <span class="p">(</span><span class="kt">True</span> <span class="o">&&</span> <span class="kt">True</span><span class="p">)</span>
<span class="kt">False</span></p>
<p><span class="n">ghci</span><span class="o">></span> <span class="mi">5</span> <span class="o">==</span> <span class="mi">5</span>
<span class="kt">True</span>
<span class="n">ghci</span><span class="o">></span> <span class="mi">1</span> <span class="o">==</span> <span class="mi">0</span>
<span class="kt">False</span>
<span class="n">ghci</span><span class="o">></span> <span class="mi">5</span> <span class="o">/=</span> <span class="mi">5</span>
<span class="kt">False</span>
<span class="n">ghci</span><span class="o">></span> <span class="mi">5</span> <span class="o">/=</span> <span class="mi">4</span>
<span class="kt">True</span>
<span class="n">ghci</span><span class="o">></span> <span class="s">"hello"</span> <span class="o">==</span> <span class="s">"hello"</span>
<span class="kt">True</span></code></pre></figure></p>
<h3>Calling Functions</h3>
<p>You may not have realized it, but we’ve actually been using functions this whole time. For in- stance, * is a function that takes two numbers and multiplies them. As you’ve seen, we apply (or call) it by sandwiching it between the two numbers we want to multiply. This is called an infix function.</p>
<p> Most functions, however, are <em>prefix</em> func- tions. When calling prefix functions in Haskell, the function name comes first, then a space, then its parameters (also separated by spaces). As an example, we’ll try call- ing one of the most boring functions in Haskell, succ:</p>
<p><figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">ghci</span><span class="o">></span> <span class="n">succ</span> <span class="mi">8</span>
<span class="mi">9</span></code></pre></figure></p>
<p>The succ function takes one parameter that can be anything that has a well-defined successor, and returns that value. The successor of an integer value is just the next higher number.</p>
<p>Now let’s call two prefix functions that take multiple parameters, min and max:</p>
<p><figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">ghci</span><span class="o">></span> <span class="n">min</span> <span class="mi">9</span> <span class="mi">10</span>
<span class="mi">9</span>
<span class="n">ghci</span><span class="o">></span> <span class="n">min</span> <span class="mf">3.4</span> <span class="mf">3.2</span>
<span class="mf">3.2</span>
<span class="n">ghci</span><span class="o">></span> <span class="n">max</span> <span class="mi">100</span> <span class="mi">101</span>
<span class="mi">101</span></code></pre></figure></p>
haskell Install2015-08-12T00:00:00+00:00http://jiaxianhua.github.io/haskell/2015/08/12/haskell-2<h2>Haskell Official Home Download</h2>
<hr />
<p><a href="https://www.haskell.org/downloads">https://www.haskell.org/downloads</a></p>
<h2>OSX brew</h2>
<hr />
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>brew install haskell-platform
Error: No available formula <span class="k">for </span>haskell-platform
We no longer package haskell-platform. Consider installing ghc
and cabal-install instead:
brew install ghc cabal-install</p>
<p>A binary installer is available:
https://www.haskell.org/platform/mac.html</code></pre></figure></p>
<p>so, let's open <a href="https://www.haskell.org/platform/mac.html">https://www.haskell.org/platform/mac.html</a></p>
haskell introduction2015-08-11T00:00:00+00:00http://jiaxianhua.github.io/haskell/2015/08/11/haskell-1<h2>Haskell – Official Home Page: <a href="https://www.haskell.org">https://www.haskell.org</a></h2>
<hr />
<h2>中文:<a href="http://www.haskellcn.org">http://www.haskellcn.org</a></h2>
<hr />
<h3>Haskell</h3>
<hr />
<p>An advanced purely-functional programming language
Declarative, statically typed code.</p>
<p><figure class="highlight"><pre><code class="language-haskell" data-lang="haskell"><span class="n">primes</span> <span class="o">=</span> <span class="n">filterPrime</span> <span class="p">[</span><span class="mi">2</span><span class="o">..</span><span class="p">]</span>
<span class="kr">where</span> <span class="n">filterPrime</span> <span class="p">(</span><span class="n">p</span><span class="o">:</span><span class="n">xs</span><span class="p">)</span> <span class="o">=</span>
<span class="n">p</span> <span class="o">:</span> <span class="n">filterPrime</span> <span class="p">[</span><span class="n">x</span> <span class="o">|</span> <span class="n">x</span> <span class="o"><-</span> <span class="n">xs</span><span class="p">,</span> <span class="n">x</span> <span class="p"><code></span><span class="n">mod</span><span class="p"></code></span> <span class="n">p</span> <span class="o">/=</span> <span class="mi">0</span><span class="p">]</span></code></pre></figure></p>
<h2>Features</h2>
<hr />
<h3>Statically typed</h3>
<hr />
<p>Every expression in Haskell has a type which is determined at compile time. All the types composed together by function application have to match up. If they don't, the program will be rejected by the compiler. Types become not only a form of guarantee, but a language for expressing the construction of programs.</p>
<h3>Purely functional</h3>
<hr />
<p>Every function in Haskell is a function in the mathematical sense (i.e., "pure"). Even side-effecting IO operations are but a description of what to do, produced by pure code. There are no statements or instructions, only expressions which cannot mutate variables (local or global) nor access state like time or random numbers.</p>
<h3>Type inference</h3>
<hr />
<p>You don't have to explicitly write out every type in a Haskell program. Types will be inferred by unifying every type bidirectionally. However, you can write out types if you choose, or ask the compiler to write them for you for handy documentation.</p>
<h3>Concurrent</h3>
<hr />
<p>Haskell lends itself well to concurrent programming due to its explicit handling of effects. Its flagship compiler, GHC, comes with a high-performance parallel garbage collector and light-weight concurrency library containing a number of useful concurrency primitives and abstractions.</p>
<h3>Lazy</h3>
<hr />
<p>Functions don't evaluate their arguments. This means that programs can compose together very well, with the ability to write control constructs (such as if/else) just by writing normal functions. The purity of Haskell code makes it easy to fuse chains of functions together, allowing for performance benefits.</p>
<h3>Packages</h3>
<hr />
<p>Open source contribution to Haskell is very active with a wide range of packages available on the public package servers.</p>
<h2>什么是Haskell?</h2>
<hr />
<p>Haskell 与其它语言不同,是一门纯粹的函数式编程语言(purely functional programming language)。在一般常见的命令式语言中,要执行操作的话是给电脑一组命令,而状态会随着命令的执行而改变。例如你指派变量 a 的值为 5,而随后做了其它天王终点事情之后 a 就可能变成其它值。有控制流程(control flow),你就可以重复执行操作。然而在纯粹函数式编程语言中,你不是像命令式语言那样命令电脑 【要做什么】,而是通过函数描述出总是【是什么】,如【阶乘是指比 1 到莫要个数的乘积】,【一个串列中数据的和】是指把第一个数字跟剩余数字的和相加。你用宣告函数是什么的形式来写程序。另外,变量(variable)一旦被指定,就不可以更改了,你已经说了 a 就是 5,就不能再说 a 是别的什么数。所有说,在纯粹函数式编程语言中函数能做的唯一事情就是利用引数计算结果,不会产生所谓的<strong>副作用(side effect)</strong>。一开始会觉得这限制很大,不过这也是它的优点所在。若以同样的参数调用同一个函数两次,得到的结果一定是相同的。这被称作<strong>引用透明(Referential Transparency)</strong>。如此一来编译器就可以理解程序的行为,你也很容易就验证一个函数的正确性,继而可以将一些简单的函数组合成更复杂的函数。</p>
项目总结2015-08-11T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/08/11/gpxj-37-day<p>项目要求一个月时间完成第一版,所有用了非常多的第三方库,现在列举一下。</p>
<p>基本上都能在 github 上面找到,或者使用</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>pod search xxx</code></pre></figure></p>
<p>找到,就不列举网址了。</p>
<p>Third</p>
<h2>设置属性 Label</h2>
<p>TTTAttributedLabel</p>
<h2>极光推送 (其它人用)</h2>
<p>APService</p>
<h2>短信验证码</h2>
<p>CCPRestSDK</p>
<h2>ios-charts</h2>
<p>图表库,用作股票 k线图,分时图(后期因为收费去除了),庄家历史操作图</p>
<h1>Podfile</h1>
<hr />
<p>source 'https://github.com/CocoaPods/Specs.git'</p>
<h1>最底版支持7.0,好像最后改成 8.0了</h1>
<p>platform :ios, '7.0'</p>
<h3>网络库</h3>
<p>pod "AFNetworking", "~> 2.0"</p>
<h3>数据库 (未使用)</h3>
<p>pod 'FMDB', '~> 2.5'</p>
<h3>分离 Storyboard</h3>
<p>pod 'RBStoryboardLink', '~> 0.1.4'</p>
<h3>刷新库</h3>
<p>pod 'MJRefresh', '~> 2.2.0'</p>
<h3>侧边栏抽屉菜单(未使用,由REFrostedViewController替换)</h3>
<p>pod 'RESideMenu', '~> 4.0.7'</p>
<h3>提示框</h3>
<p>pod 'MBProgressHUD', '~> 0.9.1'</p>
<h3>引导画面</h3>
<p>pod 'Onboard', '2.1.5'</p>
<h3>自动更新</h3>
<p>pod 'iVersion', '~> 1.11.4'</p>
<h3>侧边栏抽屉菜单</h3>
<p>pod 'REFrostedViewController', '~> 2.4.8'</p>
<h3>扩展库(未使用)</h3>
<p>pod 'MJExtension', '~> 2.4.0'</p>
<h3>日志转换(未使用)</h3>
<p>pod 'DateTools', '~> 1.6.1'</p>
<h3>函数响应库 实现注册登录功能</h3>
<p>pod 'ReactiveCocoa', '2.5'</p>
<h3>日志库</h3>
<p>pod 'CocoaLumberjack', '~> 2.0.1'</p>
<h3>手工 Autolayout (其他人用)</h3>
<p>pod 'Masonry', '~> 0.6.2'</p>
<h3>输入管理</h3>
<p>pod 'IQKeyboardManager', '~> 3.2.4'</p>
<h3>替代 UIAlertView (其他人用)</h3>
<p>pod 'SIAlertView', '~> 1.3'</p>
<h3>另一个 提示库 (其他人用)</h3>
<p>pod 'SVProgressHUD', '~> 1.1.3'</p>
<h3>应用内测</h3>
<p>pod 'FIR.im', '~> 1.2.0'</p>
<h3>统计 (其它人用)</h3>
<p>pod 'UMengAnalytics-NO-IDFA', '~> 3.5.9'</p>
<h3>自定义 AlertView (未使用)</h3>
<p>pod 'CustomIOSAlertView', '~> 0.9.3'</p>
<h3>Block 库 (未使用)</h3>
<p>#pod 'BlocksKit', '~> 2.2.5'</p>
<h3>提示库 (未使用)</h3>
<p>pod 'SDCAlertView', '~> 2.4.1'</p>
<h3>分段选择 类似 Android, 在顶部实现 Tabbar 效果</h3>
<p>pod 'THSegmentedPager', '~> 1.1.0'</p>
<h3>自己实现的广告条,项目中用 SDWebImage 加载网络图片</h3>
<p>pod 'YWBannerView', '~> 1.0.0'</p>
<h3>调试使用,可观察 手机上面 UIView 尺寸</h3>
<p>pod 'MMPlaceHolder', '~> 1.8'</p>
<h3>瀑布流 (后期不用了)</h3>
<p>pod 'CHTCollectionViewWaterfallLayout', '~> 0.9.1'</p>
<h3>提示用户先登录</h3>
<p>pod 'KLCPopup', '~> 1.0'</p>
<h3>缓存图片</h3>
<p>pod 'SDWebImage', '~> 3.7.3'</p>
<h3>主页三个卡片滑动,替换 UICollectionView</h3>
<p>pod 'iCarousel', '~> 1.8.1'</p>
<h3>数据存储 (其他人用)</h3>
<p>pod 'NanoStore', '~> 2.7.7'</p>
<h3>显示 TextField 提示信息</h3>
<p>pod 'JVFloatLabeledTextField', '~> 1.1.0'</p>
<h3>实时推送右上角图标</h3>
<p>pod 'RKNotificationHub', '~> 2.0.1'</p>
<h3>带分享的 WebView</h3>
<p>pod 'SVWebViewController', '~> 1.0'</p>
<h3>二级 TableViewCell</h3>
<p>pod 'RATreeView', '~> 1.0.3'</p>
<h3>状态栏提示 无网络时使用</h3>
<p>pod 'JDStatusBarNotification', '~> 1.5.2'</p>
<h3>TableViewCell 自适应高度 (比较麻烦,后期改 iOS 8, 就不使用了)</h3>
<p>pod 'UITableView+FDTemplateLayoutCell', '~> 1.3'</p>
<h1>开发过程</h1>
<hr />
<p>7月5号编码开始,8月10号第一版完成,花了一个月零几天。</p>
<p>一开始是两个人做,我测试了网络连接,做好应用架构后让另一个哥们做网络请求,后期作数据缓存。</p>
<p>其它我处理,7月5号新建项目,上传到 Bitbucket 免费仓库。用 iOS 最佳实践 上面的方式,分了</p>
<p>Models, Views, Controllers, Stores, Helpers几个组,又添加了Constants, Third, ViewModel。</p>
<ul>
<li>常量使用</li>
</ul>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">// Constants.h
FOUNDATION_EXPORT NSString * const kUserHasOnboardedKey;</p>
<p>// Constants.m
NSString * const kUserHasOnboardedKey = @"user_has_onboarded";</code></pre></figure></p>
<p>最先把引导画面,左侧菜单,注册,登录完成,又把最右边的 Tabbar 信息完成。</p>
<p>股票图最开始使用的是原生组件 CoreGraphics,为了和 Android 一样(Android 用的MPAndroidChart, 比iOS早一个多月开发)。</p>
<p>在 github 上面找到 ios-charts,一些东西需要定制,比如颜色,要改源码。第一版完成,坐标轴也没有改成和 Android 一致,主要是没有时间了。</p>
<p>有比较多的模型数据,我找到一个可以把 JSON 数据转换成 Objective-c 的 Xcode 插件 ESJsonFormat.</p>
<p>可以直接输入到 文件 ,再添加进 XCode 就可以了。记得把映射 id。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">+ (NSDictionary *)replacedKeyFromPropertyName {
return @{@"ID": @"id"};
}</code></pre></figure></p>
<p>一开始没有设计图,说先按照 Android 的做,而 Android 还要改版。7月20几号了 iPhone 6 设计图终于出来了,没有标记尺寸。</p>
<p>幸好有 Pixel Winch,当时是免费使用的,可以取尺寸。然后又按照设计图改,花时间啊。</p>
<p>网络层测试,参照 WWDC 2014 上的视频,那个是 swift 版的,后来找到objc.io上面的 测试异步代码。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">// 获取新闻
- (void)testGetBanner {
XCTestExpectation *expectation = [self expectationWithDescription:@"GetBanner called"];</p>
<pre><code>[[DataCenter SharedInstance] GetBannerWithCallBack:^(id data, NSError *error) {
[expectation fulfill];
XCTAssertNil(error, @"error should be nil");
XCTAssertNotNil(data, @"data should not be nil");
NSDictionary *dictData = (NSDictionary *)data;
XCTAssertNil(error, @"dictData should be Dictionary");
id resultData = dictData[@"data"];
XCTAssertNotEqual([NSNull null], resultData, @"resultData should not be nil");
NSLog(@"Success : %s-%@", __func__, data);
}];
// 10s 超时
[self waitForExpectationsWithTimeout:10 handler:^(NSError *error) {
NSLog(@"%s-%@", __func__, error);
}];
</code></pre>
<p>}</code></pre></figure></p>
<p>最后新招进一个专职的,处理业务逻辑。</p>
<p>8月10号,星期天,第一版本要出来,加班,处理股票池的排序等其它事情。</p>
<h1>总结</h1>
<hr />
<p>一定要把主界面先做出来,主要功能先处理,而不要挑两侧菜单上不太重要的提示,动态,简介之类的先处理。</p>
<p>另一点是一定不能等后台数据。当时显示 k 线图是数据是错误的,怎么改都不对,一两天过去,也没有什么进展。</p>
<p>要么模拟数据,不太熟悉数据,要么就先做其它的吧。</p>
<p>最后,能见面沟通还是见面沟通吧。</p>
项目第三十六天 一文让你彻底了解iOS字体相关知识2015-08-10T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/08/10/gpxj-36-day<h2>原文:<a href="http://www.cnblogs.com/dsxniubility/p/4699352.html">一文让你彻底了解iOS字体相关知识</a></h2>
<hr />
<p>写本文的契机主要是把自己整理的关于iOS字体方面的知识不断更新写在这篇博文中,用来自己以后查阅。</p>
<h3>一、iOS原生字体展示</h3>
<hr />
<p>在label中选择字体的font,并把font由system改成custom后,就能在family中看到72种特殊字体。这些里面就有很炫的字体,但是全部是只针对英文数字,对中文无效。写了一个程序把所有的原生样式遍历出来展示可以达到如下效果。可以清楚地看到每个字体对应的样式,不用再一个个试了。</p>
<p><img src="/assets/img/ios/gpxj/36/1.png" alt="1" />
<img src="/assets/img/ios/gpxj/36/2.png" alt="2" />
<img src="/assets/img/ios/gpxj/36/3.png" alt="3" />
<img src="/assets/img/ios/gpxj/36/4.png" alt="4" />
<img src="/assets/img/ios/gpxj/36/5.png" alt="5" />
<img src="/assets/img/ios/gpxj/36/6.png" alt="6" /></p>
<p>一共是72种样式,我这个demo程序有两种展示方法,简洁展示和详细展示,简洁展示中只会把每个family的第一个font拿出来展示。最后一张图是详细展示界面的。分了group展示,每个section对应一个family。可以看出苹果的原生字体还是有很多美观的字体,只是都仅对英文支持。</p>
<p>相信现在大部分的软件大部分的项目都是这么写代码的:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">label.font = [UIFont systemFontOfSize:14];</code></pre></figure></p>
<p>如果不想用默认系统字体则需要使用此方法赋值:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">UIFont *font = [UIFont fontWithName:@"Georgia" size:14];</code></pre></figure></p>
<p>这里传进Name里的参数是familyName而不是fontName。</p>
<h3>二、获取family名称</h3>
<hr />
<p>那么如何获取这个family的名称?</p>
<ol>
<li>在storyboard中或是xib中用label的图形化界面选中一个自己喜欢的样式,然后把名称记下写到代码中。</li>
<li>上面就有啊,从上面5张图中选吧。</li>
<li>(推荐)遍历</li>
</ol>
<p>在UIFont类中有这些关于家族名和字体名的开放API,通过这些可以清晰的写个遍历打印,查看所有的familyName和其中包含的fontName</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">int i = 0;
for(NSString <em>fontfamilyname in [UIFont familyNames])
{
NSLog(@"family:'%@'",fontfamilyname);
for(NSString </em>fontName in [UIFont fontNamesForFamilyName:fontfamilyname])
{
NSLog(@"\tfont:'%@'",fontName);
}
NSLog(@"-------------%d",i++);
}</code></pre></figure></p>
<p>使用上面的代码即可遍历打印出所有的名称,然后从打印中复制名称到代码中个人感觉更为科学。</p>
<h3>三、外界字体引入项目</h3>
<hr />
<p>本人亲测,在网上不管是windows字体,还是Android字体只要是ttf格式的,一般iOS程序都支持内嵌。</p>
<p>具体步骤也很简单:</p>
<ol>
<li>将ttf文件拖入项目中</li>
</ol>
<p><img src="/assets/img/ios/gpxj/36/7.png" alt="7" /></p>
<ol>
<li>修改plist文件,加入Fonts provided by application 配置,后面填上拖进来的项目名</li>
</ol>
<p><img src="/assets/img/ios/gpxj/36/8.png" alt="8" /></p>
<ol>
<li>就可以在图形化界面看到新的字体选择了</li>
</ol>
<p><img src="/assets/img/ios/gpxj/36/9.png" alt="9" /></p>
<ol>
<li>如果不想从IB界面找,建议使用一下上面的遍历打印,可以用循环打印数量来最直接的看是否导入成功,并找到自己需要的内容。</li>
</ol>
<p><img src="/assets/img/ios/gpxj/36/10.png" alt="10" /></p>
<ol>
<li>运行项目得到自己想要的结果</li>
</ol>
<p><img src="/assets/img/ios/gpxj/36/11.png" alt="11" /></p>
<h3>四、动态字体</h3>
<hr />
<p>动态字体-Dynamic Type源于iOS7引入的一个文本渲染框架TextKit。主要的作用就是可以系统自设大小。当下的苹果已经做了越来越多的人性化的处理,甚至连盲人模式都有。对于字体的展示也是考虑到了各人的喜好,有的人喜欢看大字,有的人喜欢看小字。在动态字体出来之前,有的应用也考虑到了此用户体验,比如网易新闻以前就有能够在应用中设置偏好的字体大小功能。苹果也整合到了整个手机中,动态字体的思想就是:<strong>在setting中设置字体大小,不单单系统的字体会变,连应用程序中的字体大小也会随之改变。</strong>前提是你应用程序中的字体的代码写的符合要求。</p>
<p><img src="/assets/img/ios/gpxj/36/12.png" alt="12" /></p>
<p>前面的文章大部分说的都是要在字体中选custom,这时要考虑动态字体就有选 Text Styles中的选项了。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">UIFontTextStyleHeadline
UIFontTextStyleBody
UIFontTextStyleSubheadline
UIFontTextStyleFootnote
UIFontTextStyleCaption1
UIFontTextStyleCaption2</code></pre></figure></p>
<p>这些样式顾名思义,就不每个都列出显示效果了。标题,子标题,正文等等都是一些比较朴素。 我感觉这也就相当于word中的“样式”,把自己的每个章节的标题子标题选中设置成标题一或者标题二 然后就能用word的自动生成目录功能,并且一改某个样式里详细设置,每个标题子标题的格式也都会随之改变。 这里就是如果你在代码中把字体用这些样式,那你在手机setting里设置大小之后应用字体会有所反应。</p>
<p><strong>设置字体的位置是: 设置-> 显示与亮度-> 文字大小</strong></p>
<p><img src="/assets/img/ios/gpxj/36/13.png" alt="13" /></p>
<p><img src="/assets/img/ios/gpxj/36/14.png" alt="14" /></p>
<p>如上左图是把尺寸调到了最大后的效果,下面的提示语只有到最大才会显示。 我试了下QQ里的字体设置的都是动态字体,微信和支付宝暂时还不支持动态大小,在字体尺寸设置很大后微信支付宝没反应,QQ则响应改变。上面右图是在iphone6上截到QQ客户端的展示效果。</p>
<p>设置动态字体的代码实现如下:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">UIFont *font = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];</code></pre></figure></p>
<p>项目中建议把动态字体和自动布局结合起来用,以防止字体设置改变后出现错位Bug。 </p>
<h3>五、字体描述符</h3>
<hr />
<p>
字体描述符-UIFontDescriptor 也是TextKit的核心之一,大致意思就是:字体描述符可以把一个你不知道详情的font样式临时存起来做修改或赋值给别人使用。在使用了上面的动态字体之后,可能你只知道现在的text-Style但是详细的familyName,fontName都不知道是什么,这种情况下如果想修改字体的样式为斜体或粗体就只能使用这种方法:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">// ------取出当前正文的字体样式
UIFontDescriptor <em>bodyFontDesciptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleBody];
// ------把样式改为斜体
UIFontDescriptor </em>italicFontDescriptor = [bodyFontDesciptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic];
// ------赋值给另一个label。
self.titleLabel.font = [UIFont fontWithDescriptor:italicFontDescriptor size:0.0];</code></pre></figure></p>
<p>关于样式一共有四种可选:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">UIFontDescriptorTraitItalic</p>
<p>UIFontDescriptorTraitExpanded</p>
<p>UIFontDescriptorTraitCondensed</p>
<p>UIFontDescriptorTraitBold</code></pre></figure></p>
<p>字体描述符还有一个API是通过详细属性字典设置一个label的样式,写法如下</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">UIFontDescriptor <em>attributeFontDescriptor = [UIFontDescriptor fontDescriptorWithFontAttributes:
@{UIFontDescriptorFamilyAttribute: @"Avenir Next Condensed",
UIFontDescriptorNameAttribute:@"AvenirNextCondensed-Italic",
UIFontDescriptorSizeAttribute: @40.0,
UIFontDescriptorMatrixAttribute:[NSValue valueWithCGAffineTransform:CGAffineTransformMakeRotation(M_1_PI</em>1.5)
]}];
label.font = [UIFont fontWithDescriptor:attributeFontDescriptor size:0.0];</code></pre></figure></p>
<p>这上面分别设置了家族名,字体名,尺寸,形变,最后的size填0.0就可以,如果填了一个值,那这个值会把上面字典中的尺寸覆盖,感觉一般情况下应该不会有人这么蛋疼用这种方法建样式,这个Attribute按command点进去还有很多,大多都是平时用不到的,有兴趣的可以一个一个钻研,好像一共有十几个。</p>
<p>上面这段代码创建的label会显示成这样:</p>
<p><img src="/assets/img/ios/gpxj/36/15.png" alt="15" /></p>
<h3>六、扩展字体样式</h3>
<hr />
<p>上面说了原生全部不支持中文,但是我们用中文的人还是比较多,中文字体现在在网上搜索结果很多,但是大部分都不是想要的结果,要不就是不会让你那么简单下载的。我整理了一个常用的字体样式包,里面大致包括:</p>
<p>华文行楷,华文琥珀,华文新魏,隶书 等等这些熟悉的名字</p>
<p>纯净下载地址:<a href="http://pan.baidu.com/s/1hqfGdpE">http://pan.baidu.com/s/1hqfGdpE</a> 密码:31qs</p>
<p>除了这些常用样式,还有一些非常规的字体样式,当然好的字体遇到时我会积累,并整理在下面,不要求多,只要求精。</p>
<p>纯净下载地址:<a href="http://pan.baidu.com/s/1i38etV3">http://pan.baidu.com/s/1i38etV3</a> 密码:hnv7</p>
<p><img src="/assets/img/ios/gpxj/36/16.png" alt="16" /></p>
<p>如果有非常推荐的字体也欢迎告诉我 我整理在一起。</p>
<p>上面说的有个程序可以看到iOS所有字体的展示样式,下载地址在:<a href="https://github.com/dsxNiubility/SXFontShow">https://github.com/dsxNiubility/SXFontShow</a></p>
<p>董铂然博客园看到更新,请<a href="http://www.cnblogs.com/dsxniubility/p/4699352.html">点击查看原文</a>。</p>
项目第三十五天 chart 动画2015-08-09T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/08/09/gpxj-35-day<h3>ChartEasingOption</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">// 平滑过度选项</span>
<span class="kd">@objc</span>
<span class="kd">public</span> <span class="kd">enum</span> <span class="kt">ChartEasingOption</span><span class="p">:</span> <span class="kt">Int</span>
<span class="p">{</span>
<span class="k">case</span> <span class="kt">Linear</span>
<span class="k">case</span> <span class="kt">EaseInQuad</span>
<span class="k">case</span> <span class="kt">EaseOutQuad</span>
<span class="k">case</span> <span class="kt">EaseInOutQuad</span>
<span class="k">case</span> <span class="kt">EaseInCubic</span>
<span class="k">case</span> <span class="kt">EaseOutCubic</span>
<span class="k">case</span> <span class="kt">EaseInOutCubic</span>
<span class="k">case</span> <span class="kt">EaseInQuart</span>
<span class="k">case</span> <span class="kt">EaseOutQuart</span>
<span class="k">case</span> <span class="kt">EaseInOutQuart</span>
<span class="k">case</span> <span class="kt">EaseInQuint</span>
<span class="k">case</span> <span class="kt">EaseOutQuint</span>
<span class="k">case</span> <span class="kt">EaseInOutQuint</span>
<span class="k">case</span> <span class="kt">EaseInSine</span>
<span class="k">case</span> <span class="kt">EaseOutSine</span>
<span class="k">case</span> <span class="kt">EaseInOutSine</span>
<span class="k">case</span> <span class="kt">EaseInExpo</span>
<span class="k">case</span> <span class="kt">EaseOutExpo</span>
<span class="k">case</span> <span class="kt">EaseInOutExpo</span>
<span class="k">case</span> <span class="kt">EaseInCirc</span>
<span class="k">case</span> <span class="kt">EaseOutCirc</span>
<span class="k">case</span> <span class="kt">EaseInOutCirc</span>
<span class="k">case</span> <span class="kt">EaseInElastic</span>
<span class="k">case</span> <span class="kt">EaseOutElastic</span>
<span class="k">case</span> <span class="kt">EaseInOutElastic</span>
<span class="k">case</span> <span class="kt">EaseInBack</span>
<span class="k">case</span> <span class="kt">EaseOutBack</span>
<span class="k">case</span> <span class="kt">EaseInOutBack</span>
<span class="k">case</span> <span class="kt">EaseInBounce</span>
<span class="k">case</span> <span class="kt">EaseOutBounce</span>
<span class="k">case</span> <span class="kt">EaseInOutBounce</span>
<span class="p">}</span></p>
<p><span class="c1">// elapsed: v. 时间过去;消逝(elapse的过去分词)</span>
<span class="c1">// duration: n. 持续,持续的时间,期间</span>
<span class="kd">public</span> <span class="kd">typealias</span> <span class="kt">ChartEasingFunctionBlock</span> <span class="o">=</span> <span class="p">((</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-></span> <span class="kt">CGFloat</span><span class="p">)</span></p>
<p><span class="c1">// 根据动画过渡类型,获取动画函数</span>
<span class="kd">internal</span> <span class="kd">func</span> <span class="nf">easingFunctionFromOption</span><span class="p">(</span><span class="nv">easing</span><span class="p">:</span> <span class="kt">ChartEasingOption</span><span class="p">)</span> <span class="o">-></span> <span class="kt">ChartEasingFunctionBlock</span>
<span class="p">{</span>
<span class="k">switch</span> <span class="n">easing</span>
<span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">Linear</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">Linear</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInQuad</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInQuad</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseOutQuad</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseOutQuad</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInOutQuad</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInOutQuad</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInCubic</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInCubic</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseOutCubic</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseOutCubic</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInOutCubic</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInOutCubic</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInQuart</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInQuart</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseOutQuart</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseOutQuart</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInOutQuart</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInOutQuart</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInQuint</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInQuint</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseOutQuint</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseOutQuint</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInOutQuint</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInOutQuint</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInSine</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInSine</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseOutSine</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseOutSine</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInOutSine</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInOutSine</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInExpo</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInExpo</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseOutExpo</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseOutExpo</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInOutExpo</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInOutExpo</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInCirc</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInCirc</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseOutCirc</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseOutCirc</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInOutCirc</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInOutCirc</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInElastic</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInElastic</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseOutElastic</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseOutElastic</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInOutElastic</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInOutElastic</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInBack</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInBack</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseOutBack</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseOutBack</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInOutBack</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInOutBack</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInBounce</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInBounce</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseOutBounce</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseOutBounce</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">EaseInOutBounce</span><span class="p">:</span>
<span class="k">return</span> <span class="kt">EasingFunctions</span><span class="o">.</span><span class="kt">EaseInOutBounce</span>
<span class="p">}</span>
<span class="p">}</span></p>
<p><span class="c1">// 平滑过度函数</span>
<span class="kd">internal</span> <span class="kd">struct</span> <span class="kt">EasingFunctions</span>
<span class="p">{</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">Linear</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-></span> <span class="kt">CGFloat</span> <span class="k">in</span> <span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span><span class="p">);</span> <span class="p">}</span></p>
<pre><code><span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInQuad</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span><span class="p">)</span>
<span class="k">return</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseOutQuad</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span><span class="p">)</span>
<span class="k">return</span> <span class="o">-</span><span class="n">position</span> <span class="o">*</span> <span class="p">(</span><span class="n">position</span> <span class="o">-</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInOutQuad</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="p">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">))</span>
<span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">&lt;</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span>
<span class="p">}</span>
<span class="k">return</span> <span class="o">-</span><span class="mf">0.5</span> <span class="o">*</span> <span class="p">((</span><span class="o">--</span><span class="n">position</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">position</span> <span class="o">-</span> <span class="mf">2.0</span><span class="p">)</span> <span class="o">-</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInCubic</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span><span class="p">)</span>
<span class="k">return</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseOutCubic</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span><span class="p">)</span>
<span class="n">position</span><span class="o">--</span>
<span class="nf">return</span> <span class="p">(</span><span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">+</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInOutCubic</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="p">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">))</span>
<span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">&lt;</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span>
<span class="p">}</span>
<span class="n">position</span> <span class="o">-=</span> <span class="mf">2.0</span>
<span class="k">return</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="p">(</span><span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">+</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInQuart</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span><span class="p">)</span>
<span class="k">return</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseOutQuart</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span><span class="p">)</span>
<span class="n">position</span><span class="o">--</span>
<span class="k">return</span> <span class="o">-</span><span class="p">(</span><span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">-</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInOutQuart</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="p">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">))</span>
<span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">&lt;</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span>
<span class="p">}</span>
<span class="n">position</span> <span class="o">-=</span> <span class="mf">2.0</span>
<span class="k">return</span> <span class="o">-</span><span class="mf">0.5</span> <span class="o">*</span> <span class="p">(</span><span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">-</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInQuint</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span><span class="p">)</span>
<span class="k">return</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseOutQuint</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span><span class="p">)</span>
<span class="n">position</span><span class="o">--</span>
<span class="nf">return</span> <span class="p">(</span><span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">+</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInOutQuint</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="p">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">))</span>
<span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">&lt;</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">position</span> <span class="o">-=</span> <span class="mf">2.0</span>
<span class="k">return</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="p">(</span><span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">+</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInSine</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="o">-</span><span class="nf">cos</span><span class="p">(</span><span class="n">position</span> <span class="o">*</span> <span class="kt">M_PI_2</span><span class="p">)</span> <span class="o">+</span> <span class="mf">1.0</span> <span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseOutSine</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="nf">sin</span><span class="p">(</span><span class="n">position</span> <span class="o">*</span> <span class="kt">M_PI_2</span><span class="p">)</span> <span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInOutSine</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="o">-</span><span class="mf">0.5</span> <span class="o">*</span> <span class="p">(</span><span class="nf">cos</span><span class="p">(</span><span class="kt">M_PI</span> <span class="o">*</span> <span class="n">position</span><span class="p">)</span> <span class="o">-</span> <span class="mf">1.0</span><span class="p">)</span> <span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInExpo</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="nf">return</span> <span class="p">(</span><span class="n">elapsed</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">?</span> <span class="mf">0.0</span> <span class="p">:</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="nf">pow</span><span class="p">(</span><span class="mf">2.0</span><span class="p">,</span> <span class="mf">10.0</span> <span class="o">*</span> <span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span> <span class="o">-</span> <span class="mf">1.0</span><span class="p">)))</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseOutExpo</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="nf">return</span> <span class="p">(</span><span class="n">elapsed</span> <span class="o">==</span> <span class="n">duration</span><span class="p">)</span> <span class="p">?</span> <span class="mf">1.0</span> <span class="p">:</span> <span class="p">(</span><span class="o">-</span><span class="kt">CGFloat</span><span class="p">(</span><span class="nf">pow</span><span class="p">(</span><span class="mf">2.0</span><span class="p">,</span> <span class="o">-</span><span class="mf">10.0</span> <span class="o">*</span> <span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span><span class="p">))</span> <span class="o">+</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInOutExpo</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">if</span> <span class="p">(</span><span class="n">elapsed</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">0.0</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">elapsed</span> <span class="o">==</span> <span class="n">duration</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">1.0</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">position</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsed</span> <span class="o">/</span> <span class="p">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">&lt;</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="nf">pow</span><span class="p">(</span><span class="mf">2.0</span><span class="p">,</span> <span class="mf">10.0</span> <span class="o">*</span> <span class="p">(</span><span class="n">position</span> <span class="o">-</span> <span class="mf">1.0</span><span class="p">))</span> <span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="p">(</span><span class="o">-</span><span class="nf">pow</span><span class="p">(</span><span class="mf">2.0</span><span class="p">,</span> <span class="o">-</span><span class="mf">10.0</span> <span class="o">*</span> <span class="o">--</span><span class="n">position</span><span class="p">)</span> <span class="o">+</span> <span class="mf">2.0</span><span class="p">)</span> <span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInCirc</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span><span class="p">)</span>
<span class="k">return</span> <span class="o">-</span><span class="p">(</span><span class="kt">CGFloat</span><span class="p">(</span><span class="nf">sqrt</span><span class="p">(</span><span class="mf">1.0</span> <span class="o">-</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span><span class="p">))</span> <span class="o">-</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseOutCirc</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span><span class="p">)</span>
<span class="n">position</span><span class="o">--</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="nf">sqrt</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span><span class="p">)</span> <span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInOutCirc</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsed</span> <span class="o">/</span> <span class="p">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">&lt;</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="o">-</span><span class="mf">0.5</span> <span class="o">*</span> <span class="p">(</span><span class="nf">sqrt</span><span class="p">(</span><span class="mf">1.0</span> <span class="o">-</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span><span class="p">)</span> <span class="o">-</span> <span class="mf">1.0</span><span class="p">)</span> <span class="p">)</span>
<span class="p">}</span>
<span class="n">position</span> <span class="o">-=</span> <span class="mf">2.0</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="p">(</span><span class="nf">sqrt</span><span class="p">(</span><span class="mf">1.0</span> <span class="o">-</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span><span class="p">)</span> <span class="o">+</span> <span class="mf">1.0</span><span class="p">)</span> <span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInElastic</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">if</span> <span class="p">(</span><span class="n">elapsed</span> <span class="o">==</span> <span class="mf">0.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">0.0</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">position</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span>
<span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">==</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">1.0</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">p</span> <span class="o">=</span> <span class="n">duration</span> <span class="o">*</span> <span class="mf">0.3</span>
<span class="k">var</span> <span class="nv">s</span> <span class="o">=</span> <span class="n">p</span> <span class="o">/</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="o">*</span> <span class="nf">asin</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span>
<span class="n">position</span> <span class="o">-=</span> <span class="mf">1.0</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="o">-</span><span class="p">(</span><span class="nf">pow</span><span class="p">(</span><span class="mf">2.0</span><span class="p">,</span> <span class="mf">10.0</span> <span class="o">*</span> <span class="n">position</span><span class="p">)</span> <span class="o">*</span> <span class="nf">sin</span><span class="p">((</span><span class="n">position</span> <span class="o">*</span> <span class="n">duration</span> <span class="o">-</span> <span class="n">s</span><span class="p">)</span> <span class="o">*</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="o">/</span> <span class="n">p</span><span class="p">))</span> <span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseOutElastic</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">if</span> <span class="p">(</span><span class="n">elapsed</span> <span class="o">==</span> <span class="mf">0.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">0.0</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">position</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span>
<span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">==</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">1.0</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">p</span> <span class="o">=</span> <span class="n">duration</span> <span class="o">*</span> <span class="mf">0.3</span>
<span class="k">var</span> <span class="nv">s</span> <span class="o">=</span> <span class="n">p</span> <span class="o">/</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="o">*</span> <span class="nf">asin</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="nf">pow</span><span class="p">(</span><span class="mf">2.0</span><span class="p">,</span> <span class="o">-</span><span class="mf">10.0</span> <span class="o">*</span> <span class="n">position</span><span class="p">)</span> <span class="o">*</span> <span class="nf">sin</span><span class="p">((</span><span class="n">position</span> <span class="o">*</span> <span class="n">duration</span> <span class="o">-</span> <span class="n">s</span><span class="p">)</span> <span class="o">*</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="o">/</span> <span class="n">p</span><span class="p">)</span> <span class="o">+</span> <span class="mf">1.0</span> <span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInOutElastic</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">if</span> <span class="p">(</span><span class="n">elapsed</span> <span class="o">==</span> <span class="mf">0.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">0.0</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">position</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsed</span> <span class="o">/</span> <span class="p">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">==</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">1.0</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">p</span> <span class="o">=</span> <span class="n">duration</span> <span class="o">*</span> <span class="p">(</span><span class="mf">0.3</span> <span class="o">*</span> <span class="mf">1.5</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">s</span> <span class="o">=</span> <span class="n">p</span> <span class="o">/</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="o">*</span> <span class="nf">asin</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">&lt;</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">position</span> <span class="o">-=</span> <span class="mf">1.0</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="o">-</span><span class="mf">0.5</span> <span class="o">*</span> <span class="p">(</span><span class="nf">pow</span><span class="p">(</span><span class="mf">2.0</span><span class="p">,</span> <span class="mf">10.0</span> <span class="o">*</span> <span class="n">position</span><span class="p">)</span> <span class="o">*</span> <span class="nf">sin</span><span class="p">((</span><span class="n">position</span> <span class="o">*</span> <span class="n">duration</span> <span class="o">-</span> <span class="n">s</span><span class="p">)</span> <span class="o">*</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="o">/</span> <span class="n">p</span><span class="p">))</span> <span class="p">)</span>
<span class="p">}</span>
<span class="n">position</span> <span class="o">-=</span> <span class="mf">1.0</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="nf">pow</span><span class="p">(</span><span class="mf">2.0</span><span class="p">,</span> <span class="o">-</span><span class="mf">10.0</span> <span class="o">*</span> <span class="n">position</span><span class="p">)</span> <span class="o">*</span> <span class="nf">sin</span><span class="p">((</span><span class="n">position</span> <span class="o">*</span> <span class="n">duration</span> <span class="o">-</span> <span class="n">s</span><span class="p">)</span> <span class="o">*</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="o">/</span> <span class="n">p</span><span class="p">)</span> <span class="o">*</span> <span class="mf">0.5</span> <span class="o">+</span> <span class="mf">1.0</span> <span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInBack</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">let</span> <span class="nv">s</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="mf">1.70158</span>
<span class="k">var</span> <span class="nv">position</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="p">((</span><span class="n">s</span> <span class="o">+</span> <span class="mf">1.0</span><span class="p">)</span> <span class="o">*</span> <span class="n">position</span> <span class="o">-</span> <span class="n">s</span><span class="p">)</span> <span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseOutBack</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">let</span> <span class="nv">s</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="mf">1.70158</span>
<span class="k">var</span> <span class="nv">position</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span>
<span class="n">position</span><span class="o">--</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="p">(</span><span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="p">((</span><span class="n">s</span> <span class="o">+</span> <span class="mf">1.0</span><span class="p">)</span> <span class="o">*</span> <span class="n">position</span> <span class="o">+</span> <span class="n">s</span><span class="p">)</span> <span class="o">+</span> <span class="mf">1.0</span><span class="p">)</span> <span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInOutBack</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">s</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="mf">1.70158</span>
<span class="k">var</span> <span class="nv">position</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsed</span> <span class="o">/</span> <span class="p">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">&lt;</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">s</span> <span class="o">*=</span> <span class="mf">1.525</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="p">(</span><span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="p">((</span><span class="n">s</span> <span class="o">+</span> <span class="mf">1.0</span><span class="p">)</span> <span class="o">*</span> <span class="n">position</span> <span class="o">-</span> <span class="n">s</span><span class="p">))</span> <span class="p">)</span>
<span class="p">}</span>
<span class="n">s</span> <span class="o">*=</span> <span class="mf">1.525</span>
<span class="n">position</span> <span class="o">-=</span> <span class="mf">2.0</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="mf">0.5</span> <span class="o">*</span> <span class="p">(</span><span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="p">((</span><span class="n">s</span> <span class="o">+</span> <span class="mf">1.0</span><span class="p">)</span> <span class="o">*</span> <span class="n">position</span> <span class="o">+</span> <span class="n">s</span><span class="p">)</span> <span class="o">+</span> <span class="mf">2.0</span><span class="p">)</span> <span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInBounce</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">return</span> <span class="mf">1.0</span> <span class="o">-</span> <span class="kt">EaseOutBounce</span><span class="p">(</span><span class="n">duration</span> <span class="o">-</span> <span class="n">elapsed</span><span class="p">,</span> <span class="n">duration</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseOutBounce</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">var</span> <span class="nv">position</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span>
<span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mf">1.0</span> <span class="o">/</span> <span class="mf">2.75</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="mf">7.5625</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="p">)</span>
<span class="p">}</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mf">2.0</span> <span class="o">/</span> <span class="mf">2.75</span><span class="p">))</span>
<span class="p">{</span>
<span class="n">position</span> <span class="o">-=</span> <span class="p">(</span><span class="mf">1.5</span> <span class="o">/</span> <span class="mf">2.75</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="mf">7.5625</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">+</span> <span class="mf">0.75</span> <span class="p">)</span>
<span class="p">}</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">position</span> <span class="o">&lt;</span> <span class="p">(</span><span class="mf">2.5</span> <span class="o">/</span> <span class="mf">2.75</span><span class="p">))</span>
<span class="p">{</span>
<span class="n">position</span> <span class="o">-=</span> <span class="p">(</span><span class="mf">2.25</span> <span class="o">/</span> <span class="mf">2.75</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="mf">7.5625</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">+</span> <span class="mf">0.9375</span> <span class="p">)</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">position</span> <span class="o">-=</span> <span class="p">(</span><span class="mf">2.625</span> <span class="o">/</span> <span class="mf">2.75</span><span class="p">)</span>
<span class="k">return</span> <span class="kt">CGFloat</span><span class="p">(</span> <span class="mf">7.5625</span> <span class="o">*</span> <span class="n">position</span> <span class="o">*</span> <span class="n">position</span> <span class="o">+</span> <span class="mf">0.984375</span> <span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">static</span> <span class="k">let</span> <span class="nv">EaseInOutBounce</span> <span class="o">=</span> <span class="p">{</span> <span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CGFloat</span> <span class="k">in</span>
<span class="k">if</span> <span class="p">(</span><span class="n">elapsed</span> <span class="o">&lt;</span> <span class="p">(</span><span class="n">duration</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kt">EaseInBounce</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">*</span> <span class="mf">2.0</span><span class="p">,</span> <span class="n">duration</span><span class="p">)</span> <span class="o">*</span> <span class="mf">0.5</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kt">EaseOutBounce</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">*</span> <span class="mf">2.0</span> <span class="o">-</span> <span class="n">duration</span><span class="p">,</span> <span class="n">duration</span><span class="p">)</span> <span class="o">*</span> <span class="mf">0.5</span> <span class="o">+</span> <span class="mf">0.5</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<h3>ChartAnimator</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">@objc</span>
<span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">ChartAnimatorDelegate</span>
<span class="p">{</span>
<span class="c1">/// Called when the Animator has stepped.</span>
<span class="c1">/// 动画执行时调用</span>
<span class="kd">func</span> <span class="nf">chartAnimatorUpdated</span><span class="p">(</span><span class="nv">chartAnimator</span><span class="p">:</span> <span class="kt">ChartAnimator</span><span class="p">)</span></p>
<pre><code><span class="c1">/// Called when the Animator has stopped.</span>
<span class="c1">/// 动画结束时调用</span>
<span class="kd">func</span> <span class="nf">chartAnimatorStopped</span><span class="p">(</span><span class="nv">chartAnimator</span><span class="p">:</span> <span class="kt">ChartAnimator</span><span class="p">)</span>
</code></pre>
<p><span class="p">}</span></p>
<p><span class="kd">public</span> <span class="kd">class</span> <span class="kt">ChartAnimator</span><span class="p">:</span> <span class="kt">NSObject</span>
<span class="p">{</span>
<span class="kd">public</span> <span class="k">weak</span> <span class="k">var</span> <span class="nv">delegate</span><span class="p">:</span> <span class="kt">ChartAnimatorDelegate</span><span class="p">?</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">updateBlock</span><span class="p">:</span> <span class="p">(()</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)?</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">stopBlock</span><span class="p">:</span> <span class="p">(()</span> <span class="o">-></span> <span class="kt">Void</span><span class="p">)?</span></p>
<pre><code><span class="c1">/// the phase that is animated and influences the drawn values on the y-axis</span>
<span class="c1">/// x相位 影响y轴绘值</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">phaseX</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mf">1.0</span>
<span class="c1">/// the phase that is animated and influences the drawn values on the y-axis</span>
<span class="c1">/// y相位 影响y轴绘值</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">phaseY</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mf">1.0</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_startTime</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_displayLink</span><span class="p">:</span> <span class="kt">CADisplayLink</span><span class="o">!</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_xDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_yDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_endTimeX</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_endTimeY</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_endTime</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_enabledX</span><span class="p">:</span> <span class="kt">Bool</span> <span class="o">=</span> <span class="kc">false</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_enabledY</span><span class="p">:</span> <span class="kt">Bool</span> <span class="o">=</span> <span class="kc">false</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_easingX</span><span class="p">:</span> <span class="kt">ChartEasingFunctionBlock</span><span class="p">?</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_easingY</span><span class="p">:</span> <span class="kt">ChartEasingFunctionBlock</span><span class="p">?</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">deinit</span>
<span class="p">{</span>
<span class="nf">stop</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">stop</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_displayLink</span> <span class="o">!=</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_displayLink</span><span class="o">.</span><span class="nf">removeFromRunLoop</span><span class="p">(</span><span class="kt">NSRunLoop</span><span class="o">.</span><span class="nf">mainRunLoop</span><span class="p">(),</span> <span class="nv">forMode</span><span class="p">:</span> <span class="kt">NSRunLoopCommonModes</span><span class="p">)</span>
<span class="n">_displayLink</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="n">_enabledX</span> <span class="o">=</span> <span class="kc">false</span>
<span class="n">_enabledY</span> <span class="o">=</span> <span class="kc">false</span>
<span class="k">if</span> <span class="p">(</span><span class="n">delegate</span> <span class="o">!=</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">delegate</span><span class="o">!.</span><span class="nf">chartAnimatorStopped</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">stopBlock</span> <span class="o">!=</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="nf">stopBlock</span><span class="p">?()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">updateAnimationPhases</span><span class="p">(</span><span class="nv">currentTime</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">elapsedTime</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">currentTime</span> <span class="o">-</span> <span class="n">_startTime</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_enabledX</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">_xDuration</span>
<span class="k">var</span> <span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsedTime</span>
<span class="k">if</span> <span class="p">(</span><span class="n">elapsed</span> <span class="o">&gt;</span> <span class="n">duration</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">elapsed</span> <span class="o">=</span> <span class="n">duration</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_easingX</span> <span class="o">!=</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">phaseX</span> <span class="o">=</span> <span class="nf">_easingX</span><span class="o">!</span><span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="n">elapsed</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="n">duration</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">phaseX</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</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">_enabledY</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">duration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">_yDuration</span>
<span class="k">var</span> <span class="nv">elapsed</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="n">elapsedTime</span>
<span class="k">if</span> <span class="p">(</span><span class="n">elapsed</span> <span class="o">&gt;</span> <span class="n">duration</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">elapsed</span> <span class="o">=</span> <span class="n">duration</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_easingY</span> <span class="o">!=</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">phaseY</span> <span class="o">=</span> <span class="nf">_easingY</span><span class="o">!</span><span class="p">(</span><span class="nv">elapsed</span><span class="p">:</span> <span class="n">elapsed</span><span class="p">,</span> <span class="nv">duration</span><span class="p">:</span> <span class="n">duration</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">phaseY</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="n">elapsed</span> <span class="o">/</span> <span class="n">duration</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">@objc</span> <span class="kd">private</span> <span class="kd">func</span> <span class="nf">animationLoop</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">currentTime</span><span class="p">:</span> <span class="kt">NSTimeInterval</span> <span class="o">=</span> <span class="kt">CACurrentMediaTime</span><span class="p">()</span>
<span class="nf">updateAnimationPhases</span><span class="p">(</span><span class="n">currentTime</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">delegate</span> <span class="o">!=</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">delegate</span><span class="o">!.</span><span class="nf">chartAnimatorUpdated</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">updateBlock</span> <span class="o">!=</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="nf">updateBlock</span><span class="o">!</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">currentTime</span> <span class="o">&gt;=</span> <span class="n">_endTime</span><span class="p">)</span>
<span class="p">{</span>
<span class="nf">stop</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.</span>
<span class="c1">/// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart.</span>
<span class="c1">/// :param: xAxisDuration duration for animating the x axis</span>
<span class="c1">/// :param: yAxisDuration duration for animating the y axis</span>
<span class="c1">/// :param: easingX an easing function for the animation on the x axis</span>
<span class="c1">/// :param: easingY an easing function for the animation on the y axis</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">animate</span><span class="p">(</span><span class="nv">#xAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">easingX</span><span class="p">:</span> <span class="kt">ChartEasingFunctionBlock</span><span class="p">?,</span> <span class="nv">easingY</span><span class="p">:</span> <span class="kt">ChartEasingFunctionBlock</span><span class="p">?)</span>
<span class="p">{</span>
<span class="nf">stop</span><span class="p">()</span>
<span class="n">_displayLink</span> <span class="o">=</span> <span class="kt">CADisplayLink</span><span class="p">(</span><span class="nv">target</span><span class="p">:</span> <span class="k">self</span><span class="p">,</span> <span class="nv">selector</span><span class="p">:</span> <span class="kt">Selector</span><span class="p">(</span><span class="s">"animationLoop"</span><span class="p">))</span>
<span class="n">_startTime</span> <span class="o">=</span> <span class="kt">CACurrentMediaTime</span><span class="p">()</span>
<span class="n">_xDuration</span> <span class="o">=</span> <span class="n">xAxisDuration</span>
<span class="n">_yDuration</span> <span class="o">=</span> <span class="n">yAxisDuration</span>
<span class="n">_endTimeX</span> <span class="o">=</span> <span class="n">_startTime</span> <span class="o">+</span> <span class="n">xAxisDuration</span>
<span class="n">_endTimeY</span> <span class="o">=</span> <span class="n">_startTime</span> <span class="o">+</span> <span class="n">yAxisDuration</span>
<span class="n">_endTime</span> <span class="o">=</span> <span class="n">_endTimeX</span> <span class="o">&gt;</span> <span class="n">_endTimeY</span> <span class="p">?</span> <span class="nv">_endTimeX</span> <span class="p">:</span> <span class="n">_endTimeY</span>
<span class="n">_enabledX</span> <span class="o">=</span> <span class="n">xAxisDuration</span> <span class="o">&gt;</span> <span class="mf">0.0</span>
<span class="n">_enabledY</span> <span class="o">=</span> <span class="n">yAxisDuration</span> <span class="o">&gt;</span> <span class="mf">0.0</span>
<span class="n">_easingX</span> <span class="o">=</span> <span class="n">easingX</span>
<span class="n">_easingY</span> <span class="o">=</span> <span class="n">easingY</span>
<span class="c1">// Take care of the first frame if rendering is already scheduled...</span>
<span class="nf">updateAnimationPhases</span><span class="p">(</span><span class="n">_startTime</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_enabledX</span> <span class="o">||</span> <span class="n">_enabledY</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_displayLink</span><span class="o">.</span><span class="nf">addToRunLoop</span><span class="p">(</span><span class="kt">NSRunLoop</span><span class="o">.</span><span class="nf">mainRunLoop</span><span class="p">(),</span> <span class="nv">forMode</span><span class="p">:</span> <span class="kt">NSRunLoopCommonModes</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.</span>
<span class="c1">/// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart.</span>
<span class="c1">/// :param: xAxisDuration duration for animating the x axis</span>
<span class="c1">/// :param: yAxisDuration duration for animating the y axis</span>
<span class="c1">/// :param: easingOptionX the easing function for the animation on the x axis</span>
<span class="c1">/// :param: easingOptionY the easing function for the animation on the y axis</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">animate</span><span class="p">(</span><span class="nv">#xAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">easingOptionX</span><span class="p">:</span> <span class="kt">ChartEasingOption</span><span class="p">,</span> <span class="nv">easingOptionY</span><span class="p">:</span> <span class="kt">ChartEasingOption</span><span class="p">)</span>
<span class="p">{</span>
<span class="nf">animate</span><span class="p">(</span><span class="nv">xAxisDuration</span><span class="p">:</span> <span class="n">xAxisDuration</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="n">yAxisDuration</span><span class="p">,</span> <span class="nv">easingX</span><span class="p">:</span> <span class="nf">easingFunctionFromOption</span><span class="p">(</span><span class="n">easingOptionX</span><span class="p">),</span> <span class="nv">easingY</span><span class="p">:</span> <span class="nf">easingFunctionFromOption</span><span class="p">(</span><span class="n">easingOptionY</span><span class="p">))</span>
<span class="p">}</span>
<span class="c1">/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.</span>
<span class="c1">/// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart.</span>
<span class="c1">/// :param: xAxisDuration duration for animating the x axis</span>
<span class="c1">/// :param: yAxisDuration duration for animating the y axis</span>
<span class="c1">/// :param: easing an easing function for the animation</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">animate</span><span class="p">(</span><span class="nv">#xAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">easing</span><span class="p">:</span> <span class="kt">ChartEasingFunctionBlock</span><span class="p">?)</span>
<span class="p">{</span>
<span class="nf">animate</span><span class="p">(</span><span class="nv">xAxisDuration</span><span class="p">:</span> <span class="n">xAxisDuration</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="n">yAxisDuration</span><span class="p">,</span> <span class="nv">easingX</span><span class="p">:</span> <span class="n">easing</span><span class="p">,</span> <span class="nv">easingY</span><span class="p">:</span> <span class="n">easing</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.</span>
<span class="c1">/// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart.</span>
<span class="c1">/// :param: xAxisDuration duration for animating the x axis</span>
<span class="c1">/// :param: yAxisDuration duration for animating the y axis</span>
<span class="c1">/// :param: easingOption the easing function for the animation</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">animate</span><span class="p">(</span><span class="nv">#xAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">easingOption</span><span class="p">:</span> <span class="kt">ChartEasingOption</span><span class="p">)</span>
<span class="p">{</span>
<span class="nf">animate</span><span class="p">(</span><span class="nv">xAxisDuration</span><span class="p">:</span> <span class="n">xAxisDuration</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="n">yAxisDuration</span><span class="p">,</span> <span class="nv">easing</span><span class="p">:</span> <span class="nf">easingFunctionFromOption</span><span class="p">(</span><span class="n">easingOption</span><span class="p">))</span>
<span class="p">}</span>
<span class="c1">/// Animates the drawing / rendering of the chart on both x- and y-axis with the specified animation time.</span>
<span class="c1">/// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart.</span>
<span class="c1">/// :param: xAxisDuration duration for animating the x axis</span>
<span class="c1">/// :param: yAxisDuration duration for animating the y axis</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">animate</span><span class="p">(</span><span class="nv">#xAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span>
<span class="p">{</span>
<span class="nf">animate</span><span class="p">(</span><span class="nv">xAxisDuration</span><span class="p">:</span> <span class="n">xAxisDuration</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="n">yAxisDuration</span><span class="p">,</span> <span class="nv">easingOption</span><span class="p">:</span> <span class="o">.</span><span class="kt">EaseInOutSine</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// Animates the drawing / rendering of the chart the x-axis with the specified animation time.</span>
<span class="c1">/// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart.</span>
<span class="c1">/// :param: xAxisDuration duration for animating the x axis</span>
<span class="c1">/// :param: easing an easing function for the animation</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">animate</span><span class="p">(</span><span class="nv">#xAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">easing</span><span class="p">:</span> <span class="kt">ChartEasingFunctionBlock</span><span class="p">?)</span>
<span class="p">{</span>
<span class="nf">animate</span><span class="p">(</span><span class="nv">xAxisDuration</span><span class="p">:</span> <span class="n">xAxisDuration</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span> <span class="nv">easing</span><span class="p">:</span> <span class="n">easing</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// Animates the drawing / rendering of the chart the x-axis with the specified animation time.</span>
<span class="c1">/// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart.</span>
<span class="c1">/// :param: xAxisDuration duration for animating the x axis</span>
<span class="c1">/// :param: easingOption the easing function for the animation</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">animate</span><span class="p">(</span><span class="nv">#xAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">easingOption</span><span class="p">:</span> <span class="kt">ChartEasingOption</span><span class="p">)</span>
<span class="p">{</span>
<span class="nf">animate</span><span class="p">(</span><span class="nv">xAxisDuration</span><span class="p">:</span> <span class="n">xAxisDuration</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span> <span class="nv">easing</span><span class="p">:</span> <span class="nf">easingFunctionFromOption</span><span class="p">(</span><span class="n">easingOption</span><span class="p">))</span>
<span class="p">}</span>
<span class="c1">/// Animates the drawing / rendering of the chart the x-axis with the specified animation time.</span>
<span class="c1">/// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart.</span>
<span class="c1">/// :param: xAxisDuration duration for animating the x axis</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">animate</span><span class="p">(</span><span class="nv">#xAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span>
<span class="p">{</span>
<span class="nf">animate</span><span class="p">(</span><span class="nv">xAxisDuration</span><span class="p">:</span> <span class="n">xAxisDuration</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span> <span class="nv">easingOption</span><span class="p">:</span> <span class="o">.</span><span class="kt">EaseInOutSine</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// Animates the drawing / rendering of the chart the y-axis with the specified animation time.</span>
<span class="c1">/// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart.</span>
<span class="c1">/// :param: yAxisDuration duration for animating the y axis</span>
<span class="c1">/// :param: easing an easing function for the animation</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">animate</span><span class="p">(</span><span class="nv">#yAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">easing</span><span class="p">:</span> <span class="kt">ChartEasingFunctionBlock</span><span class="p">?)</span>
<span class="p">{</span>
<span class="nf">animate</span><span class="p">(</span><span class="nv">xAxisDuration</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="n">yAxisDuration</span><span class="p">,</span> <span class="nv">easing</span><span class="p">:</span> <span class="n">easing</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// Animates the drawing / rendering of the chart the y-axis with the specified animation time.</span>
<span class="c1">/// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart.</span>
<span class="c1">/// :param: yAxisDuration duration for animating the y axis</span>
<span class="c1">/// :param: easingOption the easing function for the animation</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">animate</span><span class="p">(</span><span class="nv">#yAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">,</span> <span class="nv">easingOption</span><span class="p">:</span> <span class="kt">ChartEasingOption</span><span class="p">)</span>
<span class="p">{</span>
<span class="nf">animate</span><span class="p">(</span><span class="nv">xAxisDuration</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="n">yAxisDuration</span><span class="p">,</span> <span class="nv">easing</span><span class="p">:</span> <span class="nf">easingFunctionFromOption</span><span class="p">(</span><span class="n">easingOption</span><span class="p">))</span>
<span class="p">}</span>
<span class="c1">/// Animates the drawing / rendering of the chart the y-axis with the specified animation time.</span>
<span class="c1">/// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart.</span>
<span class="c1">/// :param: yAxisDuration duration for animating the y axis</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">animate</span><span class="p">(</span><span class="nv">#yAxisDuration</span><span class="p">:</span> <span class="kt">NSTimeInterval</span><span class="p">)</span>
<span class="p">{</span>
<span class="nf">animate</span><span class="p">(</span><span class="nv">xAxisDuration</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span> <span class="nv">yAxisDuration</span><span class="p">:</span> <span class="n">yAxisDuration</span><span class="p">,</span> <span class="nv">easingOption</span><span class="p">:</span> <span class="o">.</span><span class="kt">EaseInOutSine</span><span class="p">)</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
项目第三十四天 CombinedChart Data2015-08-08T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/08/08/gpxj-34-day<h3>CombinedChart</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">CombinedChartData</span><span class="p">:</span> <span class="kt">BarLineScatterCandleChartData</span>
<span class="p">{</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv"><em>lineData</span><span class="p">:</span> <span class="kt">LineChartData</span><span class="o">!</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv"></em>barData</span><span class="p">:</span> <span class="kt">BarChartData</span><span class="o">!</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv"><em>scatterData</span><span class="p">:</span> <span class="kt">ScatterChartData</span><span class="o">!</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv"></em>candleData</span><span class="p">:</span> <span class="kt">CandleChartData</span><span class="o">!</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_bubbleData</span><span class="p">:</span> <span class="kt">BubbleChartData</span><span class="o">!</span></p>
<pre><code><span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">?]?,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="n">xVals</span><span class="p">,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="n">dataSets</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">NSObject</span><span class="p">]?,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="n">xVals</span><span class="p">,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="n">dataSets</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">lineData</span><span class="p">:</span> <span class="kt">LineChartData</span><span class="o">!</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_lineData</span>
<span class="p">}</span>
<span class="k">set</span>
<span class="p">{</span>
<span class="n">_lineData</span> <span class="o">=</span> <span class="n">newValue</span>
<span class="k">for</span> <span class="n">dataSet</span> <span class="k">in</span> <span class="n">newValue</span><span class="o">.</span><span class="n">dataSets</span>
<span class="p">{</span>
<span class="n">_dataSets</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">dataSet</span><span class="p">)</span>
<span class="p">}</span>
<span class="nf">checkIsLegal</span><span class="p">(</span><span class="n">newValue</span><span class="o">.</span><span class="n">dataSets</span><span class="p">)</span>
<span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="n">_lastStart</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">_lastEnd</span><span class="p">)</span>
<span class="nf">calcYValueSum</span><span class="p">()</span>
<span class="nf">calcYValueCount</span><span class="p">()</span>
<span class="nf">calcXValAverageLength</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">barData</span><span class="p">:</span> <span class="kt">BarChartData</span><span class="o">!</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_barData</span>
<span class="p">}</span>
<span class="k">set</span>
<span class="p">{</span>
<span class="n">_barData</span> <span class="o">=</span> <span class="n">newValue</span>
<span class="k">for</span> <span class="n">dataSet</span> <span class="k">in</span> <span class="n">newValue</span><span class="o">.</span><span class="n">dataSets</span>
<span class="p">{</span>
<span class="n">_dataSets</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">dataSet</span><span class="p">)</span>
<span class="p">}</span>
<span class="nf">checkIsLegal</span><span class="p">(</span><span class="n">newValue</span><span class="o">.</span><span class="n">dataSets</span><span class="p">)</span>
<span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="n">_lastStart</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">_lastEnd</span><span class="p">)</span>
<span class="nf">calcYValueSum</span><span class="p">()</span>
<span class="nf">calcYValueCount</span><span class="p">()</span>
<span class="nf">calcXValAverageLength</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">scatterData</span><span class="p">:</span> <span class="kt">ScatterChartData</span><span class="o">!</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_scatterData</span>
<span class="p">}</span>
<span class="k">set</span>
<span class="p">{</span>
<span class="n">_scatterData</span> <span class="o">=</span> <span class="n">newValue</span>
<span class="k">for</span> <span class="n">dataSet</span> <span class="k">in</span> <span class="n">newValue</span><span class="o">.</span><span class="n">dataSets</span>
<span class="p">{</span>
<span class="n">_dataSets</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">dataSet</span><span class="p">)</span>
<span class="p">}</span>
<span class="nf">checkIsLegal</span><span class="p">(</span><span class="n">newValue</span><span class="o">.</span><span class="n">dataSets</span><span class="p">)</span>
<span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="n">_lastStart</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">_lastEnd</span><span class="p">)</span>
<span class="nf">calcYValueSum</span><span class="p">()</span>
<span class="nf">calcYValueCount</span><span class="p">()</span>
<span class="nf">calcXValAverageLength</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">candleData</span><span class="p">:</span> <span class="kt">CandleChartData</span><span class="o">!</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_candleData</span>
<span class="p">}</span>
<span class="k">set</span>
<span class="p">{</span>
<span class="n">_candleData</span> <span class="o">=</span> <span class="n">newValue</span>
<span class="k">for</span> <span class="n">dataSet</span> <span class="k">in</span> <span class="n">newValue</span><span class="o">.</span><span class="n">dataSets</span>
<span class="p">{</span>
<span class="n">_dataSets</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">dataSet</span><span class="p">)</span>
<span class="p">}</span>
<span class="nf">checkIsLegal</span><span class="p">(</span><span class="n">newValue</span><span class="o">.</span><span class="n">dataSets</span><span class="p">)</span>
<span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="n">_lastStart</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">_lastEnd</span><span class="p">)</span>
<span class="nf">calcYValueSum</span><span class="p">()</span>
<span class="nf">calcYValueCount</span><span class="p">()</span>
<span class="nf">calcXValAverageLength</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">bubbleData</span><span class="p">:</span> <span class="kt">BubbleChartData</span><span class="o">!</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_bubbleData</span>
<span class="p">}</span>
<span class="k">set</span>
<span class="p">{</span>
<span class="n">_bubbleData</span> <span class="o">=</span> <span class="n">newValue</span>
<span class="k">for</span> <span class="n">dataSet</span> <span class="k">in</span> <span class="n">newValue</span><span class="o">.</span><span class="n">dataSets</span>
<span class="p">{</span>
<span class="n">_dataSets</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">dataSet</span><span class="p">)</span>
<span class="p">}</span>
<span class="nf">checkIsLegal</span><span class="p">(</span><span class="n">newValue</span><span class="o">.</span><span class="n">dataSets</span><span class="p">)</span>
<span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="n">_lastStart</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">_lastEnd</span><span class="p">)</span>
<span class="nf">calcYValueSum</span><span class="p">()</span>
<span class="nf">calcYValueCount</span><span class="p">()</span>
<span class="nf">calcXValAverageLength</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">notifyDataChanged</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_lineData</span> <span class="o">!==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_lineData</span><span class="o">.</span><span class="nf">notifyDataChanged</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_barData</span> <span class="o">!==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_barData</span><span class="o">.</span><span class="nf">notifyDataChanged</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_scatterData</span> <span class="o">!==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_scatterData</span><span class="o">.</span><span class="nf">notifyDataChanged</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_candleData</span> <span class="o">!==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_candleData</span><span class="o">.</span><span class="nf">notifyDataChanged</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_bubbleData</span> <span class="o">!==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_bubbleData</span><span class="o">.</span><span class="nf">notifyDataChanged</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
项目第三十三天 Candle Data2015-08-07T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/08/07/gpxj-33-day<h2>蜡烛图</h2>
<hr />
<ul>
<li>CandleChartDataEntry 蜡烛图 数据条目</li>
<li>CandleChartDataSet 蜡烛图集合</li>
<li>CandleChartData 蜡烛图数据</li>
</ul>
<h3>CandleChartDataEntry</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">CandleChartDataEntry</span><span class="p">:</span> <span class="kt">ChartDataEntry</span>
<span class="p">{</span>
<span class="c1">/// shadow-high value</span>
<span class="c1">// 最高值</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">high</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span></p>
<pre><code><span class="c1">/// shadow-low value</span>
<span class="c1">// 最低值</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">low</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1">/// close value</span>
<span class="c1">// 收盘值</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">close</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1">/// open value</span>
<span class="c1">// 开盘值</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">open</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">xIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">shadowH</span><span class="p">:</span> <span class="kt">Double</span><span class="p">,</span> <span class="nv">shadowL</span><span class="p">:</span> <span class="kt">Double</span><span class="p">,</span> <span class="nv">open</span><span class="p">:</span> <span class="kt">Double</span><span class="p">,</span> <span class="nv">close</span><span class="p">:</span> <span class="kt">Double</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="p">(</span><span class="n">shadowH</span> <span class="o">+</span> <span class="n">shadowL</span><span class="p">)</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">,</span> <span class="nv">xIndex</span><span class="p">:</span> <span class="n">xIndex</span><span class="p">)</span>
<span class="k">self</span><span class="o">.</span><span class="n">high</span> <span class="o">=</span> <span class="n">shadowH</span>
<span class="k">self</span><span class="o">.</span><span class="n">low</span> <span class="o">=</span> <span class="n">shadowL</span>
<span class="k">self</span><span class="o">.</span><span class="n">open</span> <span class="o">=</span> <span class="n">open</span>
<span class="k">self</span><span class="o">.</span><span class="n">close</span> <span class="o">=</span> <span class="n">close</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">xIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">shadowH</span><span class="p">:</span> <span class="kt">Double</span><span class="p">,</span> <span class="nv">shadowL</span><span class="p">:</span> <span class="kt">Double</span><span class="p">,</span> <span class="nv">open</span><span class="p">:</span> <span class="kt">Double</span><span class="p">,</span> <span class="nv">close</span><span class="p">:</span> <span class="kt">Double</span><span class="p">,</span> <span class="nv">data</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="p">(</span><span class="n">shadowH</span> <span class="o">+</span> <span class="n">shadowL</span><span class="p">)</span> <span class="o">/</span> <span class="mf">2.0</span><span class="p">,</span> <span class="nv">xIndex</span><span class="p">:</span> <span class="n">xIndex</span><span class="p">,</span> <span class="nv">data</span><span class="p">:</span> <span class="n">data</span><span class="p">)</span>
<span class="k">self</span><span class="o">.</span><span class="n">high</span> <span class="o">=</span> <span class="n">shadowH</span>
<span class="k">self</span><span class="o">.</span><span class="n">low</span> <span class="o">=</span> <span class="n">shadowL</span>
<span class="k">self</span><span class="o">.</span><span class="n">open</span> <span class="o">=</span> <span class="n">open</span>
<span class="k">self</span><span class="o">.</span><span class="n">close</span> <span class="o">=</span> <span class="n">close</span>
<span class="p">}</span>
<span class="c1">/// Returns the overall range (difference) between shadow-high and shadow-low.</span>
<span class="c1">// 最高值和最低值差值</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">shadowRange</span><span class="p">:</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nf">abs</span><span class="p">(</span><span class="n">high</span> <span class="o">-</span> <span class="n">low</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// Returns the body size (difference between open and close).</span>
<span class="c1">// 开盘与收盘值 之差</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">bodyRange</span><span class="p">:</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nf">abs</span><span class="p">(</span><span class="n">open</span> <span class="o">-</span> <span class="n">close</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// the center value of the candle. (Middle value between high and low)</span>
<span class="c1">// 高低盘中心</span>
<span class="kd">public</span> <span class="k">override</span> <span class="k">var</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">super</span><span class="o">.</span><span class="n">value</span>
<span class="p">}</span>
<span class="k">set</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="p">(</span><span class="n">high</span> <span class="o">+</span> <span class="n">low</span><span class="p">)</span> <span class="o">/</span> <span class="mf">2.0</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// MARK: NSCopying</span>
<span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">copyWithZone</span><span class="p">(</span><span class="nv">zone</span><span class="p">:</span> <span class="kt">NSZone</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">AnyObject</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">copy</span> <span class="o">=</span> <span class="k">super</span><span class="o">.</span><span class="nf">copyWithZone</span><span class="p">(</span><span class="n">zone</span><span class="p">)</span> <span class="k">as!</span> <span class="kt">CandleChartDataEntry</span>
<span class="n">copy</span><span class="o">.</span><span class="n">high</span> <span class="o">=</span> <span class="n">high</span>
<span class="n">copy</span><span class="o">.</span><span class="n">high</span> <span class="o">=</span> <span class="n">low</span>
<span class="n">copy</span><span class="o">.</span><span class="n">high</span> <span class="o">=</span> <span class="n">open</span>
<span class="n">copy</span><span class="o">.</span><span class="n">high</span> <span class="o">=</span> <span class="n">close</span>
<span class="k">return</span> <span class="n">copy</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<h3>CandleChartDataSet</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">CandleChartDataSet</span><span class="p">:</span> <span class="kt">LineScatterCandleChartDataSet</span>
<span class="p">{</span>
<span class="c1">/// the width of the candle-shadow-line in pixels.</span>
<span class="c1">/// :default: 3.0</span>
<span class="c1">// 高低盘值的宽度</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">shadowWidth</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="mf">1.5</span><span class="p">)</span></p>
<pre><code><span class="c1">/// the space between the candle entries</span>
<span class="c1">/// :default: 0.1 (10%)</span>
<span class="c1">// 两个条目之间的距离</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_bodySpace</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
<span class="c1">/// the color of the shadow line</span>
<span class="c1">/// 阴影颜色</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">shadowColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">?</span>
<span class="c1">/// use candle color for the shadow</span>
<span class="c1">// 是否使用 蜡烛图的颜色作为阴影颜色</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">shadowColorSameAsCandle</span> <span class="o">=</span> <span class="kc">false</span>
<span class="c1">/// color for open &lt;= close</span>
<span class="c1">// 跌盘的颜色</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">decreasingColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">?</span>
<span class="c1">/// color for open &gt; close</span>
<span class="c1">// 涨盘的颜色</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">increasingColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">?</span>
<span class="c1">/// Are decreasing values drawn as filled?</span>
<span class="c1">// 跌盘是否填充</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">decreasingFilled</span> <span class="o">=</span> <span class="kc">false</span>
<span class="c1">/// Are increasing values drawn as filled?</span>
<span class="c1">// 涨盘是否填充</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">increasingFilled</span> <span class="o">=</span> <span class="kc">true</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">yVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]?,</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">String</span><span class="p">?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">yVals</span><span class="p">:</span> <span class="n">yVals</span><span class="p">,</span> <span class="nv">label</span><span class="p">:</span> <span class="n">label</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">#start</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">yVals</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="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">entries</span> <span class="o">=</span> <span class="n">yVals</span> <span class="k">as!</span> <span class="p">[</span><span class="kt">CandleChartDataEntry</span><span class="p">]</span>
<span class="k">var</span> <span class="nv">endValue</span> <span class="p">:</span> <span class="kt">Int</span>
<span class="k">if</span> <span class="n">end</span> <span class="o">==</span> <span class="mi">0</span>
<span class="p">{</span>
<span class="n">endValue</span> <span class="o">=</span> <span class="n">entries</span><span class="o">.</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">endValue</span> <span class="o">=</span> <span class="n">end</span>
<span class="p">}</span>
<span class="n">_lastStart</span> <span class="o">=</span> <span class="n">start</span>
<span class="n">_lastEnd</span> <span class="o">=</span> <span class="n">end</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="n">entries</span><span class="p">[</span><span class="n">start</span><span class="p">]</span><span class="o">.</span><span class="n">low</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">entries</span><span class="p">[</span><span class="n">start</span><span class="p">]</span><span class="o">.</span><span class="n">high</span>
<span class="k">for</span> <span class="p">(</span><span class="k">var</span> <span class="nv">i</span> <span class="o">=</span> <span class="n">start</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">endValue</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">e</span> <span class="o">=</span> <span class="n">entries</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">e</span><span class="o">.</span><span class="n">low</span> <span class="o">&lt;</span> <span class="n">_yMin</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">low</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="o">.</span><span class="n">high</span> <span class="o">&gt;</span> <span class="n">_yMax</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">high</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// the space that is left out on the left and right side of each candle,</span>
<span class="c1">/// :default: 0.1 (10%), max 0.45, min 0.0</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">bodySpace</span><span class="p">:</span> <span class="kt">CGFloat</span>
<span class="p">{</span>
<span class="k">set</span>
<span class="p">{</span>
<span class="n">_bodySpace</span> <span class="o">=</span> <span class="n">newValue</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_bodySpace</span> <span class="o">&lt;</span> <span class="mf">0.0</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_bodySpace</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_bodySpace</span> <span class="o">&gt;</span> <span class="mf">0.45</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_bodySpace</span> <span class="o">=</span> <span class="mf">0.45</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_bodySpace</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Is the shadow color same as the candle color?</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">isShadowColorSameAsCandle</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">return</span> <span class="n">shadowColorSameAsCandle</span> <span class="p">}</span>
<span class="c1">/// Are increasing values drawn as filled?</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">isIncreasingFilled</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">return</span> <span class="n">increasingFilled</span><span class="p">;</span> <span class="p">}</span>
<span class="c1">/// Are decreasing values drawn as filled?</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">isDecreasingFilled</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">return</span> <span class="n">decreasingFilled</span><span class="p">;</span> <span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<h3>CandleChartData</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">CandleChartData</span><span class="p">:</span> <span class="kt">BarLineScatterCandleChartData</span>
<span class="p">{</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
<span class="p">}</span></p>
<pre><code><span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">?]?,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="n">xVals</span><span class="p">,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="n">dataSets</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">NSObject</span><span class="p">]?,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="n">xVals</span><span class="p">,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="n">dataSets</span><span class="p">)</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
项目第三十二天 Bar Data2015-08-06T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/08/06/gpxj-32-day<h2>条形图</h2>
<hr />
<ul>
<li>BarChartDataEntry 条形图条目</li>
<li>BarChartDataSet 条形图数据集合</li>
<li>BarChartData 条形图数据</li>
</ul>
<h3>BarChartDataEntry</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">BarChartDataEntry</span><span class="p">:</span> <span class="kt">ChartDataEntry</span>
<span class="p">{</span>
<span class="c1">/// the values the stacked barchart holds</span>
<span class="c1">/// 堆积条形图</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_values</span><span class="p">:</span> <span class="p">[</span><span class="kt">Double</span><span class="p">]?</span></p>
<pre><code><span class="c1">/// the sum of all negative values this entry (if stacked) contains</span>
<span class="c1">// 负数条目之和(如果是堆积条形图)</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_negativeSum</span><span class="p">:</span> <span class="kt">Double</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="c1">/// the sum of all positive values this entry (if stacked) contains</span>
<span class="c1">// 正数条目之和(如果是堆积条形图)</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_positiveSum</span><span class="p">:</span> <span class="kt">Double</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="c1">/// Constructor for stacked bar entries.</span>
<span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">values</span><span class="p">:</span> <span class="p">[</span><span class="kt">Double</span><span class="p">],</span> <span class="nv">xIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="kt">BarChartDataEntry</span><span class="o">.</span><span class="nf">calcSum</span><span class="p">(</span><span class="n">values</span><span class="p">),</span> <span class="nv">xIndex</span><span class="p">:</span> <span class="n">xIndex</span><span class="p">)</span>
<span class="k">self</span><span class="o">.</span><span class="n">values</span> <span class="o">=</span> <span class="n">values</span>
<span class="nf">calcPosNegSum</span><span class="p">()</span>
<span class="p">}</span>
<span class="c1">/// Constructor for normal bars (not stacked).</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="kt">Double</span><span class="p">,</span> <span class="nv">xIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="n">value</span><span class="p">,</span> <span class="nv">xIndex</span><span class="p">:</span> <span class="n">xIndex</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// Constructor for stacked bar entries.</span>
<span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">values</span><span class="p">:</span> <span class="p">[</span><span class="kt">Double</span><span class="p">],</span> <span class="nv">xIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="kt">BarChartDataEntry</span><span class="o">.</span><span class="nf">calcSum</span><span class="p">(</span><span class="n">values</span><span class="p">),</span> <span class="nv">xIndex</span><span class="p">:</span> <span class="n">xIndex</span><span class="p">,</span> <span class="nv">data</span><span class="p">:</span> <span class="n">label</span><span class="p">)</span>
<span class="k">self</span><span class="o">.</span><span class="n">values</span> <span class="o">=</span> <span class="n">values</span>
<span class="p">}</span>
<span class="c1">/// Constructor for normal bars (not stacked).</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="kt">Double</span><span class="p">,</span> <span class="nv">xIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">data</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="n">value</span><span class="p">,</span> <span class="nv">xIndex</span><span class="p">:</span> <span class="n">xIndex</span><span class="p">,</span> <span class="nv">data</span><span class="p">:</span> <span class="n">data</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">getBelowSum</span><span class="p">(</span><span class="nv">stackIndex</span> <span class="p">:</span><span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">values</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mi">0</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">remainder</span><span class="p">:</span> <span class="kt">Double</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="k">var</span> <span class="nv">index</span> <span class="o">=</span> <span class="n">values</span><span class="o">!.</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span>
<span class="k">while</span> <span class="p">(</span><span class="n">index</span> <span class="o">&gt;</span> <span class="n">stackIndex</span> <span class="o">&amp;&amp;</span> <span class="n">index</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">remainder</span> <span class="o">+=</span> <span class="n">values</span><span class="o">!</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
<span class="n">index</span><span class="o">--</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">remainder</span>
<span class="p">}</span>
<span class="c1">/// :returns: the sum of all negative values this entry (if stacked) contains. (this is a positive number)</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">negativeSum</span><span class="p">:</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_negativeSum</span>
<span class="p">}</span>
<span class="c1">/// :returns: the sum of all positive values this entry (if stacked) contains.</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">positiveSum</span><span class="p">:</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_positiveSum</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">calcPosNegSum</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">if</span> <span class="n">_values</span> <span class="o">==</span> <span class="kc">nil</span>
<span class="p">{</span>
<span class="n">_positiveSum</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">_negativeSum</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">sumNeg</span><span class="p">:</span> <span class="kt">Double</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="k">var</span> <span class="nv">sumPos</span><span class="p">:</span> <span class="kt">Double</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="k">for</span> <span class="n">f</span> <span class="k">in</span> <span class="n">_values</span><span class="o">!</span>
<span class="p">{</span>
<span class="k">if</span> <span class="n">f</span> <span class="o">&lt;</span> <span class="mf">0.0</span>
<span class="p">{</span>
<span class="n">sumNeg</span> <span class="o">+=</span> <span class="o">-</span><span class="n">f</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">sumPos</span> <span class="o">+=</span> <span class="n">f</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">_negativeSum</span> <span class="o">=</span> <span class="n">sumNeg</span>
<span class="n">_positiveSum</span> <span class="o">=</span> <span class="n">sumPos</span>
<span class="p">}</span>
<span class="c1">// MARK: Accessors</span>
<span class="c1">/// the values the stacked barchart holds</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">isStacked</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_values</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">}</span>
<span class="c1">/// the values the stacked barchart holds</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">values</span><span class="p">:</span> <span class="p">[</span><span class="kt">Double</span><span class="p">]?</span>
<span class="p">{</span>
<span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="k">self</span><span class="o">.</span><span class="n">_values</span> <span class="p">}</span>
<span class="k">set</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="kt">BarChartDataEntry</span><span class="o">.</span><span class="nf">calcSum</span><span class="p">(</span><span class="n">newValue</span><span class="p">)</span>
<span class="k">self</span><span class="o">.</span><span class="n">_values</span> <span class="o">=</span> <span class="n">newValue</span>
<span class="nf">calcPosNegSum</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// MARK: NSCopying</span>
<span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">copyWithZone</span><span class="p">(</span><span class="nv">zone</span><span class="p">:</span> <span class="kt">NSZone</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">AnyObject</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">copy</span> <span class="o">=</span> <span class="k">super</span><span class="o">.</span><span class="nf">copyWithZone</span><span class="p">(</span><span class="n">zone</span><span class="p">)</span> <span class="k">as!</span> <span class="kt">BarChartDataEntry</span>
<span class="n">copy</span><span class="o">.</span><span class="n">_values</span> <span class="o">=</span> <span class="n">_values</span>
<span class="n">copy</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
<span class="n">copy</span><span class="o">.</span><span class="n">_negativeSum</span> <span class="o">=</span> <span class="n">_negativeSum</span>
<span class="k">return</span> <span class="n">copy</span>
<span class="p">}</span>
<span class="c1">/// Calculates the sum across all values of the given stack.</span>
<span class="c1">///</span>
<span class="c1">/// :param: vals</span>
<span class="c1">/// :returns:</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">func</span> <span class="nf">calcSum</span><span class="p">(</span><span class="nv">vals</span><span class="p">:</span> <span class="p">[</span><span class="kt">Double</span><span class="p">]?)</span> <span class="o">-&gt;</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">if</span> <span class="n">vals</span> <span class="o">==</span> <span class="kc">nil</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">0.0</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">sum</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="k">for</span> <span class="n">f</span> <span class="k">in</span> <span class="n">vals</span><span class="o">!</span>
<span class="p">{</span>
<span class="n">sum</span> <span class="o">+=</span> <span class="n">f</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">sum</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<h3>BarChartDataSet</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">BarChartDataSet</span><span class="p">:</span> <span class="kt">BarLineScatterCandleChartDataSet</span>
<span class="p">{</span>
<span class="c1">/// space indicator between the bars in percentage of the whole width of one value (0.15 == 15% of bar width)</span>
<span class="c1">/// Bar之间的间距,相对于整个Bar的百分比</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">barSpace</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mf">0.15</span></p>
<pre><code><span class="c1">/// the maximum number of bars that are stacked upon each other, this value</span>
<span class="c1">/// is calculated from the Entries that are added to the DataSet</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_stackSize</span> <span class="o">=</span> <span class="mi">1</span>
<span class="c1">/// the color used for drawing the bar-shadows. The bar shadows is a surface behind the bar that indicates the maximum value</span>
<span class="c1">/// Bar 阴影颜色</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">barShadowColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="p">(</span><span class="nv">red</span><span class="p">:</span> <span class="mf">215.0</span><span class="o">/</span><span class="mf">255.0</span><span class="p">,</span> <span class="nv">green</span><span class="p">:</span> <span class="mf">215.0</span><span class="o">/</span><span class="mf">255.0</span><span class="p">,</span> <span class="nv">blue</span><span class="p">:</span> <span class="mf">215.0</span><span class="o">/</span><span class="mf">255.0</span><span class="p">,</span> <span class="nv">alpha</span><span class="p">:</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="c1">/// the alpha value (transparency) that is used for drawing the highlight indicator bar. min = 0.0 (fully transparent), max = 1.0 (fully opaque)</span>
<span class="c1">/// 标识 高亮 的透明度</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">highLightAlpha</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="mf">120.0</span> <span class="o">/</span> <span class="mf">255.0</span><span class="p">)</span>
<span class="c1">/// the overall entry count, including counting each stack-value individually</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_entryCountStacks</span> <span class="o">=</span> <span class="mi">0</span>
<span class="c1">/// array of labels used to describe the different values of the stacked bars</span>
<span class="c1">// 堆积条形图标签数组</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">stackLabels</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">"Stack"</span><span class="p">]</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">yVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]?,</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">String</span><span class="p">?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">yVals</span><span class="p">:</span> <span class="n">yVals</span><span class="p">,</span> <span class="nv">label</span><span class="p">:</span> <span class="n">label</span><span class="p">)</span>
<span class="k">self</span><span class="o">.</span><span class="n">highlightColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">blackColor</span><span class="p">()</span>
<span class="k">self</span><span class="o">.</span><span class="nf">calcStackSize</span><span class="p">(</span><span class="n">yVals</span> <span class="k">as!</span> <span class="p">[</span><span class="kt">BarChartDataEntry</span><span class="p">]?)</span>
<span class="k">self</span><span class="o">.</span><span class="nf">calcEntryCountIncludingStacks</span><span class="p">(</span><span class="n">yVals</span> <span class="k">as!</span> <span class="p">[</span><span class="kt">BarChartDataEntry</span><span class="p">]?)</span>
<span class="p">}</span>
<span class="c1">// MARK: NSCopying</span>
<span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">copyWithZone</span><span class="p">(</span><span class="nv">zone</span><span class="p">:</span> <span class="kt">NSZone</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">AnyObject</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">copy</span> <span class="o">=</span> <span class="k">super</span><span class="o">.</span><span class="nf">copyWithZone</span><span class="p">(</span><span class="n">zone</span><span class="p">)</span> <span class="k">as!</span> <span class="kt">BarChartDataSet</span>
<span class="n">copy</span><span class="o">.</span><span class="n">barSpace</span> <span class="o">=</span> <span class="n">barSpace</span>
<span class="n">copy</span><span class="o">.</span><span class="n">_stackSize</span> <span class="o">=</span> <span class="n">_stackSize</span>
<span class="n">copy</span><span class="o">.</span><span class="n">barShadowColor</span> <span class="o">=</span> <span class="n">barShadowColor</span>
<span class="n">copy</span><span class="o">.</span><span class="n">highLightAlpha</span> <span class="o">=</span> <span class="n">highLightAlpha</span>
<span class="n">copy</span><span class="o">.</span><span class="n">_entryCountStacks</span> <span class="o">=</span> <span class="n">_entryCountStacks</span>
<span class="n">copy</span><span class="o">.</span><span class="n">stackLabels</span> <span class="o">=</span> <span class="n">stackLabels</span>
<span class="k">return</span> <span class="n">copy</span>
<span class="p">}</span>
<span class="c1">/// Calculates the total number of entries this DataSet represents, including</span>
<span class="c1">/// stacks. All values belonging to a stack are calculated separately.</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">calcEntryCountIncludingStacks</span><span class="p">(</span><span class="nv">yVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">BarChartDataEntry</span><span class="p">]</span><span class="o">!</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_entryCountStacks</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">yVals</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">vals</span> <span class="o">=</span> <span class="n">yVals</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">values</span>
<span class="k">if</span> <span class="p">(</span><span class="n">vals</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_entryCountStacks</span><span class="o">++</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">_entryCountStacks</span> <span class="o">+=</span> <span class="n">vals</span><span class="o">!.</span><span class="n">count</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// calculates the maximum stacksize that occurs in the Entries array of this DataSet</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">calcStackSize</span><span class="p">(</span><span class="nv">yVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">BarChartDataEntry</span><span class="p">]</span><span class="o">!</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">yVals</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">vals</span> <span class="o">=</span> <span class="n">yVals</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">values</span>
<span class="p">{</span>
<span class="k">if</span> <span class="n">vals</span><span class="o">.</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">_stackSize</span>
<span class="p">{</span>
<span class="n">_stackSize</span> <span class="o">=</span> <span class="n">vals</span><span class="o">.</span><span class="n">count</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">#start</span> <span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">let</span> <span class="nv">yValCount</span> <span class="o">=</span> <span class="n">_yVals</span><span class="o">.</span><span class="n">count</span>
<span class="k">if</span> <span class="n">yValCount</span> <span class="o">==</span> <span class="mi">0</span>
<span class="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">endValue</span> <span class="p">:</span> <span class="kt">Int</span>
<span class="k">if</span> <span class="n">end</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">end</span> <span class="o">&gt;=</span> <span class="n">yValCount</span>
<span class="p">{</span>
<span class="n">endValue</span> <span class="o">=</span> <span class="n">yValCount</span> <span class="o">-</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">endValue</span> <span class="o">=</span> <span class="n">end</span>
<span class="p">}</span>
<span class="n">_lastStart</span> <span class="o">=</span> <span class="n">start</span>
<span class="n">_lastEnd</span> <span class="o">=</span> <span class="n">endValue</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="kt">DBL_MAX</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="o">-</span><span class="kt">DBL_MAX</span>
<span class="k">for</span> <span class="p">(</span><span class="k">var</span> <span class="nv">i</span> <span class="o">=</span> <span class="n">start</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">endValue</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="k">let</span> <span class="nv">e</span> <span class="o">=</span> <span class="n">_yVals</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">BarChartDataEntry</span>
<span class="p">{</span>
<span class="k">if</span> <span class="o">!</span><span class="n">e</span><span class="o">.</span><span class="n">value</span><span class="o">.</span><span class="n">isNaN</span>
<span class="p">{</span>
<span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">values</span> <span class="o">==</span> <span class="kc">nil</span>
<span class="p">{</span>
<span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span> <span class="o">&lt;</span> <span class="n">_yMin</span>
<span class="p">{</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span> <span class="o">&gt;</span> <span class="n">_yMax</span>
<span class="p">{</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">if</span> <span class="o">-</span><span class="n">e</span><span class="o">.</span><span class="n">negativeSum</span> <span class="o">&lt;</span> <span class="n">_yMin</span>
<span class="p">{</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="o">-</span><span class="n">e</span><span class="o">.</span><span class="n">negativeSum</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">positiveSum</span> <span class="o">&gt;</span> <span class="n">_yMax</span>
<span class="p">{</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">positiveSum</span>
<span class="p">}</span>
<span class="p">}</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">_yMin</span> <span class="o">==</span> <span class="kt">DBL_MAX</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Returns the maximum number of bars that can be stacked upon another in this DataSet.</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">stackSize</span><span class="p">:</span> <span class="kt">Int</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_stackSize</span>
<span class="p">}</span>
<span class="c1">/// Returns true if this DataSet is stacked (stacksize &gt; 1) or not.</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">isStacked</span><span class="p">:</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_stackSize</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="p">?</span> <span class="nv">true</span> <span class="p">:</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="c1">/// returns the overall entry count, including counting each stack-value individually</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">entryCountStacks</span><span class="p">:</span> <span class="kt">Int</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_entryCountStacks</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<h3>BarChartData</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">BarChartData</span><span class="p">:</span> <span class="kt">BarLineScatterCandleChartData</span>
<span class="p">{</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
<span class="p">}</span></p>
<pre><code><span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">?]?,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="n">xVals</span><span class="p">,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="n">dataSets</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">NSObject</span><span class="p">]?,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="n">xVals</span><span class="p">,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="n">dataSets</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// 组之间的距离</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_groupSpace</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="mf">0.8</span><span class="p">)</span>
<span class="c1">/// The spacing is relative to a full bar width</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">groupSpace</span><span class="p">:</span> <span class="kt">CGFloat</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="mf">0.0</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">_groupSpace</span>
<span class="p">}</span>
<span class="k">set</span>
<span class="p">{</span>
<span class="n">_groupSpace</span> <span class="o">=</span> <span class="n">newValue</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Returns true if this BarData object contains grouped DataSets (more than 1 DataSet).</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">isGrouped</span><span class="p">:</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span> <span class="o">&gt;</span> <span class="mi">1</span> <span class="p">?</span> <span class="nv">true</span> <span class="p">:</span> <span class="kc">false</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
项目第三十一天 BarLineScatterCandleChart Data2015-08-05T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/08/05/gpxj-31-day<h3>BarLineScatterCandleChartDataSet</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">BarLineScatterCandleChartDataSet</span><span class="p">:</span> <span class="kt">ChartDataSet</span>
<span class="p">{</span>
<span class="c1">// 高亮颜色</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">highlightColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="p">(</span><span class="nv">red</span><span class="p">:</span> <span class="mf">255.0</span><span class="o">/</span><span class="mf">255.0</span><span class="p">,</span> <span class="nv">green</span><span class="p">:</span> <span class="mf">187.0</span><span class="o">/</span><span class="mf">255.0</span><span class="p">,</span> <span class="nv">blue</span><span class="p">:</span> <span class="mf">115.0</span><span class="o">/</span><span class="mf">255.0</span><span class="p">,</span> <span class="nv">alpha</span><span class="p">:</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="c1">// 高亮宽度</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">highlightLineWidth</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)</span>
<span class="c1">// 高亮虚线</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">highlightLineDashPhase</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1">// 虚线长度集合</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">highlightLineDashLengths</span><span class="p">:</span> <span class="p">[</span><span class="kt">CGFloat</span><span class="p">]?</span></p>
<pre><code><span class="c1">// MARK: NSCopying</span>
<span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">copyWithZone</span><span class="p">(</span><span class="nv">zone</span><span class="p">:</span> <span class="kt">NSZone</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">AnyObject</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">copy</span> <span class="o">=</span> <span class="k">super</span><span class="o">.</span><span class="nf">copyWithZone</span><span class="p">(</span><span class="n">zone</span><span class="p">)</span> <span class="k">as!</span> <span class="kt">BarLineScatterCandleChartDataSet</span>
<span class="n">copy</span><span class="o">.</span><span class="n">highlightColor</span> <span class="o">=</span> <span class="n">highlightColor</span>
<span class="n">copy</span><span class="o">.</span><span class="n">highlightLineWidth</span> <span class="o">=</span> <span class="n">highlightLineWidth</span>
<span class="n">copy</span><span class="o">.</span><span class="n">highlightLineDashPhase</span> <span class="o">=</span> <span class="n">highlightLineDashPhase</span>
<span class="n">copy</span><span class="o">.</span><span class="n">highlightLineDashLengths</span> <span class="o">=</span> <span class="n">highlightLineDashLengths</span>
<span class="k">return</span> <span class="n">copy</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<h3>BarLineScatterCandleChartData</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">BarLineScatterCandleChartData</span><span class="p">:</span> <span class="kt">ChartData</span>
<span class="p">{</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
<span class="p">}</span></p>
<pre><code><span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">?]?,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="n">xVals</span><span class="p">,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="n">dataSets</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">NSObject</span><span class="p">]?,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="n">xVals</span><span class="p">,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="n">dataSets</span><span class="p">)</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
项目第三十天 Line Data2015-08-04T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/08/04/gpxj-30-day<h2>线数据</h2>
<hr />
<ul>
<li>LineChartDataSet</li>
</ul>
<h3>LineChartDataSet</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">class</span> <span class="kt">LineChartDataSet</span><span class="p">:</span> <span class="kt">LineRadarChartDataSet</span>
<span class="p">{</span>
<span class="c1">// 圆圈颜色</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">circleColors</span> <span class="o">=</span> <span class="p"><a href=""></span><span class="kt">UIColor</span><span class="p"></a></span>
<span class="c1">// 小孔颜色</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">circleHoleColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">whiteColor</span><span class="p">()</span>
<span class="c1">// 圆圈半径</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">circleRadius</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="mf">8.0</span><span class="p">)</span></p>
<pre><code><span class="c1">// 立方强度</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_cubicIntensity</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="mf">0.2</span><span class="p">)</span>
<span class="c1">// 虚线</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">lineDashPhase</span> <span class="o">=</span> <span class="kt">CGFloat</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1">// 虚线长度</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">lineDashLengths</span><span class="p">:</span> <span class="p">[</span><span class="kt">CGFloat</span><span class="p">]</span><span class="o">!</span>
<span class="c1">/// if true, drawing circles is enabled</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">drawCirclesEnabled</span> <span class="o">=</span> <span class="kc">true</span>
<span class="c1">/// if true, cubic lines are drawn instead of linear</span>
<span class="c1">/// cubic-bezier 贝塞尔曲线</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">drawCubicEnabled</span> <span class="o">=</span> <span class="kc">false</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">drawCircleHoleEnabled</span> <span class="o">=</span> <span class="kc">true</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
项目第二十九天 chart 数据2015-08-03T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/08/03/gpxj-29-day<h2>Data</h2>
<hr />
<ul>
<li>ChartDataEntry 条目 (一个x下标,一个y值)</li>
<li>ChartDataSet 数据集合 (一系列的条目)</li>
<li>ChartData 数据 (多个数据集)</li>
</ul>
<h3>ChartDataEntry.swift</h3>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//</span>
<span class="c1">// ChartDataEntry.swift</span>
<span class="c1">// Charts</span>
<span class="c1">//</span>
<span class="c1">// Created by Daniel Cohen Gindi on 23/2/15.</span></p>
<p><span class="c1">//</span>
<span class="c1">// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda</span>
<span class="c1">// A port of MPAndroidChart for iOS</span>
<span class="c1">// Licensed under Apache License 2.0</span>
<span class="c1">//</span>
<span class="c1">// https://github.com/danielgindi/ios-charts</span>
<span class="c1">//</span></p>
<p><span class="kd">import</span> <span class="kt">Foundation</span></p>
<p><span class="c1">// Swift标准库定义了一个Equatable协议,要求任何遵循的类型实现等式 == 和不等式 != 对两个该类型进行比较。所有的Swift标准类型自动支持Equatable协议</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="kt">ChartDataEntry</span><span class="p">:</span> <span class="kt">NSObject</span><span class="p">,</span> <span class="kt">Equatable</span>
<span class="p">{</span>
<span class="c1">/// the actual value (y axis)</span>
<span class="c1">/// y值</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">value</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span></p>
<pre><code><span class="c1">/// the index on the x-axis</span>
<span class="c1">/// x坐标值</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">xIndex</span> <span class="o">=</span> <span class="kt">Int</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="c1">/// optional spot for additional data this Entry represents</span>
<span class="c1">/// 条目 额外可选数据</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">data</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">?</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</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="nv">value</span><span class="p">:</span> <span class="kt">Double</span><span class="p">,</span> <span class="nv">xIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</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="k">self</span><span class="o">.</span><span class="n">xIndex</span> <span class="o">=</span> <span class="n">xIndex</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="kt">Double</span><span class="p">,</span> <span class="nv">xIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">data</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</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="k">self</span><span class="o">.</span><span class="n">xIndex</span> <span class="o">=</span> <span class="n">xIndex</span>
<span class="k">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">data</span>
<span class="p">}</span>
<span class="c1">// MARK: NSObject</span>
<span class="c1">// objective-c : #pragram mark - NSObject</span>
<span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">isEqual</span><span class="p">(</span><span class="nv">object</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">object</span> <span class="o">===</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">object</span><span class="o">!.</span><span class="nf">isKindOfClass</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="k">dynamicType</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">object</span><span class="o">!.</span><span class="n">data</span> <span class="o">!==</span> <span class="n">data</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">object</span><span class="o">!.</span><span class="n">data</span><span class="o">.</span><span class="nf">isEqual</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">data</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">object</span><span class="o">!.</span><span class="n">xIndex</span> <span class="o">!=</span> <span class="n">xIndex</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nf">fabs</span><span class="p">(</span><span class="n">object</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="o">&gt;</span> <span class="mf">0.00001</span><span class="p">)</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="c1">// MARK: NSObject</span>
<span class="c1">// toString</span>
<span class="kd">public</span> <span class="k">override</span> <span class="k">var</span> <span class="nv">description</span><span class="p">:</span> <span class="kt">String</span>
<span class="p">{</span>
<span class="k">return</span> <span class="s">"ChartDataEntry, xIndex: </span><span class="se">\(</span><span class="n">xIndex</span><span class="se">)</span><span class="s">, value </span><span class="se">\(</span><span class="n">value</span><span class="se">)</span><span class="s">"</span>
<span class="p">}</span>
<span class="c1">// MARK: NSCopying</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">copyWithZone</span><span class="p">(</span><span class="nv">zone</span><span class="p">:</span> <span class="kt">NSZone</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">AnyObject</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">copy</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="k">dynamicType</span><span class="o">.</span><span class="nf">allocWithZone</span><span class="p">(</span><span class="n">zone</span><span class="p">)</span> <span class="k">as</span> <span class="kt">ChartDataEntry</span>
<span class="n">copy</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
<span class="n">copy</span><span class="o">.</span><span class="n">xIndex</span> <span class="o">=</span> <span class="n">xIndex</span>
<span class="n">copy</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">data</span>
<span class="k">return</span> <span class="n">copy</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></p>
<p><span class="kd">public</span> <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">ChartDataEntry</span><span class="p">,</span> <span class="nv">rhs</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">lhs</span> <span class="o">===</span> <span class="n">rhs</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">true</span>
<span class="p">}</span></p>
<pre><code><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">lhs</span><span class="o">.</span><span class="nf">isKindOfClass</span><span class="p">(</span><span class="n">rhs</span><span class="o">.</span><span class="k">dynamicType</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">lhs</span><span class="o">.</span><span class="n">data</span> <span class="o">!==</span> <span class="n">rhs</span><span class="o">.</span><span class="n">data</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">lhs</span><span class="o">.</span><span class="n">data</span><span class="o">!.</span><span class="nf">isEqual</span><span class="p">(</span><span class="n">rhs</span><span class="o">.</span><span class="n">data</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">lhs</span><span class="o">.</span><span class="n">xIndex</span> <span class="o">!=</span> <span class="n">rhs</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nf">fabs</span><span class="p">(</span><span class="n">lhs</span><span class="o">.</span><span class="n">value</span> <span class="o">-</span> <span class="n">rhs</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mf">0.00001</span><span class="p">)</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>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<h3>ChartDataSet</h3>
<p>Swift 提供了三种不同的访问级别。这些访问级别相对于源文件中定义的实体,同时也相对于这些源文件所属的模块。</p>
<ol>
<li><strong>Public</strong>:可以访问自己模块或应用中源文件里的任何实体,别人也可以访问引入该模块中源文件里的所有实体。通常情况下,某个接口或 Framework 是可以被任何人使用时,你可以将其设置为 public 级别。</li>
<li><strong>Internal</strong>:可以访问自己模块或应用中源文件里的任何实体,但是别人不能访问该模块中源文件里的实体。通常情况下,某个接口或 Framework 作为内部结构使用时,你可以将其设置为 internal 级别。</li>
<li><strong>Private</strong>:只能在当前源文件中使用的实体,称为私有实体。使用 private 级别,可以用作隐藏某些功能的实现细节。</li>
</ol>
<p>Public 为最高级访问级别,Private 为最低级访问级别。</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//</span>
<span class="c1">// ChartDataSet.swift</span>
<span class="c1">// Charts</span>
<span class="c1">//</span>
<span class="c1">// Created by Daniel Cohen Gindi on 23/2/15.</span></p>
<p><span class="c1">//</span>
<span class="c1">// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda</span>
<span class="c1">// A port of MPAndroidChart for iOS</span>
<span class="c1">// Licensed under Apache License 2.0</span>
<span class="c1">//</span>
<span class="c1">// https://github.com/danielgindi/ios-charts</span>
<span class="c1">//</span></p>
<p><span class="kd">import</span> <span class="kt">Foundation</span>
<span class="kd">import</span> <span class="kt">UIKit</span></p>
<p><span class="kd">public</span> <span class="kd">class</span> <span class="kt">ChartDataSet</span><span class="p">:</span> <span class="kt">NSObject</span>
<span class="p">{</span>
<span class="c1">// 颜色,循环使用</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">colors</span> <span class="o">=</span> <span class="p"><a href=""></span><span class="kt">UIColor</span><span class="p"></a></span>
<span class="c1">// y值集合</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv"><em>yVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]</span><span class="o">!</span>
<span class="c1">// y值最大值</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv"></em>yMax</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1">// y值最小值</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv"><em>yMin</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1">// y值总和</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv"></em>yValueSum</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span></p>
<pre><code><span class="c1">/// the last start value used for calcMinMax</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv">_lastStart</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">0</span>
<span class="c1">/// the last end value used for calcMinMax</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv">_lastEnd</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">0</span>
<span class="c1">// 标签</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span> <span class="o">=</span> <span class="s">"DataSet"</span>
<span class="c1">// 可用</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">visible</span> <span class="o">=</span> <span class="kc">true</span>
<span class="c1">// 是否绘画y值</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">drawValuesEnabled</span> <span class="o">=</span> <span class="kc">true</span>
<span class="c1">/// the color used for the value-text</span>
<span class="c1">/// y值文字的种颜色</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">valueTextColor</span><span class="p">:</span> <span class="kt">UIColor</span> <span class="o">=</span> <span class="kt">UIColor</span><span class="o">.</span><span class="nf">blackColor</span><span class="p">()</span>
<span class="c1">/// the font for the value-text labels</span>
<span class="n">y值字体</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">valueFont</span><span class="p">:</span> <span class="kt">UIFont</span> <span class="o">=</span> <span class="kt">UIFont</span><span class="o">.</span><span class="nf">systemFontOfSize</span><span class="p">(</span><span class="mf">7.0</span><span class="p">)</span>
<span class="c1">/// the formatter used to customly format the values</span>
<span class="c1">// 自定义格式化</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">valueFormatter</span><span class="p">:</span> <span class="kt">NSNumberFormatter</span><span class="p">?</span>
<span class="c1">/// the axis this DataSet should be plotted against.</span>
<span class="c1">// 根据哪个y坐标, 左?右?</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">axisDependency</span> <span class="o">=</span> <span class="kt">ChartYAxis</span><span class="o">.</span><span class="kt">AxisDependency</span><span class="o">.</span><span class="kt">Left</span>
<span class="c1">// 外部可访问属性</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">yVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_yVals</span> <span class="p">}</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">yValueSum</span><span class="p">:</span> <span class="kt">Double</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_yValueSum</span> <span class="p">}</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">yMin</span><span class="p">:</span> <span class="kt">Double</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_yMin</span> <span class="p">}</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">yMax</span><span class="p">:</span> <span class="kt">Double</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_yMax</span> <span class="p">}</span>
<span class="c1">/// if true, value highlighting is enabled</span>
<span class="c1">// 能否高亮</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">highlightEnabled</span> <span class="o">=</span> <span class="kc">true</span>
<span class="c1">/// :returns: true if value highlighting is enabled for this dataset</span>
<span class="c1">// 是否高亮状态</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">isHighlightEnabled</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">return</span> <span class="n">highlightEnabled</span> <span class="p">}</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</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="nv">yVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]?,</span> <span class="nv">label</span><span class="p">:</span> <span class="kt">String</span><span class="p">?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
<span class="k">self</span><span class="o">.</span><span class="n">label</span> <span class="o">=</span> <span class="n">label</span>
<span class="n">_yVals</span> <span class="o">=</span> <span class="n">yVals</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">?</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]()</span> <span class="p">:</span> <span class="n">yVals</span>
<span class="c1">// default color</span>
<span class="n">colors</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="kt">UIColor</span><span class="p">(</span><span class="nv">red</span><span class="p">:</span> <span class="mf">140.0</span><span class="o">/</span><span class="mf">255.0</span><span class="p">,</span> <span class="nv">green</span><span class="p">:</span> <span class="mf">234.0</span><span class="o">/</span><span class="mf">255.0</span><span class="p">,</span> <span class="nv">blue</span><span class="p">:</span> <span class="mf">255.0</span><span class="o">/</span><span class="mf">255.0</span><span class="p">,</span> <span class="nv">alpha</span><span class="p">:</span> <span class="mf">1.0</span><span class="p">))</span>
<span class="k">self</span><span class="o">.</span><span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="n">_lastStart</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">_lastEnd</span><span class="p">)</span>
<span class="k">self</span><span class="o">.</span><span class="nf">calcYValueSum</span><span class="p">()</span>
<span class="p">}</span>
<span class="c1">// 便利构造方法</span>
<span class="kd">public</span> <span class="n">convenience</span> <span class="nf">init</span><span class="p">(</span><span class="nv">yVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataEntry</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="nv">yVals</span><span class="p">:</span> <span class="n">yVals</span><span class="p">,</span> <span class="nv">label</span><span class="p">:</span> <span class="s">"DataSet"</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// Use this method to tell the data set that the underlying data has changed</span>
<span class="c1">// 当数据改变时调用</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">notifyDataSetChanged</span><span class="p">()</span>
<span class="p">{</span>
<span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="n">_lastStart</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">_lastEnd</span><span class="p">)</span>
<span class="nf">calcYValueSum</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">func</span> <span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">#start</span> <span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">let</span> <span class="nv">yValCount</span> <span class="o">=</span> <span class="n">_yVals</span><span class="o">.</span><span class="n">count</span>
<span class="k">if</span> <span class="n">yValCount</span> <span class="o">==</span> <span class="mi">0</span>
<span class="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">endValue</span> <span class="p">:</span> <span class="kt">Int</span>
<span class="k">if</span> <span class="n">end</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">end</span> <span class="o">&gt;=</span> <span class="n">yValCount</span>
<span class="p">{</span>
<span class="n">endValue</span> <span class="o">=</span> <span class="n">yValCount</span> <span class="o">-</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">endValue</span> <span class="o">=</span> <span class="n">end</span>
<span class="p">}</span>
<span class="n">_lastStart</span> <span class="o">=</span> <span class="n">start</span>
<span class="n">_lastEnd</span> <span class="o">=</span> <span class="n">endValue</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="kt">DBL_MAX</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="o">-</span><span class="kt">DBL_MAX</span>
<span class="k">for</span> <span class="p">(</span><span class="k">var</span> <span class="nv">i</span> <span class="o">=</span> <span class="n">start</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">endValue</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">let</span> <span class="nv">e</span> <span class="o">=</span> <span class="n">_yVals</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="o">!</span><span class="n">e</span><span class="o">.</span><span class="n">value</span><span class="o">.</span><span class="n">isNaN</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="o">.</span><span class="n">value</span> <span class="o">&lt;</span> <span class="n">_yMin</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="o">.</span><span class="n">value</span> <span class="o">&gt;</span> <span class="n">_yMax</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</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">_yMin</span> <span class="o">==</span> <span class="kt">DBL_MAX</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">calcYValueSum</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">_yValueSum</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="k">var</span> <span class="nv">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_yVals</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span>
<span class="p">{</span>
<span class="n">_yValueSum</span> <span class="o">+=</span> <span class="nf">fabs</span><span class="p">(</span><span class="n">_yVals</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">entryCount</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_yVals</span><span class="o">!.</span><span class="n">count</span><span class="p">;</span> <span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">yValForXIndex</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="o">-&gt;</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">let</span> <span class="nv">e</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="nf">entryForXIndex</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">e</span> <span class="o">!==</span> <span class="kc">nil</span> <span class="o">&amp;&amp;</span> <span class="n">e</span><span class="o">!.</span><span class="n">xIndex</span> <span class="o">==</span> <span class="n">x</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">e</span><span class="o">!.</span><span class="n">value</span> <span class="p">}</span>
<span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="kt">Double</span><span class="o">.</span><span class="kt">NaN</span> <span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Returns the first Entry object found at the given xIndex with binary search. </span>
<span class="c1">/// If the no Entry at the specifed x-index is found, this method returns the Entry at the closest x-index. </span>
<span class="c1">/// Returns nil if no Entry object at that index.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">entryForXIndex</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="o">-&gt;</span> <span class="kt">ChartDataEntry</span><span class="p">?</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">index</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="nf">entryIndex</span><span class="p">(</span><span class="nv">xIndex</span><span class="p">:</span> <span class="n">x</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">index</span> <span class="o">&gt;</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_yVals</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">entriesForXIndex</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="o">-&gt;</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">entries</span> <span class="o">=</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]()</span>
<span class="k">var</span> <span class="nv">low</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">var</span> <span class="nv">high</span> <span class="o">=</span> <span class="n">_yVals</span><span class="o">.</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span>
<span class="k">while</span> <span class="p">(</span><span class="n">low</span> <span class="o">&lt;=</span> <span class="n">high</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">m</span> <span class="o">=</span> <span class="kt">Int</span><span class="p">((</span><span class="n">high</span> <span class="o">+</span> <span class="n">low</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">entry</span> <span class="o">=</span> <span class="n">_yVals</span><span class="p">[</span><span class="n">m</span><span class="p">]</span>
<span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">==</span> <span class="n">entry</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">while</span> <span class="p">(</span><span class="n">m</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">_yVals</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="n">xIndex</span> <span class="o">==</span> <span class="n">x</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">m</span><span class="o">--</span>
<span class="p">}</span>
<span class="n">high</span> <span class="o">=</span> <span class="n">_yVals</span><span class="o">.</span><span class="n">count</span>
<span class="k">for</span> <span class="p">(;</span> <span class="n">m</span> <span class="o">&lt;</span> <span class="n">high</span><span class="p">;</span> <span class="n">m</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">entry</span> <span class="o">=</span> <span class="n">_yVals</span><span class="p">[</span><span class="n">m</span><span class="p">]</span>
<span class="k">if</span> <span class="p">(</span><span class="n">entry</span><span class="o">.</span><span class="n">xIndex</span> <span class="o">==</span> <span class="n">x</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">entries</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">entry</span><span class="p">)</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="p">}</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">&gt;</span> <span class="n">_yVals</span><span class="p">[</span><span class="n">m</span><span class="p">]</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">low</span> <span class="o">=</span> <span class="n">m</span> <span class="o">+</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">high</span> <span class="o">=</span> <span class="n">m</span> <span class="o">-</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">entries</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">entryIndex</span><span class="p">(</span><span class="n">xIndex</span> <span class="nv">x</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Int</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">low</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">var</span> <span class="nv">high</span> <span class="o">=</span> <span class="n">_yVals</span><span class="o">.</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span>
<span class="k">var</span> <span class="nv">closest</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
<span class="k">while</span> <span class="p">(</span><span class="n">low</span> <span class="o">&lt;=</span> <span class="n">high</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">m</span> <span class="o">=</span> <span class="p">(</span><span class="n">high</span> <span class="o">+</span> <span class="n">low</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span>
<span class="k">var</span> <span class="nv">entry</span> <span class="o">=</span> <span class="n">_yVals</span><span class="p">[</span><span class="n">m</span><span class="p">]</span>
<span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">==</span> <span class="n">entry</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">while</span> <span class="p">(</span><span class="n">m</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">_yVals</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="n">xIndex</span> <span class="o">==</span> <span class="n">x</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">m</span><span class="o">--</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">m</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">x</span> <span class="o">&gt;</span> <span class="n">entry</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">low</span> <span class="o">=</span> <span class="n">m</span> <span class="o">+</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">high</span> <span class="o">=</span> <span class="n">m</span> <span class="o">-</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="n">closest</span> <span class="o">=</span> <span class="n">m</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">closest</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">entryIndex</span><span class="p">(</span><span class="n">entry</span> <span class="nv">e</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">,</span> <span class="nv">isEqual</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Int</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">isEqual</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_yVals</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yVals</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="nf">isEqual</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">i</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_yVals</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yVals</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">===</span> <span class="n">e</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">i</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
<span class="p">}</span>
<span class="c1">/// Returns the number of entries this DataSet holds.</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">valueCount</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span> <span class="k">return</span> <span class="n">_yVals</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="p">}</span>
<span class="c1">/// Adds an Entry to the DataSet dynamically.</span>
<span class="c1">/// Entries are added to the end of the list.</span>
<span class="c1">/// This will also recalculate the current minimum and maximum values of the DataSet and the value-sum.</span>
<span class="c1">/// :param: e the entry to add</span>
<span class="c1">/// 动态添加 条目 到 数据集,添加到末尾,自动计算最大值,最小值和总和。</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">addEntry</span><span class="p">(</span><span class="nv">e</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">val</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yVals</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yVals</span> <span class="o">=</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]()</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yVals</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="p">{</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">val</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="n">val</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yMax</span> <span class="o">&lt;</span> <span class="n">val</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">val</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yMin</span> <span class="o">&gt;</span> <span class="n">val</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="n">val</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">_yValueSum</span> <span class="o">+=</span> <span class="n">val</span>
<span class="n">_yVals</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// Adds an Entry to the DataSet dynamically.</span>
<span class="c1">/// Entries are added to their appropriate index respective to it's x-index.</span>
<span class="c1">/// This will also recalculate the current minimum and maximum values of the DataSet and the value-sum.</span>
<span class="c1">/// :param: e the entry to add</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">addEntryOrdered</span><span class="p">(</span><span class="nv">e</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">val</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yVals</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yVals</span> <span class="o">=</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]()</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yVals</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="p">{</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">val</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="n">val</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yMax</span> <span class="o">&lt;</span> <span class="n">val</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">val</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yMin</span> <span class="o">&gt;</span> <span class="n">val</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="n">val</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">_yValueSum</span> <span class="o">+=</span> <span class="n">val</span>
<span class="k">if</span> <span class="n">_yVals</span><span class="o">.</span><span class="n">last</span><span class="p">?</span><span class="o">.</span><span class="n">xIndex</span> <span class="o">&gt;</span> <span class="n">e</span><span class="o">.</span><span class="n">xIndex</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">closestIndex</span> <span class="o">=</span> <span class="nf">entryIndex</span><span class="p">(</span><span class="nv">xIndex</span><span class="p">:</span> <span class="n">e</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span>
<span class="k">if</span> <span class="n">_yVals</span><span class="p">[</span><span class="n">closestIndex</span><span class="p">]</span><span class="o">.</span><span class="n">xIndex</span> <span class="o">&lt;</span> <span class="n">e</span><span class="o">.</span><span class="n">xIndex</span>
<span class="p">{</span>
<span class="n">closestIndex</span><span class="o">++</span>
<span class="p">}</span>
<span class="n">_yVals</span><span class="o">.</span><span class="nf">insert</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="nv">atIndex</span><span class="p">:</span> <span class="n">closestIndex</span><span class="p">)</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">_yVals</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">removeEntry</span><span class="p">(</span><span class="nv">entry</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">removed</span> <span class="o">=</span> <span class="kc">false</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_yVals</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yVals</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">===</span> <span class="n">entry</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yVals</span><span class="o">.</span><span class="nf">removeAtIndex</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
<span class="n">removed</span> <span class="o">=</span> <span class="kc">true</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">removed</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yValueSum</span> <span class="o">-=</span> <span class="n">entry</span><span class="o">.</span><span class="n">value</span>
<span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="n">_lastStart</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">_lastEnd</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">removed</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">removeEntry</span><span class="p">(</span><span class="nv">#xIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">index</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="nf">entryIndex</span><span class="p">(</span><span class="nv">xIndex</span><span class="p">:</span> <span class="n">xIndex</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">index</span> <span class="o">&gt;</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">e</span> <span class="o">=</span> <span class="n">_yVals</span><span class="o">.</span><span class="nf">removeAtIndex</span><span class="p">(</span><span class="n">index</span><span class="p">)</span>
<span class="n">_yValueSum</span> <span class="o">-=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="n">_lastStart</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">_lastEnd</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">true</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">resetColors</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">colors</span><span class="o">.</span><span class="nf">removeAll</span><span class="p">(</span><span class="nv">keepCapacity</span><span class="p">:</span> <span class="kc">false</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">addColor</span><span class="p">(</span><span class="nv">color</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">colors</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">color</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">setColor</span><span class="p">(</span><span class="nv">color</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">colors</span><span class="o">.</span><span class="nf">removeAll</span><span class="p">(</span><span class="nv">keepCapacity</span><span class="p">:</span> <span class="kc">false</span><span class="p">)</span>
<span class="n">colors</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">color</span><span class="p">)</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">colorAt</span><span class="p">(</span><span class="k">var</span> <span class="nv">index</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">UIColor</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">index</span> <span class="o">=</span> <span class="mi">0</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">colors</span><span class="p">[</span><span class="n">index</span> <span class="o">%</span> <span class="n">colors</span><span class="o">.</span><span class="n">count</span><span class="p">]</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">isVisible</span><span class="p">:</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">visible</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">isDrawValuesEnabled</span><span class="p">:</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">drawValuesEnabled</span>
<span class="p">}</span>
<span class="c1">/// Checks if this DataSet contains the specified Entry.</span>
<span class="c1">/// :returns: true if contains the entry, false if not.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">contains</span><span class="p">(</span><span class="nv">e</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">for</span> <span class="n">entry</span> <span class="k">in</span> <span class="n">_yVals</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">entry</span><span class="o">.</span><span class="nf">isEqual</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">true</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="c1">/// Removes all values from this DataSet and recalculates min and max value.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">clear</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">_yVals</span><span class="o">.</span><span class="nf">removeAll</span><span class="p">(</span><span class="nv">keepCapacity</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span>
<span class="n">_lastStart</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">_lastEnd</span> <span class="o">=</span> <span class="mi">0</span>
<span class="nf">notifyDataSetChanged</span><span class="p">()</span>
<span class="p">}</span>
<span class="c1">// MARK: NSObject</span>
<span class="kd">public</span> <span class="k">override</span> <span class="k">var</span> <span class="nv">description</span><span class="p">:</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">format</span><span class="p">:</span> <span class="s">"ChartDataSet, label: %@, %i entries"</span><span class="p">,</span> <span class="nv">arguments</span><span class="p">:</span> <span class="p">[</span><span class="k">self</span><span class="o">.</span><span class="n">label</span> <span class="p">??</span> <span class="s">""</span><span class="p">,</span> <span class="n">_yVals</span><span class="o">.</span><span class="n">count</span><span class="p">])</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="k">override</span> <span class="k">var</span> <span class="nv">debugDescription</span><span class="p">:</span> <span class="kt">String</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">desc</span> <span class="o">=</span> <span class="n">description</span> <span class="o">+</span> <span class="s">":"</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_yVals</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">desc</span> <span class="o">+=</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span> <span class="o">+</span> <span class="n">_yVals</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">description</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">desc</span>
<span class="p">}</span>
<span class="c1">// MARK: NSCopying</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">copyWithZone</span><span class="p">(</span><span class="nv">zone</span><span class="p">:</span> <span class="kt">NSZone</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">AnyObject</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">copy</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="k">dynamicType</span><span class="o">.</span><span class="nf">allocWithZone</span><span class="p">(</span><span class="n">zone</span><span class="p">)</span> <span class="k">as</span> <span class="kt">ChartDataSet</span>
<span class="n">copy</span><span class="o">.</span><span class="n">colors</span> <span class="o">=</span> <span class="n">colors</span>
<span class="n">copy</span><span class="o">.</span><span class="n">_yVals</span> <span class="o">=</span> <span class="n">_yVals</span>
<span class="n">copy</span><span class="o">.</span><span class="n">_yMax</span> <span class="o">=</span> <span class="n">_yMax</span>
<span class="n">copy</span><span class="o">.</span><span class="n">_yMin</span> <span class="o">=</span> <span class="n">_yMin</span>
<span class="n">copy</span><span class="o">.</span><span class="n">_yValueSum</span> <span class="o">=</span> <span class="n">_yValueSum</span>
<span class="n">copy</span><span class="o">.</span><span class="n">label</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">label</span>
<span class="k">return</span> <span class="n">copy</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<h3>ChartData</h3>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//</span>
<span class="c1">// ChartData.swift</span>
<span class="c1">// Charts</span>
<span class="c1">//</span>
<span class="c1">// Created by Daniel Cohen Gindi on 23/2/15.</span></p>
<p><span class="c1">//</span>
<span class="c1">// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda</span>
<span class="c1">// A port of MPAndroidChart for iOS</span>
<span class="c1">// Licensed under Apache License 2.0</span>
<span class="c1">//</span>
<span class="c1">// https://github.com/danielgindi/ios-charts</span>
<span class="c1">//</span></p>
<p><span class="kd">import</span> <span class="kt">Foundation</span>
<span class="kd">import</span> <span class="kt">UIKit</span></p>
<p><span class="kd">public</span> <span class="kd">class</span> <span class="kt">ChartData</span><span class="p">:</span> <span class="kt">NSObject</span>
<span class="p">{</span>
<span class="c1">// y最大值</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv"><em>yMax</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1">// y最小值</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv"></em>yMin</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1">// 左y轴最大值</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv"><em>leftAxisMax</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1">// 左y轴最小值</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv"></em>leftAxisMin</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1">// 右y轴最大值</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv"><em>rightAxisMax</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1">// 右y轴最小值</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv"></em>rightAxisMin</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1">// y值总和</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv"><em>yValueSum</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="c1">// y值个数</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv"></em>yValCount</span> <span class="o">=</span> <span class="kt">Int</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span></p>
<pre><code><span class="c1">/// the last start value used for calcMinMax</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv">_lastStart</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">0</span>
<span class="c1">/// the last end value used for calcMinMax</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv">_lastEnd</span><span class="p">:</span> <span class="kt">Int</span> <span class="o">=</span> <span class="mi">0</span>
<span class="c1">/// the average length (in characters) across all x-value strings</span>
<span class="kd">private</span> <span class="k">var</span> <span class="nv">_xValAverageLength</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv">_xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">?]</span><span class="o">!</span>
<span class="kd">internal</span> <span class="k">var</span> <span class="nv">_dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]</span><span class="o">!</span>
<span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
<span class="n">_xVals</span> <span class="o">=</span> <span class="p">[</span><span class="kt">String</span><span class="p">?]()</span>
<span class="n">_dataSets</span> <span class="o">=</span> <span class="p">[</span><span class="kt">ChartDataSet</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="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">?]?,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
<span class="n">_xVals</span> <span class="o">=</span> <span class="n">xVals</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">?</span> <span class="p">[</span><span class="kt">String</span><span class="p">?]()</span> <span class="p">:</span> <span class="n">xVals</span>
<span class="n">_dataSets</span> <span class="o">=</span> <span class="n">dataSets</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">?</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]()</span> <span class="p">:</span> <span class="n">dataSets</span>
<span class="k">self</span><span class="o">.</span><span class="nf">initialize</span><span class="p">(</span><span class="n">_dataSets</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="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">NSObject</span><span class="p">]?,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]?)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
<span class="n">_xVals</span> <span class="o">=</span> <span class="n">xVals</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">?</span> <span class="p">[</span><span class="kt">String</span><span class="p">?]()</span> <span class="p">:</span> <span class="kt">ChartUtils</span><span class="o">.</span><span class="nf">bridgedObjCGetStringArray</span><span class="p">(</span><span class="nv">objc</span><span class="p">:</span> <span class="n">xVals</span><span class="o">!</span><span class="p">)</span>
<span class="n">_dataSets</span> <span class="o">=</span> <span class="n">dataSets</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">?</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]()</span> <span class="p">:</span> <span class="n">dataSets</span>
<span class="k">self</span><span class="o">.</span><span class="nf">initialize</span><span class="p">(</span><span class="n">_dataSets</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="nv">xVals</span><span class="p">:</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="nf">init</span><span class="p">(</span><span class="nv">xVals</span><span class="p">:</span> <span class="n">xVals</span><span class="p">,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</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="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">NSObject</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="nv">xVals</span><span class="p">:</span> <span class="n">xVals</span><span class="p">,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</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="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">?]?,</span> <span class="nv">dataSet</span><span class="p">:</span> <span class="kt">ChartDataSet</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="nv">xVals</span><span class="p">:</span> <span class="n">xVals</span><span class="p">,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="n">dataSet</span> <span class="o">===</span> <span class="kc">nil</span> <span class="p">?</span> <span class="nv">nil</span> <span class="p">:</span> <span class="p">[</span><span class="n">dataSet</span><span class="o">!</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="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">NSObject</span><span class="p">]?,</span> <span class="nv">dataSet</span><span class="p">:</span> <span class="kt">ChartDataSet</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="nv">xVals</span><span class="p">:</span> <span class="n">xVals</span><span class="p">,</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="n">dataSet</span> <span class="o">===</span> <span class="kc">nil</span> <span class="p">?</span> <span class="nv">nil</span> <span class="p">:</span> <span class="p">[</span><span class="n">dataSet</span><span class="o">!</span><span class="p">])</span>
<span class="p">}</span>
<span class="kd">internal</span> <span class="kd">func</span> <span class="nf">initialize</span><span class="p">(</span><span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">])</span>
<span class="p">{</span>
<span class="nf">checkIsLegal</span><span class="p">(</span><span class="n">dataSets</span><span class="p">)</span>
<span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="n">_lastStart</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">_lastEnd</span><span class="p">)</span>
<span class="nf">calcYValueSum</span><span class="p">()</span>
<span class="nf">calcYValueCount</span><span class="p">()</span>
<span class="nf">calcXValAverageLength</span><span class="p">()</span>
<span class="p">}</span>
<span class="c1">// calculates the average length (in characters) across all x-value strings</span>
<span class="kd">internal</span> <span class="kd">func</span> <span class="nf">calcXValAverageLength</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_xVals</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="p">{</span>
<span class="n">_xValAverageLength</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">sum</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_xVals</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">sum</span> <span class="o">+=</span> <span class="n">_xVals</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">?</span> <span class="mi">0</span> <span class="p">:</span> <span class="nf">count</span><span class="p">(</span><span class="n">_xVals</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">!</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">_xValAverageLength</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="n">sum</span><span class="p">)</span> <span class="o">/</span> <span class="kt">Double</span><span class="p">(</span><span class="n">_xVals</span><span class="o">.</span><span class="n">count</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// Checks if the combination of x-values array and DataSet array is legal or not.</span>
<span class="c1">// :param: dataSets</span>
<span class="kd">internal</span> <span class="kd">func</span> <span class="nf">checkIsLegal</span><span class="p">(</span><span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]</span><span class="o">!</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dataSets</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">yVals</span><span class="o">.</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">_xVals</span><span class="o">.</span><span class="n">count</span><span class="p">)</span>
<span class="p">{</span>
<span class="nf">println</span><span class="p">(</span><span class="s">"One or more of the DataSet Entry arrays are longer than the x-values array of this Data object."</span><span class="p">)</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">notifyDataChanged</span><span class="p">()</span>
<span class="p">{</span>
<span class="nf">initialize</span><span class="p">(</span><span class="n">_dataSets</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// calc minimum and maximum y value over all datasets</span>
<span class="kd">internal</span> <span class="kd">func</span> <span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">#start</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span> <span class="o">==</span> <span class="kc">nil</span> <span class="o">||</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">_lastStart</span> <span class="o">=</span> <span class="n">start</span>
<span class="n">_lastEnd</span> <span class="o">=</span> <span class="n">end</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="kt">DBL_MAX</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="o">-</span><span class="kt">DBL_MAX</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="n">start</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">end</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">yMin</span> <span class="o">&lt;</span> <span class="n">_yMin</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="n">_dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">yMin</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">yMax</span> <span class="o">&gt;</span> <span class="n">_yMax</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">_dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">yMax</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yMin</span> <span class="o">==</span> <span class="kt">DBL_MAX</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="p">}</span>
<span class="c1">// left axis</span>
<span class="k">var</span> <span class="nv">firstLeft</span> <span class="o">=</span> <span class="nf">getFirstLeft</span><span class="p">()</span>
<span class="k">if</span> <span class="p">(</span><span class="n">firstLeft</span> <span class="o">!==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_leftAxisMax</span> <span class="o">=</span> <span class="n">firstLeft</span><span class="o">!.</span><span class="n">yMax</span>
<span class="n">_leftAxisMin</span> <span class="o">=</span> <span class="n">firstLeft</span><span class="o">!.</span><span class="n">yMin</span>
<span class="k">for</span> <span class="n">dataSet</span> <span class="k">in</span> <span class="n">_dataSets</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dataSet</span><span class="o">.</span><span class="n">axisDependency</span> <span class="o">==</span> <span class="o">.</span><span class="kt">Left</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dataSet</span><span class="o">.</span><span class="n">yMin</span> <span class="o">&lt;</span> <span class="n">_leftAxisMin</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_leftAxisMin</span> <span class="o">=</span> <span class="n">dataSet</span><span class="o">.</span><span class="n">yMin</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dataSet</span><span class="o">.</span><span class="n">yMax</span> <span class="o">&gt;</span> <span class="n">_leftAxisMax</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_leftAxisMax</span> <span class="o">=</span> <span class="n">dataSet</span><span class="o">.</span><span class="n">yMax</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// right axis</span>
<span class="k">var</span> <span class="nv">firstRight</span> <span class="o">=</span> <span class="nf">getFirstRight</span><span class="p">()</span>
<span class="k">if</span> <span class="p">(</span><span class="n">firstRight</span> <span class="o">!==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_rightAxisMax</span> <span class="o">=</span> <span class="n">firstRight</span><span class="o">!.</span><span class="n">yMax</span>
<span class="n">_rightAxisMin</span> <span class="o">=</span> <span class="n">firstRight</span><span class="o">!.</span><span class="n">yMin</span>
<span class="k">for</span> <span class="n">dataSet</span> <span class="k">in</span> <span class="n">_dataSets</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dataSet</span><span class="o">.</span><span class="n">axisDependency</span> <span class="o">==</span> <span class="o">.</span><span class="kt">Right</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dataSet</span><span class="o">.</span><span class="n">yMin</span> <span class="o">&lt;</span> <span class="n">_rightAxisMin</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_rightAxisMin</span> <span class="o">=</span> <span class="n">dataSet</span><span class="o">.</span><span class="n">yMin</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dataSet</span><span class="o">.</span><span class="n">yMax</span> <span class="o">&gt;</span> <span class="n">_rightAxisMax</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_rightAxisMax</span> <span class="o">=</span> <span class="n">dataSet</span><span class="o">.</span><span class="n">yMax</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// in case there is only one axis, adjust the second axis</span>
<span class="nf">handleEmptyAxis</span><span class="p">(</span><span class="n">firstLeft</span><span class="p">,</span> <span class="nv">firstRight</span><span class="p">:</span> <span class="n">firstRight</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// calculates the sum of all y-values in all datasets</span>
<span class="kd">internal</span> <span class="kd">func</span> <span class="nf">calcYValueSum</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">_yValueSum</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yValueSum</span> <span class="o">+=</span> <span class="nf">fabs</span><span class="p">(</span><span class="n">_dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">yValueSum</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Calculates the total number of y-values across all ChartDataSets the ChartData represents.</span>
<span class="kd">internal</span> <span class="kd">func</span> <span class="nf">calcYValueCount</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">_yValCount</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">count</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">count</span> <span class="o">+=</span> <span class="n">_dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">entryCount</span>
<span class="p">}</span>
<span class="n">_yValCount</span> <span class="o">=</span> <span class="n">count</span>
<span class="p">}</span>
<span class="c1">/// returns the number of LineDataSets this object contains</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">dataSetCount</span><span class="p">:</span> <span class="kt">Int</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</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="n">_dataSets</span><span class="o">.</span><span class="n">count</span>
<span class="p">}</span>
<span class="c1">/// returns the smallest y-value the data object contains.</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">yMin</span><span class="p">:</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_yMin</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">getYMin</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_yMin</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">getYMin</span><span class="p">(</span><span class="nv">axis</span><span class="p">:</span> <span class="kt">ChartYAxis</span><span class="o">.</span><span class="kt">AxisDependency</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">axis</span> <span class="o">==</span> <span class="o">.</span><span class="kt">Left</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_leftAxisMin</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_rightAxisMin</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// returns the greatest y-value the data object contains.</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">yMax</span><span class="p">:</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_yMax</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">getYMax</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_yMax</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">getYMax</span><span class="p">(</span><span class="nv">axis</span><span class="p">:</span> <span class="kt">ChartYAxis</span><span class="o">.</span><span class="kt">AxisDependency</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">axis</span> <span class="o">==</span> <span class="o">.</span><span class="kt">Left</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_leftAxisMax</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_rightAxisMax</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// returns the average length (in characters) across all values in the x-vals array</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">xValAverageLength</span><span class="p">:</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_xValAverageLength</span>
<span class="p">}</span>
<span class="c1">/// returns the total y-value sum across all DataSet objects the this object represents.</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">yValueSum</span><span class="p">:</span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_yValueSum</span>
<span class="p">}</span>
<span class="c1">/// Returns the total number of y-values across all DataSet objects the this object represents.</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">yValCount</span><span class="p">:</span> <span class="kt">Int</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_yValCount</span>
<span class="p">}</span>
<span class="c1">/// returns the x-values the chart represents</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">xVals</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">?]</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_xVals</span>
<span class="p">}</span>
<span class="c1">///Adds a new x-value to the chart data.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">addXValue</span><span class="p">(</span><span class="nv">xVal</span><span class="p">:</span> <span class="kt">String</span><span class="p">?)</span>
<span class="p">{</span>
<span class="n">_xVals</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">xVal</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// Removes the x-value at the specified index.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">removeXValue</span><span class="p">(</span><span class="nv">index</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_xVals</span><span class="o">.</span><span class="nf">removeAtIndex</span><span class="p">(</span><span class="n">index</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// Returns the array of ChartDataSets this object holds.</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">dataSets</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataSet</span><span class="p">]</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_dataSets</span>
<span class="p">}</span>
<span class="k">set</span>
<span class="p">{</span>
<span class="n">_dataSets</span> <span class="o">=</span> <span class="n">newValue</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Retrieve the index of a ChartDataSet with a specific label from the ChartData. Search can be case sensitive or not.</span>
<span class="c1">/// IMPORTANT: This method does calculations at runtime, do not over-use in performance critical situations.</span>
<span class="c1">///</span>
<span class="c1">/// :param: dataSets the DataSet array to search</span>
<span class="c1">/// :param: type</span>
<span class="c1">/// :param: ignorecase if true, the search is not case-sensitive</span>
<span class="c1">/// :returns:</span>
<span class="kd">internal</span> <span class="kd">func</span> <span class="nf">getDataSetIndexByLabel</span><span class="p">(</span><span class="nv">label</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">ignorecase</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Int</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">ignorecase</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">label</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">label</span><span class="o">.</span><span class="nf">caseInsensitiveCompare</span><span class="p">(</span><span class="n">dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">label</span><span class="o">!</span><span class="p">)</span> <span class="o">==</span> <span class="kt">NSComparisonResult</span><span class="o">.</span><span class="kt">OrderedSame</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">i</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">label</span> <span class="o">==</span> <span class="n">dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">label</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">i</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
<span class="p">}</span>
<span class="c1">/// returns the total number of x-values this ChartData object represents (the size of the x-values array)</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">xValCount</span><span class="p">:</span> <span class="kt">Int</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_xVals</span><span class="o">.</span><span class="n">count</span>
<span class="p">}</span>
<span class="c1">/// Returns the labels of all DataSets as a string array.</span>
<span class="kd">internal</span> <span class="kd">func</span> <span class="nf">dataSetLabels</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">types</span> <span class="o">=</span> <span class="p">[</span><span class="kt">String</span><span class="p">]()</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">label</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="n">types</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">_dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">label</span><span class="o">!</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">types</span>
<span class="p">}</span>
<span class="c1">/// Get the Entry for a corresponding highlight object</span>
<span class="c1">///</span>
<span class="c1">/// :param: highlight</span>
<span class="c1">/// :returns: the entry that is highlighted</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">getEntryForHighlight</span><span class="p">(</span><span class="nv">highlight</span><span class="p">:</span> <span class="kt">ChartHighlight</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">ChartDataEntry</span><span class="p">?</span>
<span class="p">{</span>
<span class="k">if</span> <span class="n">highlight</span><span class="o">.</span><span class="n">dataSetIndex</span> <span class="o">&gt;=</span> <span class="n">dataSets</span><span class="o">.</span><span class="n">count</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_dataSets</span><span class="p">[</span><span class="n">highlight</span><span class="o">.</span><span class="n">dataSetIndex</span><span class="p">]</span><span class="o">.</span><span class="nf">entryForXIndex</span><span class="p">(</span><span class="n">highlight</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Returns the DataSet object with the given label.</span>
<span class="c1">/// sensitive or not.</span>
<span class="c1">/// IMPORTANT: This method does calculations at runtime. Use with care in performance critical situations.</span>
<span class="c1">///</span>
<span class="c1">/// :param: label</span>
<span class="c1">/// :param: ignorecase</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">getDataSetByLabel</span><span class="p">(</span><span class="nv">label</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">ignorecase</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">ChartDataSet</span><span class="p">?</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">index</span> <span class="o">=</span> <span class="nf">getDataSetIndexByLabel</span><span class="p">(</span><span class="n">label</span><span class="p">,</span> <span class="nv">ignorecase</span><span class="p">:</span> <span class="n">ignorecase</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&gt;=</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">_dataSets</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">getDataSetByIndex</span><span class="p">(</span><span class="nv">index</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">ChartDataSet</span><span class="o">!</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span> <span class="o">==</span> <span class="kc">nil</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&gt;=</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">_dataSets</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">addDataSet</span><span class="p">(</span><span class="nv">d</span><span class="p">:</span> <span class="kt">ChartDataSet</span><span class="o">!</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="n">_yValCount</span> <span class="o">+=</span> <span class="n">d</span><span class="o">.</span><span class="n">entryCount</span>
<span class="n">_yValueSum</span> <span class="o">+=</span> <span class="n">d</span><span class="o">.</span><span class="n">yValueSum</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</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="p">{</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">yMax</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">yMin</span>
<span class="k">if</span> <span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">axisDependency</span> <span class="o">==</span> <span class="o">.</span><span class="kt">Left</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_leftAxisMax</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">yMax</span>
<span class="n">_leftAxisMin</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">yMin</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">_rightAxisMax</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">yMax</span>
<span class="n">_rightAxisMin</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">yMin</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yMax</span> <span class="o">&lt;</span> <span class="n">d</span><span class="o">.</span><span class="n">yMax</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">yMax</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yMin</span> <span class="o">&gt;</span> <span class="n">d</span><span class="o">.</span><span class="n">yMin</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">yMin</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">axisDependency</span> <span class="o">==</span> <span class="o">.</span><span class="kt">Left</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_leftAxisMax</span> <span class="o">&lt;</span> <span class="n">d</span><span class="o">.</span><span class="n">yMax</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_leftAxisMax</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">yMax</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_leftAxisMin</span> <span class="o">&gt;</span> <span class="n">d</span><span class="o">.</span><span class="n">yMin</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_leftAxisMin</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">yMin</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_rightAxisMax</span> <span class="o">&lt;</span> <span class="n">d</span><span class="o">.</span><span class="n">yMax</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_rightAxisMax</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">yMax</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_rightAxisMin</span> <span class="o">&gt;</span> <span class="n">d</span><span class="o">.</span><span class="n">yMin</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_rightAxisMin</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">yMin</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">_dataSets</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">d</span><span class="p">)</span>
<span class="nf">handleEmptyAxis</span><span class="p">(</span><span class="nf">getFirstLeft</span><span class="p">(),</span> <span class="nv">firstRight</span><span class="p">:</span> <span class="nf">getFirstRight</span><span class="p">())</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">handleEmptyAxis</span><span class="p">(</span><span class="nv">firstLeft</span><span class="p">:</span> <span class="kt">ChartDataSet</span><span class="p">?,</span> <span class="nv">firstRight</span><span class="p">:</span> <span class="kt">ChartDataSet</span><span class="p">?)</span>
<span class="p">{</span>
<span class="c1">// in case there is only one axis, adjust the second axis</span>
<span class="k">if</span> <span class="p">(</span><span class="n">firstLeft</span> <span class="o">===</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_leftAxisMax</span> <span class="o">=</span> <span class="n">_rightAxisMax</span>
<span class="n">_leftAxisMin</span> <span class="o">=</span> <span class="n">_rightAxisMin</span>
<span class="p">}</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">firstRight</span> <span class="o">===</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_rightAxisMax</span> <span class="o">=</span> <span class="n">_leftAxisMax</span>
<span class="n">_rightAxisMin</span> <span class="o">=</span> <span class="n">_leftAxisMin</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Removes the given DataSet from this data object.</span>
<span class="c1">/// Also recalculates all minimum and maximum values.</span>
<span class="c1">///</span>
<span class="c1">/// :returns: true if a DataSet was removed, false if no DataSet could be removed.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">removeDataSet</span><span class="p">(</span><span class="nv">dataSet</span><span class="p">:</span> <span class="kt">ChartDataSet</span><span class="o">!</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span> <span class="o">==</span> <span class="kc">nil</span> <span class="o">||</span> <span class="n">dataSet</span> <span class="o">===</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">===</span> <span class="n">dataSet</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nf">removeDataSetByIndex</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="c1">/// Removes the DataSet at the given index in the DataSet array from the data object.</span>
<span class="c1">/// Also recalculates all minimum and maximum values.</span>
<span class="c1">///</span>
<span class="c1">/// :returns: true if a DataSet was removed, false if no DataSet could be removed.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">removeDataSetByIndex</span><span class="p">(</span><span class="nv">index</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span> <span class="o">==</span> <span class="kc">nil</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&gt;=</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span> <span class="o">||</span> <span class="n">index</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">d</span> <span class="o">=</span> <span class="n">_dataSets</span><span class="o">.</span><span class="nf">removeAtIndex</span><span class="p">(</span><span class="n">index</span><span class="p">)</span>
<span class="n">_yValCount</span> <span class="o">-=</span> <span class="n">d</span><span class="o">.</span><span class="n">entryCount</span>
<span class="n">_yValueSum</span> <span class="o">-=</span> <span class="n">d</span><span class="o">.</span><span class="n">yValueSum</span>
<span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="n">_lastStart</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">_lastEnd</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">true</span>
<span class="p">}</span>
<span class="c1">/// Adds an Entry to the DataSet at the specified index. Entries are added to the end of the list.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">addEntry</span><span class="p">(</span><span class="nv">e</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">,</span> <span class="nv">dataSetIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="o">&amp;&amp;</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span> <span class="o">&gt;</span> <span class="n">dataSetIndex</span> <span class="o">&amp;&amp;</span> <span class="n">dataSetIndex</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">val</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="k">var</span> <span class="nv">set</span> <span class="o">=</span> <span class="n">_dataSets</span><span class="p">[</span><span class="n">dataSetIndex</span><span class="p">]</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yValCount</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="n">val</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">val</span>
<span class="k">if</span> <span class="p">(</span><span class="k">set</span><span class="o">.</span><span class="n">axisDependency</span> <span class="o">==</span> <span class="o">.</span><span class="kt">Left</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_leftAxisMax</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="n">_leftAxisMin</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="n">_rightAxisMax</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="n">_rightAxisMin</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yMax</span> <span class="o">&lt;</span> <span class="n">val</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMax</span> <span class="o">=</span> <span class="n">val</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_yMin</span> <span class="o">&gt;</span> <span class="n">val</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_yMin</span> <span class="o">=</span> <span class="n">val</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="k">set</span><span class="o">.</span><span class="n">axisDependency</span> <span class="o">==</span> <span class="o">.</span><span class="kt">Left</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_leftAxisMax</span> <span class="o">&lt;</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_leftAxisMax</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_leftAxisMin</span> <span class="o">&gt;</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_leftAxisMin</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_rightAxisMax</span> <span class="o">&lt;</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_rightAxisMax</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_rightAxisMin</span> <span class="o">&gt;</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_rightAxisMin</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="n">value</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">_yValCount</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">_yValueSum</span> <span class="o">+=</span> <span class="n">val</span>
<span class="nf">handleEmptyAxis</span><span class="p">(</span><span class="nf">getFirstLeft</span><span class="p">(),</span> <span class="nv">firstRight</span><span class="p">:</span> <span class="nf">getFirstRight</span><span class="p">())</span>
<span class="k">set</span><span class="o">.</span><span class="nf">addEntry</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="nf">println</span><span class="p">(</span><span class="s">"ChartData.addEntry() - dataSetIndex our of range."</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Removes the given Entry object from the DataSet at the specified index.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">removeEntry</span><span class="p">(</span><span class="nv">entry</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="o">!</span><span class="p">,</span> <span class="nv">dataSetIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="c1">// entry null, outofbounds</span>
<span class="k">if</span> <span class="p">(</span><span class="n">entry</span> <span class="o">===</span> <span class="kc">nil</span> <span class="o">||</span> <span class="n">dataSetIndex</span> <span class="o">&gt;=</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="c1">// remove the entry from the dataset</span>
<span class="k">var</span> <span class="nv">removed</span> <span class="o">=</span> <span class="n">_dataSets</span><span class="p">[</span><span class="n">dataSetIndex</span><span class="p">]</span><span class="o">.</span><span class="nf">removeEntry</span><span class="p">(</span><span class="nv">xIndex</span><span class="p">:</span> <span class="n">entry</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">removed</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">val</span> <span class="o">=</span> <span class="n">entry</span><span class="o">.</span><span class="n">value</span>
<span class="n">_yValCount</span> <span class="o">-=</span> <span class="mi">1</span>
<span class="n">_yValueSum</span> <span class="o">-=</span> <span class="n">val</span>
<span class="nf">calcMinMax</span><span class="p">(</span><span class="nv">start</span><span class="p">:</span> <span class="n">_lastStart</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">_lastEnd</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">removed</span>
<span class="p">}</span>
<span class="c1">/// Removes the Entry object at the given xIndex from the ChartDataSet at the</span>
<span class="c1">/// specified index. Returns true if an entry was removed, false if no Entry</span>
<span class="c1">/// was found that meets the specified requirements.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">removeEntryByXIndex</span><span class="p">(</span><span class="nv">xIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">dataSetIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dataSetIndex</span> <span class="o">&gt;=</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">entry</span> <span class="o">=</span> <span class="n">_dataSets</span><span class="p">[</span><span class="n">dataSetIndex</span><span class="p">]</span><span class="o">.</span><span class="nf">entryForXIndex</span><span class="p">(</span><span class="n">xIndex</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span><span class="n">entry</span><span class="p">?</span><span class="o">.</span><span class="n">xIndex</span> <span class="o">!=</span> <span class="n">xIndex</span><span class="p">)</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="nf">removeEntry</span><span class="p">(</span><span class="n">entry</span><span class="p">,</span> <span class="nv">dataSetIndex</span><span class="p">:</span> <span class="n">dataSetIndex</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">/// Returns the DataSet that contains the provided Entry, or null, if no DataSet contains this entry.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">getDataSetForEntry</span><span class="p">(</span><span class="nv">e</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="o">!</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">ChartDataSet</span><span class="p">?</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">e</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">set</span> <span class="o">=</span> <span class="n">_dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="k">for</span> <span class="p">(</span><span class="k">var</span> <span class="nv">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="k">set</span><span class="o">.</span><span class="n">entryCount</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">e</span> <span class="o">===</span> <span class="k">set</span><span class="o">.</span><span class="nf">entryForXIndex</span><span class="p">(</span><span class="n">e</span><span class="o">.</span><span class="n">xIndex</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">set</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="c1">/// Returns the index of the provided DataSet inside the DataSets array of</span>
<span class="c1">/// this data object. Returns -1 if the DataSet was not found.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">indexOfDataSet</span><span class="p">(</span><span class="nv">dataSet</span><span class="p">:</span> <span class="kt">ChartDataSet</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Int</span>
<span class="p">{</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">===</span> <span class="n">dataSet</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">i</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">getFirstLeft</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">ChartDataSet</span><span class="p">?</span>
<span class="p">{</span>
<span class="k">for</span> <span class="n">dataSet</span> <span class="k">in</span> <span class="n">_dataSets</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dataSet</span><span class="o">.</span><span class="n">axisDependency</span> <span class="o">==</span> <span class="o">.</span><span class="kt">Left</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">dataSet</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">getFirstRight</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">ChartDataSet</span><span class="p">?</span>
<span class="p">{</span>
<span class="k">for</span> <span class="n">dataSet</span> <span class="k">in</span> <span class="n">_dataSets</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dataSet</span><span class="o">.</span><span class="n">axisDependency</span> <span class="o">==</span> <span class="o">.</span><span class="kt">Right</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">dataSet</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="c1">/// Returns all colors used across all DataSet objects this object represents.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">getColors</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">UIColor</span><span class="p">]?</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">_dataSets</span> <span class="o">==</span> <span class="kc">nil</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">clrcnt</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">clrcnt</span> <span class="o">+=</span> <span class="n">_dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">colors</span><span class="o">.</span><span class="n">count</span>
<span class="p">}</span>
<span class="k">var</span> <span class="nv">colors</span> <span class="o">=</span> <span class="p">[</span><span class="kt">UIColor</span><span class="p">]()</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">_dataSets</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">clrs</span> <span class="o">=</span> <span class="n">_dataSets</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">colors</span>
<span class="k">for</span> <span class="n">clr</span> <span class="k">in</span> <span class="n">clrs</span>
<span class="p">{</span>
<span class="n">colors</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="n">clr</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">colors</span>
<span class="p">}</span>
<span class="c1">/// Generates an x-values array filled with numbers in range specified by the parameters. Can be used for convenience.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">generateXVals</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">to</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">xvals</span> <span class="o">=</span> <span class="p">[</span><span class="kt">String</span><span class="p">]()</span>
<span class="k">for</span> <span class="p">(</span><span class="k">var</span> <span class="nv">i</span> <span class="o">=</span> <span class="n">from</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">to</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">xvals</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="kt">String</span><span class="p">(</span><span class="n">i</span><span class="p">))</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">xvals</span>
<span class="p">}</span>
<span class="c1">/// Sets a custom ValueFormatter for all DataSets this data object contains.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">setValueFormatter</span><span class="p">(</span><span class="nv">formatter</span><span class="p">:</span> <span class="kt">NSNumberFormatter</span><span class="o">!</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">for</span> <span class="k">set</span> <span class="k">in</span> <span class="n">dataSets</span>
<span class="p">{</span>
<span class="k">set</span><span class="o">.</span><span class="n">valueFormatter</span> <span class="o">=</span> <span class="n">formatter</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Sets the color of the value-text (color in which the value-labels are drawn) for all DataSets this data object contains.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">setValueTextColor</span><span class="p">(</span><span class="nv">color</span><span class="p">:</span> <span class="kt">UIColor</span><span class="o">!</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">for</span> <span class="k">set</span> <span class="k">in</span> <span class="n">dataSets</span>
<span class="p">{</span>
<span class="k">set</span><span class="o">.</span><span class="n">valueTextColor</span> <span class="o">=</span> <span class="n">color</span> <span class="p">??</span> <span class="k">set</span><span class="o">.</span><span class="n">valueTextColor</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Sets the font for all value-labels for all DataSets this data object contains.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">setValueFont</span><span class="p">(</span><span class="nv">font</span><span class="p">:</span> <span class="kt">UIFont</span><span class="o">!</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">for</span> <span class="k">set</span> <span class="k">in</span> <span class="n">dataSets</span>
<span class="p">{</span>
<span class="k">set</span><span class="o">.</span><span class="n">valueFont</span> <span class="o">=</span> <span class="n">font</span> <span class="p">??</span> <span class="k">set</span><span class="o">.</span><span class="n">valueFont</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Enables / disables drawing values (value-text) for all DataSets this data object contains.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">setDrawValues</span><span class="p">(</span><span class="nv">enabled</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">for</span> <span class="k">set</span> <span class="k">in</span> <span class="n">dataSets</span>
<span class="p">{</span>
<span class="k">set</span><span class="o">.</span><span class="n">drawValuesEnabled</span> <span class="o">=</span> <span class="n">enabled</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// Enables / disables highlighting values for all DataSets this data object contains.</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">highlightEnabled</span><span class="p">:</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">for</span> <span class="k">set</span> <span class="k">in</span> <span class="n">dataSets</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">set</span><span class="o">.</span><span class="n">highlightEnabled</span><span class="p">)</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">return</span> <span class="kc">true</span>
<span class="p">}</span>
<span class="k">set</span>
<span class="p">{</span>
<span class="k">for</span> <span class="k">set</span> <span class="k">in</span> <span class="n">dataSets</span>
<span class="p">{</span>
<span class="k">set</span><span class="o">.</span><span class="n">highlightEnabled</span> <span class="o">=</span> <span class="n">newValue</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">/// if true, value highlightning is enabled</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">isHighlightEnabled</span><span class="p">:</span> <span class="kt">Bool</span> <span class="p">{</span> <span class="k">return</span> <span class="n">highlightEnabled</span> <span class="p">}</span>
<span class="c1">/// Clears this data object from all DataSets and removes all Entries.</span>
<span class="c1">/// Don't forget to invalidate the chart after this.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">clearValues</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">dataSets</span><span class="o">.</span><span class="nf">removeAll</span><span class="p">(</span><span class="nv">keepCapacity</span><span class="p">:</span> <span class="kc">false</span><span class="p">)</span>
<span class="nf">notifyDataChanged</span><span class="p">()</span>
<span class="p">}</span>
<span class="c1">/// Checks if this data object contains the specified Entry. Returns true if so, false if not.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">contains</span><span class="p">(</span><span class="nv">#entry</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">for</span> <span class="k">set</span> <span class="k">in</span> <span class="n">dataSets</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">set</span><span class="o">.</span><span class="nf">contains</span><span class="p">(</span><span class="n">entry</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">true</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="c1">/// Checks if this data object contains the specified DataSet. Returns true if so, false if not.</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">contains</span><span class="p">(</span><span class="nv">#dataSet</span><span class="p">:</span> <span class="kt">ChartDataSet</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span>
<span class="p">{</span>
<span class="k">for</span> <span class="k">set</span> <span class="k">in</span> <span class="n">dataSets</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">set</span><span class="o">.</span><span class="nf">isEqual</span><span class="p">(</span><span class="n">dataSet</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">return</span> <span class="kc">true</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">false</span>
<span class="p">}</span>
<span class="c1">/// MARK: - ObjC compatibility</span>
<span class="c1">/// returns the average length (in characters) across all values in the x-vals array</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">xValsObjc</span><span class="p">:</span> <span class="p">[</span><span class="kt">NSObject</span><span class="p">]</span> <span class="p">{</span> <span class="k">return</span> <span class="kt">ChartUtils</span><span class="o">.</span><span class="nf">bridgedObjCGetStringArray</span><span class="p">(</span><span class="nv">swift</span><span class="p">:</span> <span class="n">_xVals</span><span class="p">);</span> <span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
项目第二十八天 iOS-Chart源码分析-2 Filter2015-08-02T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/08/02/gpxj-28-day<h2>结构</h2>
<hr />
<ul>
<li>动画 : Animation/</li>
<li>图表 : Charts/</li>
<li>组件 : Components/</li>
<li>数据 : Data/</li>
<li>过滤 : Filters/</li>
<li>高亮 : Highlight/</li>
<li>渲染 : Renderers/</li>
<li>工具 : Utils/</li>
</ul>
<h2>Filters 过滤器</h2>
<hr />
<p>过滤器 Filters/</p>
<p>基类: └── ChartDataBaseFilter.swift
逼近器: ├── ChartDataApproximatorFilter.swift</p>
<h2>名词解释</h2>
<hr />
<p>approximator</p>
<pre><code>n. 接近者,近似者, 逼近器
</code></pre>
<p>Douglas-Peucker-Algorithm</p>
<pre><code>道格拉斯-普克算法
道格拉斯-普克算法(Douglas–Peucker algorithm,亦称为拉默-道格拉斯-普克算法、迭代适应点算法、分裂与合并算法)是将曲线近似表示为一系列点,并减少点的数量的一种算法。该算法的原始类型分别由乌尔斯·拉默(Urs Ramer)于1972年以及大卫·道格拉斯(David Douglas)和托马斯·普克(Thomas Peucker)于1973年提出[1],并在之后的数十年中由其他学者予以完善[2]。
</code></pre>
<p><a href="https://zh.wikipedia.org/wiki/%E9%81%93%E6%A0%BC%E6%8B%89%E6%96%AF-%E6%99%AE%E5%85%8B%E7%AE%97%E6%B3%95">https://zh.wikipedia.org/wiki/%E9%81%93%E6%A0%BC%E6%8B%89%E6%96%AF-%E6%99%AE%E5%85%8B%E7%AE%97%E6%B3%95</a></p>
<p><a href="https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm">https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm</a></p>
<p>tolerance</p>
<pre><code>公差(gong chai)
几何参数的公差有尺寸公差、形状公差、位置公差等。
①尺寸公差。指允许尺寸的变动量,等于最大极限尺寸与最小极限尺寸代数差的绝对值。
②形状公差。指单一实际要素的形状所允许的变动全量,包括直线度、平面度、圆度、圆柱度、线轮廓度和面轮廓度6个项目。
③位置公差。指关联实际要素的位置对基准所允许的变动全量,它限制零件的两个或两个以上的点、线、面之间的相互位置关系,包括平行度、垂直度、倾斜度、同轴度、对称度、位置度、圆跳动和全跳动8个项目。公差表示了零件的制造精度要求,反映了其加工难易程度。
</code></pre>
<h2>ChartDataApproximatorFilter</h2>
<hr />
<h3>枚举</h3>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">@objc</span>
<span class="kd">public</span> <span class="kd">enum</span> <span class="kt">ApproximatorType</span><span class="p">:</span> <span class="kt">Int</span>
<span class="p">{</span>
<span class="k">case</span> <span class="kt">None</span> <span class="c1">// 无</span>
<span class="k">case</span> <span class="kt">RamerDouglasPeucker</span> <span class="c1">// 拉默-道格拉斯-普克</span>
<span class="p">}</span></code></pre></figure></p>
<h3>属性</h3>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">/// the type of filtering algorithm to use</span>
<span class="c1">/// 逼近算法类型</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">type</span> <span class="o">=</span> <span class="kt">ApproximatorType</span><span class="o">.</span><span class="kt">None</span></p>
<p><span class="c1">/// the tolerance to be filtered with</span>
<span class="c1">/// When using the Douglas-Peucker-Algorithm, the tolerance is an angle in degrees, that will trigger the filtering</span>
<span class="c1">/// 公差的作用</span>
<span class="c1">/// 当使用 道格拉斯-普克 算法时,tolerance 作为一个角度,触发逼近</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">tolerance</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span></p>
<p><span class="c1">/// 绽放比例</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">scaleRatio</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span>
<span class="c1">/// delta 比例</span>
<span class="kd">public</span> <span class="k">var</span> <span class="nv">deltaRatio</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span></code></pre></figure></p>
<h3>构造方法</h3>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="k">override</span> <span class="nf">init</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span>
<span class="p">}</span></p>
<p><span class="c1">/// Initializes the approximator with the given type and tolerance. </span>
<span class="c1">/// If toleranec <= 0, no filtering will be done.</span>
<span class="c1">/// 根据tolerance初始化逼近器</span>
<span class="c1">/// 如果 tolerance <= 0, 逼近完成</span>
<span class="kd">public</span> <span class="nf">init</span><span class="p">(</span><span class="nv">type</span><span class="p">:</span> <span class="kt">ApproximatorType</span><span class="p">,</span> <span class="nv">tolerance</span><span class="p">:</span> <span class="kt">Double</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">()</span></p>
<pre><code><span class="nf">setup</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="nv">tolerance</span><span class="p">:</span> <span class="n">tolerance</span><span class="p">)</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<h3>成员方法</h3>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">/// Sets type and tolerance.</span>
<span class="c1">/// If tolerance <= 0, no filtering will be done.</span>
<span class="c1">/// 如果 公差 <= 0, 不再逼近</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">setup</span><span class="p">(</span><span class="nv">type</span><span class="p">:</span> <span class="kt">ApproximatorType</span><span class="p">,</span> <span class="nv">tolerance</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="n">type</span> <span class="o">=</span> <span class="n">type</span>
<span class="k">self</span><span class="o">.</span><span class="n">tolerance</span> <span class="o">=</span> <span class="n">tolerance</span>
<span class="p">}</span></p>
<p><span class="c1">/// Sets the ratios for x- and y-axis, as well as the ratio of the scale levels</span>
<span class="c1">/// 设置xy轴的比例,也就是缩放比例</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">setRatios</span><span class="p">(</span><span class="nv">deltaRatio</span><span class="p">:</span> <span class="kt">Double</span><span class="p">,</span> <span class="nv">scaleRatio</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="n">deltaRatio</span> <span class="o">=</span> <span class="n">deltaRatio</span>
<span class="k">self</span><span class="o">.</span><span class="n">scaleRatio</span> <span class="o">=</span> <span class="n">scaleRatio</span>
<span class="p">}</span></p>
<p><span class="c1">/// Filters according to type. Uses the pre set set tolerance</span>
<span class="c1">/// 逼近器取决于 type, 根据之前的set设置tolerance</span>
<span class="c1">///</span>
<span class="c1">/// :param: points the points to filter</span>
<span class="c1">/// 参数: 将要被逼近的点</span>
<span class="kd">public</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">filter</span><span class="p">(</span><span class="nv">points</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">])</span> <span class="o">-></span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]</span>
<span class="p">{</span>
<span class="k">return</span> <span class="nf">filter</span><span class="p">(</span><span class="n">points</span><span class="p">,</span> <span class="nv">tolerance</span><span class="p">:</span> <span class="n">tolerance</span><span class="p">)</span>
<span class="p">}</span></p>
<p><span class="c1">/// Filters according to type.</span>
<span class="c1">///</span>
<span class="c1">/// :param: points the points to filter</span>
<span class="c1">/// :param: tolerance the angle in degrees that will trigger the filtering</span>
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">filter</span><span class="p">(</span><span class="nv">points</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">],</span> <span class="nv">tolerance</span><span class="p">:</span> <span class="kt">Double</span><span class="p">)</span> <span class="o">-></span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">tolerance</span> <span class="o"><=</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">points</span>
<span class="p">}</span></p>
<pre><code><span class="k">switch</span> <span class="p">(</span><span class="n">type</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">RamerDouglasPeucker</span><span class="p">:</span>
<span class="k">return</span> <span class="nf">reduceWithDouglasPeuker</span><span class="p">(</span><span class="n">points</span><span class="p">,</span> <span class="nv">epsilon</span><span class="p">:</span> <span class="n">tolerance</span><span class="p">)</span>
<span class="k">case</span> <span class="o">.</span><span class="kt">None</span><span class="p">:</span>
<span class="k">return</span> <span class="n">points</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></p>
<p><span class="c1">/// uses the douglas peuker algorithm to reduce the given arraylist of entries</span>
<span class="c1">/// 使用douglas peuker algorithm 归纳(降低)给定的 实体</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">reduceWithDouglasPeuker</span><span class="p">(</span><span class="nv">entries</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">],</span> <span class="nv">epsilon</span><span class="p">:</span> <span class="kt">Double</span><span class="p">)</span> <span class="o">-></span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]</span>
<span class="p">{</span>
<span class="c1">// if a shape has 2 or less points it cannot be reduced</span>
<span class="c1">// 如果少于2个点,将不能归纳</span>
<span class="k">if</span> <span class="p">(</span><span class="n">epsilon</span> <span class="o"><=</span> <span class="mi">0</span> <span class="o">||</span> <span class="n">entries</span><span class="o">.</span><span class="n">count</span> <span class="o"><</span> <span class="mi">3</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">entries</span>
<span class="p">}</span></p>
<pre><code><span class="k">var</span> <span class="nv">keep</span> <span class="o">=</span> <span class="p">[</span><span class="kt">Bool</span><span class="p">](</span><span class="nv">count</span><span class="p">:</span> <span class="n">entries</span><span class="o">.</span><span class="n">count</span><span class="p">,</span> <span class="nv">repeatedValue</span><span class="p">:</span> <span class="kc">false</span><span class="p">)</span>
<span class="c1">// first and last always stay</span>
<span class="c1">// 保留头和尾</span>
<span class="n">keep</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="kc">true</span>
<span class="n">keep</span><span class="p">[</span><span class="n">entries</span><span class="o">.</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="kc">true</span>
<span class="c1">// first and last entry are entry point to recursion</span>
<span class="c1">// 头和尾作为递归入口</span>
<span class="nf">algorithmDouglasPeucker</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="nv">epsilon</span><span class="p">:</span> <span class="n">epsilon</span><span class="p">,</span> <span class="nv">start</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">entries</span><span class="o">.</span><span class="n">count</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="nv">keep</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">keep</span><span class="p">)</span>
<span class="c1">// create a new array with series, only take the kept ones</span>
<span class="k">var</span> <span class="nv">reducedEntries</span> <span class="o">=</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">]()</span>
<span class="k">for</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="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">entries</span><span class="o">.</span><span class="n">count</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">keep</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
<span class="p">{</span>
<span class="k">let</span> <span class="nv">curEntry</span> <span class="o">=</span> <span class="n">entries</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="n">reducedEntries</span><span class="o">.</span><span class="nf">append</span><span class="p">(</span><span class="kt">ChartDataEntry</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="n">curEntry</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="nv">xIndex</span><span class="p">:</span> <span class="n">curEntry</span><span class="o">.</span><span class="n">xIndex</span><span class="p">))</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">reducedEntries</span>
</code></pre>
<p><span class="p">}</span></p>
<p><span class="c1">/// apply the Douglas-Peucker-Reduction to an ArrayList of Entry with a given epsilon (tolerance)</span>
<span class="c1">///</span>
<span class="c1">/// :param: entries</span>
<span class="c1">/// :param: epsilon as y-value</span>
<span class="c1">/// :param: start</span>
<span class="c1">/// :param: end</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">algorithmDouglasPeucker</span><span class="p">(</span><span class="nv">entries</span><span class="p">:</span> <span class="p">[</span><span class="kt">ChartDataEntry</span><span class="p">],</span> <span class="nv">epsilon</span><span class="p">:</span> <span class="kt">Double</span><span class="p">,</span> <span class="nv">start</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="k">inout</span> <span class="nv">keep</span><span class="p">:</span> <span class="p">[</span><span class="kt">Bool</span><span class="p">])</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">end</span> <span class="o"><=</span> <span class="n">start</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// recursion finished</span>
<span class="k">return</span>
<span class="p">}</span></p>
<pre><code><span class="c1">// find the greatest distance between start and endpoint</span>
<span class="k">var</span> <span class="nv">maxDistIndex</span> <span class="o">=</span> <span class="kt">Int</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">distMax</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="mf">0.0</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">firstEntry</span> <span class="o">=</span> <span class="n">entries</span><span class="p">[</span><span class="n">start</span><span class="p">]</span>
<span class="k">var</span> <span class="nv">lastEntry</span> <span class="o">=</span> <span class="n">entries</span><span class="p">[</span><span class="n">end</span><span class="p">]</span>
<span class="k">for</span> <span class="p">(</span><span class="k">var</span> <span class="nv">i</span> <span class="o">=</span> <span class="n">start</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">end</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">dist</span> <span class="o">=</span> <span class="nf">calcAngleBetweenLines</span><span class="p">(</span><span class="n">firstEntry</span><span class="p">,</span> <span class="nv">end1</span><span class="p">:</span> <span class="n">lastEntry</span><span class="p">,</span> <span class="nv">start2</span><span class="p">:</span> <span class="n">firstEntry</span><span class="p">,</span> <span class="nv">end2</span><span class="p">:</span> <span class="n">entries</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
<span class="c1">// keep the point with the greatest distance</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dist</span> <span class="o">&gt;</span> <span class="n">distMax</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">distMax</span> <span class="o">=</span> <span class="n">dist</span>
<span class="n">maxDistIndex</span> <span class="o">=</span> <span class="n">i</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">distMax</span> <span class="o">&gt;</span> <span class="n">epsilon</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// keep max dist point</span>
<span class="n">keep</span><span class="p">[</span><span class="n">maxDistIndex</span><span class="p">]</span> <span class="o">=</span> <span class="kc">true</span>
<span class="c1">// recursive call</span>
<span class="nf">algorithmDouglasPeucker</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="nv">epsilon</span><span class="p">:</span> <span class="n">epsilon</span><span class="p">,</span> <span class="nv">start</span><span class="p">:</span> <span class="n">start</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">maxDistIndex</span><span class="p">,</span> <span class="nv">keep</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">keep</span><span class="p">)</span>
<span class="nf">algorithmDouglasPeucker</span><span class="p">(</span><span class="n">entries</span><span class="p">,</span> <span class="nv">epsilon</span><span class="p">:</span> <span class="n">epsilon</span><span class="p">,</span> <span class="nv">start</span><span class="p">:</span> <span class="n">maxDistIndex</span><span class="p">,</span> <span class="nv">end</span><span class="p">:</span> <span class="n">end</span><span class="p">,</span> <span class="nv">keep</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">keep</span><span class="p">)</span>
<span class="p">}</span> <span class="c1">// else don't keep the point...</span>
</code></pre>
<p><span class="p">}</span></p>
<p><span class="c1">/// calculate the distance between a line between two entries and an entry (point)</span>
<span class="c1">///</span>
<span class="c1">/// :param: startEntry line startpoint</span>
<span class="c1">/// :param: endEntry line endpoint</span>
<span class="c1">/// :param: entryPoint the point to which the distance is measured from the line</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">calcPointToLineDistance</span><span class="p">(</span><span class="nv">startEntry</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">,</span> <span class="nv">endEntry</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">,</span> <span class="nv">entryPoint</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">xDiffEndStart</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="n">endEntry</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span> <span class="o">-</span> <span class="kt">Double</span><span class="p">(</span><span class="n">startEntry</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">xDiffEntryStart</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="n">entryPoint</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span> <span class="o">-</span> <span class="kt">Double</span><span class="p">(</span><span class="n">startEntry</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span></p>
<pre><code><span class="k">var</span> <span class="nv">normalLength</span> <span class="o">=</span> <span class="nf">sqrt</span><span class="p">((</span><span class="n">xDiffEndStart</span><span class="p">)</span>
<span class="o">*</span> <span class="p">(</span><span class="n">xDiffEndStart</span><span class="p">)</span>
<span class="o">+</span> <span class="p">(</span><span class="n">endEntry</span><span class="o">.</span><span class="n">value</span> <span class="o">-</span> <span class="n">startEntry</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="o">*</span> <span class="p">(</span><span class="n">endEntry</span><span class="o">.</span><span class="n">value</span> <span class="o">-</span> <span class="n">startEntry</span><span class="o">.</span><span class="n">value</span><span class="p">))</span>
<span class="k">return</span> <span class="kt">Double</span><span class="p">(</span><span class="nf">fabs</span><span class="p">((</span><span class="n">xDiffEntryStart</span><span class="p">)</span>
<span class="o">*</span> <span class="p">(</span><span class="n">endEntry</span><span class="o">.</span><span class="n">value</span> <span class="o">-</span> <span class="n">startEntry</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="o">-</span> <span class="p">(</span><span class="n">entryPoint</span><span class="o">.</span><span class="n">value</span> <span class="o">-</span> <span class="n">startEntry</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="o">*</span> <span class="p">(</span><span class="n">xDiffEndStart</span><span class="p">)))</span> <span class="o">/</span> <span class="kt">Double</span><span class="p">(</span><span class="n">normalLength</span><span class="p">)</span>
</code></pre>
<p><span class="p">}</span></p>
<p><span class="c1">/// Calculates the angle between two given lines. The provided entries mark the starting and end points of the lines.</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">calcAngleBetweenLines</span><span class="p">(</span><span class="nv">start1</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">,</span> <span class="nv">end1</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">,</span> <span class="nv">start2</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">,</span> <span class="nv">end2</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">angle1</span> <span class="o">=</span> <span class="nf">calcAngleWithRatios</span><span class="p">(</span><span class="n">start1</span><span class="p">,</span> <span class="nv">p2</span><span class="p">:</span> <span class="n">end1</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">angle2</span> <span class="o">=</span> <span class="nf">calcAngleWithRatios</span><span class="p">(</span><span class="n">start2</span><span class="p">,</span> <span class="nv">p2</span><span class="p">:</span> <span class="n">end2</span><span class="p">)</span></p>
<pre><code><span class="k">return</span> <span class="nf">fabs</span><span class="p">(</span><span class="n">angle1</span> <span class="o">-</span> <span class="n">angle2</span><span class="p">)</span>
</code></pre>
<p><span class="p">}</span></p>
<p><span class="c1">/// calculates the angle between two entries (points) in the chart taking ratios into consideration</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">calcAngleWithRatios</span><span class="p">(</span><span class="nv">p1</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">,</span> <span class="nv">p2</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">dx</span> <span class="o">=</span> <span class="kt">Double</span><span class="p">(</span><span class="n">p2</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span> <span class="o"><em></span> <span class="kt">Double</span><span class="p">(</span><span class="n">deltaRatio</span><span class="p">)</span> <span class="o">-</span> <span class="kt">Double</span><span class="p">(</span><span class="n">p1</span><span class="o">.</span><span class="n">xIndex</span><span class="p">)</span> <span class="o"></em></span> <span class="kt">Double</span><span class="p">(</span><span class="n">deltaRatio</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">dy</span> <span class="o">=</span> <span class="n">p2</span><span class="o">.</span><span class="n">value</span> <span class="o"><em></span> <span class="n">scaleRatio</span> <span class="o">-</span> <span class="n">p1</span><span class="o">.</span><span class="n">value</span> <span class="o"></em></span> <span class="n">scaleRatio</span>
<span class="k">return</span> <span class="nf">atan2</span><span class="p">(</span><span class="kt">Double</span><span class="p">(</span><span class="n">dy</span><span class="p">),</span> <span class="n">dx</span><span class="p">)</span> <span class="o">*</span> <span class="kt">ChartUtils</span><span class="o">.</span><span class="kt">Math</span><span class="o">.</span><span class="kt">RAD2DEG</span>
<span class="p">}</span></p>
<p><span class="c1">// calculates the angle between two entries (points) in the chart</span>
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">calcAngle</span><span class="p">(</span><span class="nv">p1</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">,</span> <span class="nv">p2</span><span class="p">:</span> <span class="kt">ChartDataEntry</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Double</span>
<span class="p">{</span>
<span class="k">var</span> <span class="nv">dx</span> <span class="o">=</span> <span class="n">p2</span><span class="o">.</span><span class="n">xIndex</span> <span class="o">-</span> <span class="n">p1</span><span class="o">.</span><span class="n">xIndex</span>
<span class="k">var</span> <span class="nv">dy</span> <span class="o">=</span> <span class="n">p2</span><span class="o">.</span><span class="n">value</span> <span class="o">-</span> <span class="n">p1</span><span class="o">.</span><span class="n">value</span>
<span class="k">return</span> <span class="nf">atan2</span><span class="p">(</span><span class="kt">Double</span><span class="p">(</span><span class="n">dy</span><span class="p">),</span> <span class="kt">Double</span><span class="p">(</span><span class="n">dx</span><span class="p">))</span> <span class="o">*</span> <span class="kt">ChartUtils</span><span class="o">.</span><span class="kt">Math</span><span class="o">.</span><span class="kt">RAD2DEG</span>
<span class="p">}</span></code></pre></figure></p>
项目第二十七天 iOS-Chart源码分析-12015-08-01T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/08/01/gpxj-27-day<h2>结构</h2>
<hr />
<ul>
<li>动画 : Animation/</li>
<li>图表 : Charts/</li>
<li>组件 : Components/</li>
<li>数据 : Data/</li>
<li>过滤 : Filters/</li>
<li>高亮 : Highlight/</li>
<li>渲染 : Renderers/</li>
<li>工具 : Utils/</li>
</ul>
<h2>文件结构</h2>
<hr />
<p>├── Animation<br/>
│ ├── ChartAnimationEasing.swift<br/>
│ └── ChartAnimator.swift<br/>
├── Charts<br/>
│ ├── BarChartView.swift<br/>
│ ├── BarLineChartViewBase.swift<br/>
│ ├── BubbleChartView.swift<br/>
│ ├── CandleStickChartView.swift<br/>
│ ├── ChartViewBase.swift<br/>
│ ├── CombinedChartView.swift<br/>
│ ├── HorizontalBarChartView.swift<br/>
│ ├── LineChartView.swift<br/>
│ ├── PieChartView.swift<br/>
│ ├── PieRadarChartViewBase.swift<br/>
│ ├── RadarChartView.swift<br/>
│ └── ScatterChartView.swift<br/>
├── Components<br/>
│ ├── ChartAxisBase.swift<br/>
│ ├── ChartComponentBase.swift<br/>
│ ├── ChartLegend.swift<br/>
│ ├── ChartLimitLine.swift<br/>
│ ├── ChartMarker.swift<br/>
│ ├── ChartXAxis.swift<br/>
│ └── ChartYAxis.swift<br/>
├── Data<br/>
│ ├── BarChartData.swift<br/>
│ ├── BarChartDataEntry.swift<br/>
│ ├── BarChartDataSet.swift<br/>
│ ├── BarLineScatterCandleChartData.swift<br/>
│ ├── BarLineScatterCandleChartDataSet.swift<br/>
│ ├── BubbleChartData.swift<br/>
│ ├── BubbleChartDataEntry.swift<br/>
│ ├── BubbleChartDataSet.swift<br/>
│ ├── CandleChartData.swift<br/>
│ ├── CandleChartDataEntry.swift<br/>
│ ├── CandleChartDataSet.swift<br/>
│ ├── ChartData.swift<br/>
│ ├── ChartDataEntry.swift<br/>
│ ├── ChartDataSet.swift<br/>
│ ├── CombinedChartData.swift<br/>
│ ├── LineChartData.swift<br/>
│ ├── LineChartDataSet.swift<br/>
│ ├── LineRadarChartDataSet.swift<br/>
│ ├── LineScatterCandleChartDataSet.swift<br/>
│ ├── PieChartData.swift<br/>
│ ├── PieChartDataSet.swift<br/>
│ ├── RadarChartData.swift<br/>
│ ├── RadarChartDataSet.swift<br/>
│ ├── ScatterChartData.swift<br/>
│ └── ScatterChartDataSet.swift<br/>
├── Filters<br/>
│ ├── ChartDataApproximatorFilter.swift<br/>
│ └── ChartDataBaseFilter.swift<br/>
├── Highlight<br/>
│ ├── BarChartHighlighter.swift<br/>
│ ├── ChartHighlight.swift<br/>
│ ├── ChartHighlighter.swift<br/>
│ ├── ChartRange.swift<br/>
│ └── HorizontalBarChartHighlighter.swift<br/>
├── Renderers<br/>
│ ├── BarChartRenderer.swift<br/>
│ ├── BubbleChartRenderer.swift<br/>
│ ├── CandleStickChartRenderer.swift<br/>
│ ├── ChartAxisRendererBase.swift<br/>
│ ├── ChartDataRendererBase.swift<br/>
│ ├── ChartLegendRenderer.swift<br/>
│ ├── ChartRendererBase.swift<br/>
│ ├── ChartXAxisRenderer.swift<br/>
│ ├── ChartXAxisRendererBarChart.swift<br/>
│ ├── ChartXAxisRendererHorizontalBarChart.swift<br/>
│ ├── ChartXAxisRendererRadarChart.swift<br/>
│ ├── ChartYAxisRenderer.swift<br/>
│ ├── ChartYAxisRendererHorizontalBarChart.swift<br/>
│ ├── ChartYAxisRendererRadarChart.swift<br/>
│ ├── CombinedChartRenderer.swift<br/>
│ ├── HorizontalBarChartRenderer.swift<br/>
│ ├── LineChartRenderer.swift<br/>
│ ├── LineScatterCandleRadarChartRenderer.swift<br/>
│ ├── PieChartRenderer.swift<br/>
│ ├── RadarChartRenderer.swift<br/>
│ └── ScatterChartRenderer.swift<br/>
└── Utils<br/>
├── ChartColorTemplates.swift<br/>
├── ChartFillFormatter.swift<br/>
├── ChartSelectionDetail.swift<br/>
├── ChartTransformer.swift<br/>
├── ChartTransformerHorizontalBarChart.swift<br/>
├── ChartUtils.swift<br/>
└── ChartViewPortHandler.swift</p>
<p>8 directories, 81 files</p>
项目第二十六天 iOS App研发的最后冲刺:内测与部署2015-07-31T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/31/gpxj-26-day<p>今天晚上8:00参加了CSDN举办的 《iOS App研发的最后冲刺:内测与部署》。</p>
<p>主要介绍了</p>
<ul>
<li><strong>苹果开发者帐号</strong>的种类及管理。</li>
<li>测试版发布</li>
<li>Crash信息收集</li>
<li>应用上架被拒绝及申请加速审核</li>
</ul>
<p>网上的一些有用的资源</p>
<p>iOS发布流程 <a href="https://github.com/leecade/ios-dev-flow">https://github.com/leecade/ios-dev-flow</a></p>
<p>iOS app的版本号自动管理 <a href="http://blog.fir.im/ios-version/">http://blog.fir.im/ios-version/</a></p>
<p>命令行一键上传到fir.im <a href="https://github.com/FIRHQ/fir-cli">https://github.com/FIRHQ/fir-cli</a></p>
项目第二十五天 IBInspectable / IBDesignable2015-07-30T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/30/gpxj-25-day<h2>原文: <a href="http://nshipster.cn/ibinspectable-ibdesignable/">IBInspectable / IBDesignable</a></h2>
<hr />
<p>展示,而不是描述。眼见为实。一图胜千言。</p>
<p>无论陈词滥调多少次,比起一个需要我们记住并且输入什么的界面来说,如果替换成我们能够看见并可控制的界面的话将会是巨大的进步。 Xcode 6 提供了这样一个替代,在旧技术上建立新的互动。在设计项目的时候建立一个自定义的界面使你可以配置自定义控制并将它们实时显示出来,用 IBInspectable 和 IBDesignable,这将成为可能。</p>
<h2>IBInspectable</h2>
<hr />
<p>IBInspectable 属性提供了访问旧功能的新方式:用户自定义的运行时属性。从目前的身份检查器(identity inspector)中访问,这些属性在 Interface Builder 被整合到 Xcode 之前就可用了。他们提供了一个强有力的机制来配置一个 NIB,XIB,或者 storyboard 实例中的任何键值编码(key-value coded)属性:</p>
<p><img src="/assets/img/ios/gpxj/25/1.png" alt="1" /></p>
<p>User-Defined Runtime Attributes</p>
<p>虽然功能强大,运行时属性可能会使工作很繁琐。一个属性的关键字路径,类型和属性值需要在每个实例设置,没有任何自动完成或输入提示,这就需要前往文档或自定义子类的源代码仔细检查设置。 IBInspectable 属性彻底的解决了这个问题:在 Xcode 6,你现在可以指定任何属性作为可检查项并为你的自定义类建立了一个用户界面。</p>
<p>例如,在一个 UIView 子类里,这些属性用它们的值来更新背景层:</p>
<p>Swift</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">@IBInspectable</span> <span class="k">var</span> <span class="nv">cornerRadius</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span> <span class="p">{</span>
<span class="k">didSet</span> <span class="p">{</span>
<span class="n">layer</span><span class="o">.</span><span class="n">cornerRadius</span> <span class="o">=</span> <span class="n">cornerRadius</span>
<span class="n">layer</span><span class="o">.</span><span class="n">masksToBounds</span> <span class="o">=</span> <span class="n">cornerRadius</span> <span class="o">></span> <span class="mi">0</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">@IBInspectable</span> <span class="k">var</span> <span class="nv">borderWidth</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="o">=</span> <span class="mi">0</span> <span class="p">{</span>
<span class="k">didSet</span> <span class="p">{</span>
<span class="n">layer</span><span class="o">.</span><span class="n">borderWidth</span> <span class="o">=</span> <span class="n">borderWidth</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">@IBInspectable</span> <span class="k">var</span> <span class="nv">borderColor</span><span class="p">:</span> <span class="kt">UIColor</span><span class="p">?</span> <span class="p">{</span>
<span class="k">didSet</span> <span class="p">{</span>
<span class="n">layer</span><span class="o">.</span><span class="n">borderColor</span> <span class="o">=</span> <span class="n">borderColor</span><span class="p">?</span><span class="o">.</span><span class="kt">CGColor</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure></p>
<p>标有 @IBInspectable(或是 Objective-C 中的 IBInspectable),他们就可以很容易在 Interface Builder 的观察面板(inspector panel)里编辑。需要注意的是 Xcode 在这里做了更多的事,属性名称是从 camel- 转换为 title- 模式 并且相关的名称组合在一起:</p>
<p><img src="/assets/img/ios/gpxj/25/2.png" alt="2" /></p>
<p>IBInspectable Attribute Inspector</p>
<p>因为可检查属性仅仅是用户定义的运行时属性顶部的接口,所以支持相同的类型列表:布尔,字符串和数字(即,NSNumber 或任何数值类型),以及 CGPoint、CGSize、CGRect、UIColor 和 NSRange,额外增加了 UIImage。</p>
<blockquote><p>那些已经熟悉运行时属性的人将注意到在上面的例子中有一些问题。UIColor 是里面唯一支持色彩的类型,而不是原生支持视图 CALayer 的 CGColor。borderColor 会计算 UIColor 属性(通过运行时属性设置)并映射到该层需要的 CGColor。</p></blockquote>
<h3>让现有的类型可观察</h3>
<p>内置的 Cocoa 类型如果在 Interface Builder 中的属性检查器中没有列出也可以通过扩展来使属性可视。如果你喜欢圆角,你一定会喜欢这个 UIView 扩展:</p>
<p>Swift</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">extension</span> <span class="kt">UIView</span> <span class="p">{</span>
<span class="kd">@IBInspectable</span> <span class="k">var</span> <span class="nv">cornerRadius</span><span class="p">:</span> <span class="kt">CGFloat</span> <span class="p">{</span>
<span class="k">get</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">layer</span><span class="o">.</span><span class="n">cornerRadius</span>
<span class="p">}</span>
<span class="k">set</span> <span class="p">{</span>
<span class="n">layer</span><span class="o">.</span><span class="n">cornerRadius</span> <span class="o">=</span> <span class="n">newValue</span>
<span class="n">layer</span><span class="o">.</span><span class="n">masksToBounds</span> <span class="o">=</span> <span class="n">newValue</span> <span class="o">></span> <span class="mi">0</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure></p>
<p>变!你创建的任何 UIView 都将有一个可配置的边界半径。</p>
<h2>IBDesignable</h2>
<hr />
<p>如果这还不够,IBDesignable 自定义视图也在 Xcode 6 中亮相了。当应用到 UIView 或 NSView 子类中的时候,@ IBDesignable 让 Interface Builder 知道它应该在画布上直接渲染视图。你会看到你的自定义视图在每次更改后不必编译并运行你的应用程序就会显示。</p>
<p>标记一个自定义视图为 IBDesignable,只需在类名前加上 @IBDesignable 的前缀(或是 Objective-C 里的 IB_DESIGNABLE 宏)。你的初始化、布置和绘制方法将被用来在画布上渲染你的自定义视图:</p>
<p>Swift</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">@IBDesignable</span>
<span class="kd">class</span> <span class="kt">MyCustomView</span><span class="p">:</span> <span class="kt">UIView</span> <span class="p">{</span>
<span class="o">...</span>
<span class="p">}</span></code></pre></figure></p>
<p><img src="/assets/img/ios/gpxj/25/3.png" alt="3" /></p>
<p>IBDesignable Live Preview</p>
<p>从这个功能上节约的时间是不能被低估的。加上 IBInspectable 属性,一个设计师或开发人员可以轻松地调整自定义控件的呈现,以得到她想要的确切的结果。任何改变,无论是从代码或属性检查器中,都将立即呈现在画布上。</p>
<p>此外,任何问题都是可避开编译和运行整个程序来调试的。调试的方法很简单,只需在你的代码中设置一个断点,在 Interface Builder 中选择视图,并选择 Editor ➔ Debug Selected Views。</p>
<p>由于在 Interface Builder 中呈现自定义视图不会有应用程序的完整上下文,你可能需要生成模拟数据以便显示,例如一个默认用户头像图片或仿制的天气数据。有两种方法可以为这个特殊的上下文添加代码:</p>
<blockquote><p>prepareForInterfaceBuilder():此方法与你代码的其余部分一起编译,但只有当视图正在准备在 Interface Builder 显示时执行。</p>
<p>TARGET_INTERFACE_BUILDER:#if TARGET_INTERFACE_BUILDER 预处理宏在 Objective-C 或 Swift 下都是工作的,它会视情况编译正确代码:</p></blockquote>
<p>Swift</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="cp">#if !TARGET_INTERFACE_BUILDER</span>
<span class="c1">// this code will run in the app itself</span>
<span class="cp">#else</span>
<span class="c1">// this code will execute only in IB</span>
<span class="cp">#endif</span></code></pre></figure></p>
<h3>IBCalculatorConstructorSet</h3>
<hr />
<p>把自定义 IBDesignable 视图和视图里的 IBInspectable 属性结合在一起,你能干点啥?作为一个例子,让我们更新老式经典 Apple folklore:在“Steve Jobs Roll Your Own Calculator Construction Set”,Xcode 6 的风格:</p>
<p><img src="/assets/img/ios/gpxj/25/4.gif" alt="4" /></p>
<p>Calculator Construction Set</p>
<p>现在你差不多已经眼见为实了,那让我们来看看更多的图片吧。你用这些强大的新工具创造了什么?把你的 IBInspectable 或 IBDesignable 创作加上话题 #IBInspectable po 成一张图片,我们都可以看看还可以学到些什么。</p>
项目第二十四天 Using Unwind Segues2015-07-29T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/29/gpxj-24-day<h2>原文:<a href="https://developer.apple.com/library/ios/technotes/tn2298/_index.html">Using Unwind Segues</a></h2>
<hr />
<p>Technical Note TN2298
Using Unwind Segues</p>
<p>This document provides an overview of unwind segues.</p>
<ul>
<li>Introduction</li>
<li>Creating an Unwind Segue</li>
<li>Adding an Unwind Segue to a Storyboard</li>
<li>The Unwind Process</li>
<li>How an Unwind Segue Determines its Destination View Controller</li>
<li>Responsibilities for Container View Controllers</li>
<li>Selecting a Child View Controller to Handle An Unwind Action</li>
<li>Transitioning Between Two Child View Controllers</li>
<li>Additional Resources</li>
<li>Document Revision History</li>
</ul>
<h2>Introduction</h2>
<hr />
<p>Unwind segues give you a way to "unwind" the navigation stack back through push, modal, popover, and other types of segues. You use unwind segues to "go back" one or more steps in your navigation hierarchy. Unlike a normal segue, which create a new instance of their destination view controller and transitions to it, an unwind segue transitions to an existing view controller in your navigation hierarchy. Callbacks are provided to both the source and destination view controller before the transition begins. You can use these callbacks to pass data between the view controllers.</p>
<p>When you create an unwind segue in Interface Builder you do not specify a destination view controller. The unwind segue determines which view controller in your navigation hierarchy to unwind to when it is triggered at runtime. This process is customizable and additional points of customization are provided for container view controllers to participate in the unwind process. See How an Unwind Segue Determines its Destination View Controller.</p>
<h3>Creating an Unwind Segue</h3>
<p>Before you can begin adding unwind segues in Interface Builder, you must define at least one unwind action. An unwind action is an instance method with a UIStoryboardSegue as its only parameter and whose return type is IBAction. There are no requirements for the method name, but you should choose a name that is both unique and meaningful within the context of your entire application. Like IBActions and IBOutlets, unwind actions do not need to be declared in your class header file. The exception is if you are writing a framework and want clients of your framework to be able to create an unwind segue that targets your action.</p>
<p><strong>Listing 1</strong> Example definition of an unwind action.</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">/<em> Objective-C </em>/
- (IBAction)unwindToMainMenu:(UIStoryboardSegue<em>)sender
{
UIViewController </em>sourceViewController = sender.sourceViewController;
// Pull any data from the view controller which initiated the unwind segue.
}</p>
<p>/<em> Swift </em>/
@IBAction func unwindToMainMenu(sender: UIStoryboardSegue)
{
let sourceViewController = sender.sourceViewController
// Pull any data from the view controller which initiated the unwind segue.
}</code></pre></figure></p>
<p>When you create the unwind segue in your storyboard, you specify the name of an unwind action in the view controller you want the segue to unwind to. This unwind action is invoked just before the unwind segue is performed. You can access the sourceViewController of the UIStoryboardSegue parameter to retrieve any data from the view controller that initiated the unwind segue.</p>
<p>Adding an Unwind Segue to a Storyboard
To add an unwind segue to a scene, control+drag from an object (such as a button, a table view cell, or a tab view item) in the scene to the scene's exit icon, highlighted in Figure 1. Choose an unwind action for the new segue from the popup menu.</p>
<p><strong>Figure 1</strong> The exit icon for a scene is the orange icon on the right.</p>
<p><img src="/assets/img/ios/gpxj/24/1.png" alt="1" /></p>
<p>To add an unwind segue that will only be triggered programmatically, control+drag from the scene's view controller icon to its exit icon as shown in Figure 2. Choose an unwind action for the new segue from the popup menu. You must also give the segue an identifier so that it can be referenced by your code. An unwind segue can be triggered by sending a -performSegueWithIdentifier:sender: message to the scene's view controller.</p>
<p><strong>Figure 2</strong> Creating a programmatically initiated unwind segue.</p>
<p><img src="/assets/img/ios/gpxj/24/2.png" alt="2" /></p>
<p>Once you have added an unwind segue, it will appear in the outline for the scene, as shown in Figure 3.</p>
<p><strong>Figure 3</strong> Unwind segues for each scene are displayed on the shelf.</p>
<p><img src="/assets/img/ios/gpxj/24/3.png" alt="3" /></p>
<h3>The Unwind Process</h3>
<p>This section describes the process that occurs just after an unwind segue is initiated, either by the user interacting with a control in a scene or your code sending a performSegueWithIdentifier:sender: message to a view controller.</p>
<ol>
<li>The unwind segue traverses your navigation hierarchy to locate the destination view controller. This step is described in detail under How an Unwind Segue Determines its Destination View Controller.</li>
<li>The view controller that initiated the unwind segue is sent a prepareForSegue:sender: message. The default implementation does nothing. Your view controller can override this method to pass data to the destination view controller.</li>
<li>The destination view controller's unwind action is invoked.</li>
<li>The unwind segue animates the visual transition between the source and destination view controller.</li>
</ol>
<p>Container view controllers have additional responsibilities. If you are implementing a custom container view controller, see Responsibilities for Container View Controllers to learn how to your custom container view controller must participate in the unwind process.</p>
<h4>How an Unwind Segue Determines its Destination View Controller</h4>
<p>When an unwind segue is initiated, it must first locate the nearest view controller in the navigation hierarchy which implements the unwind action specified when the unwind segue was created. This view controller becomes the destination of the unwind segue. If no suitable view controller is found, the unwind segue is aborted.</p>
<p>Starting from the view controller that initiated the unwind segue the search order is as follows:</p>
<ol>
<li><p>The next view controller in the responder chain is sent a viewControllerForUnwindSegueAction:fromViewController:withSender: message. For a view controller presented modally, this will be the view controller that called presentViewController:animated:completion:. Otherwise, the parentViewController.</p>
<p>The default implementation searches the receiver's childViewControllers array for a view controller that wants to handle the unwind action. If none of the receiver's child view controllers want to handle the unwind action, the receiver checks whether it wants to handle the unwind action and returns self if it does. In both cases, the canPerformUnwindSegueAction:fromViewController:withSender: method is used to determine if a given view controller wants to handle the unwind action.</p></li>
<li><p>If no view controller is returned from viewControllerForUnwindSegueAction:fromViewController:withSender: in step one, the search repeats from the next view controller in the responder chain.</p></li>
</ol>
<p>The default implementation of canPerformUnwindSegueAction:fromViewController:withSender: returns YES if the receiver responds to the provided action. A view controller can override canPerformUnwindSegueAction:fromViewController:withSender: if additional granularity is required to determine whether the view controller can be the destination of an unwind segue. This may be necessary in order to remove ambiguity caused by having multiple instances of the same view controller on your navigation stack.</p>
<blockquote><p>Important: If your view controller overrides canPerformUnwindSegueAction:fromViewController:withSender:, your implementation should only return YES if the receiver wants to handle the unwind action. Do not return YES on behalf of a child view controller.</p></blockquote>
<h4>Responsibilities for Container View Controllers</h4>
<p>Container view controllers have two responsibilities in the unwind process, both discussed below. If you are using a container view controller provided by the SDK (UINavigationController, UITabBarController, ...) these are handled automatically.</p>
<h3>Selecting a Child View Controller to Handle An Unwind Action</h3>
<p>A container view controller should implement the method shown in Listing 2 if custom logic is required to determine which of the container's child view controllers, if any, want to handle the unwind action. For example, UINavigationController's implementation searches the view controllers on its navigation stack in reverse order beginning from the top. If none of the container's child view controllers want to handle the unwind action, invoke the super's implementation and return the result.</p>
<p><strong>Listing 2</strong> Override this method to return the child view controller that wants to handle the unwind action.</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">/<em> Objective-C </em>/
- (UIViewController <em>)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController </em>)fromViewController withSender:(id)sender</code></pre></figure></p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="cm">/<em> Swift </em>/</span>
<span class="k">override</span> <span class="kd">func</span> <span class="nf">viewControllerForUnwindSegueAction</span><span class="p">(</span><span class="nv">action</span><span class="p">:</span> <span class="kt">Selector</span><span class="p">,</span> <span class="nv">fromViewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="n">withSender</span> <span class="nv">sender</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">?)</span> <span class="o">-></span> <span class="kt">UIViewController</span><span class="p">?</span></code></pre></figure></p>
<blockquote><p>Note: Always use canPerformUnwindSegueAction:fromViewController:withSender: to determine whether a child view controller wants to handle the unwind action.</p></blockquote>
<h4>Transitioning Between Two Child View Controllers</h4>
<p>Once an unwind segue has located its destination view controller, it attempts to locate an unwind executor for the transition. The unwind executor is the common ancestor of both the view controller which initiated the unwind segue and the destination view controller. It is responsible for creating a UIStoryboardSegue which will be used to perform the visual transition between the source and destination view controllers.</p>
<p>When your container returns a view controller from viewControllerForUnwindSegueAction:fromViewController:withSender: the container becomes the unwind executor for the transition. Your container view controller must therefore implement the method shown in Listing 3 to return a UIStoryboardSegue that performs any animations and other steps are needed to transition from the view controller which initiated the unwind segue (fromViewController) to the destination view controller (toViewController). The returned object should be an instance of UIStoryboardSegue created using segueWithIdentifier:source:destination:performHandler: or your own subclass.</p>
<p><strong>Listing 3</strong> Implement this method to return a UIStoryboardSegue that transitions between fromViewController and toViewController.</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">/<em> Objective-C </em>/
- (UIStoryboardSegue <em>)segueForUnwindingToViewController:(UIViewController </em>)toViewController fromViewController:(UIViewController <em>)fromViewController identifier:(NSString </em>)identifier</code></pre></figure></p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="cm">/<em> Swift </em>/</span>
<span class="k">override</span> <span class="kd">func</span> <span class="nf">segueForUnwindingToViewController</span><span class="p">(</span><span class="nv">toViewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="nv">fromViewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="nv">identifier</span><span class="p">:</span> <span class="kt">String</span><span class="p">?)</span> <span class="o">-></span> <span class="kt">UIStoryboardSegue</span></code></pre></figure></p>
<blockquote><p>Note: There may be additional view controllers between the container view controller and fromViewController. You should not assume that fromViewController is an immediate child view controller of the container.</p></blockquote>
<h2>Additional Resources</h2>
<hr />
<p>Unwind segues are discussed in the WWDC 2012: <a href="https://developer.apple.com/videos/wwdc/2012/?id=407">Adopting Storyboards in Your App</a> session.</p>
<p>Examples of using unwind segues can be found in the <a href="https://developer.apple.com/library/ios/samplecode/UnwindSegue/Introduction/Intro.html">UnwindSegue</a> sample code</p>
<h3>Document Revision History</h3>
<table>
<thead>
<tr>
<th>Date </th>
<th> Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>2015-02-04 </td>
<td> Editorial changes.</td>
</tr>
<tr>
<td>2013-08-13 </td>
<td> New document that describes using unwind segues for reverse navigation to existing view controller instances.</td>
</tr>
</tbody>
</table>
项目第二十三天 关于iOS多线程,你看我就够了2015-07-28T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/28/gpxj-23-day<h2>原文:<a href="http://www.jianshu.com/p/0b0d9b1f1f19">关于iOS多线程,你看我就够了</a></h2>
<hr />
<p>在这篇文章中,我将为你整理一下 iOS 开发中几种多线程方案,以及其使用方法和注意事项。当然也会给出几种多线程的案例,在实际使用中感受它们的区别。还有一点需要说明的是,这篇文章将会使用 Swift 和 Objective-c 两种语言讲解,双语幼儿园。OK,let's begin!</p>
<h2>概述</h2>
<hr />
<p>这篇文章中,我不会说多线程是什么、线程和进程的区别、多线程有什么用,当然我也不会说什么是串行、什么是并行等问题,这些我们应该都知道的。</p>
<p>在 iOS 中其实目前有 4 套多线程方案,他们分别是:</p>
<ol>
<li>Pthreads</li>
<li>NSThread</li>
<li>GCD</li>
<li>NSOperation & NSOperationQueue</li>
</ol>
<p>所以接下来,我会一一讲解这些方案的使用方法和一些案例。在将这些内容的时候,我也会顺带说一些多线程周边产品。比如: 线程同步、 延时执行、 单例模式 等等。</p>
<h2>Pthreads</h2>
<hr />
<p>其实这个方案不用说的,只是拿来充个数,为了让大家了解一下就好了。百度百科里是这么说的:</p>
<blockquote><p>POSIX线程(POSIX threads),简称Pthreads,是线程的POSIX标准。该标准定义了创建和操纵线程的一整套API。在类Unix操作系统(Unix、Linux、Mac OS X等)中,都使用Pthreads作为操作系统的线程。
简单地说,这是一套在很多操作系统上都通用的多线程API,所以移植性很强(然并卵),当然在 iOS 中也是可以的。不过这是基于 c语言 的框架,使用起来这酸爽!感受一下:</p></blockquote>
<p><strong>OBJECTIVE-C</strong></p>
<p>当然第一步要包含头文件</p>
<blockquote><h1>import <pthread.h></h1></blockquote>
<p>然后创建线程,并执行任务</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)touchesBegan:(NSSet <em>)touches withEvent:(UIEvent </em>)event {
pthread_t thread;
//创建一个线程并自动执行
pthread_create(&thread, NULL, start, NULL);
}</p>
<p>void <em>start(void </em>data) {
NSLog(@"%@", [NSThread currentThread]);</p>
<pre><code>return NULL;
</code></pre>
<p>}</code></pre></figure></p>
<p>打印输出:</p>
<blockquote><p>2015-07-27 23:57:21.689 testThread[10616:2644653] <NSThread: 0x7fbb48d33690>{number = 2, name = (null)}</p></blockquote>
<p>看代码就会发现他需要 c语言函数,这是比较蛋疼的,更蛋疼的是你需要手动处理线程的各个状态的转换即管理生命周期,比如,这段代码虽然创建了一个线程,但并没有销毁。</p>
<p><strong>SWIFT</strong></p>
<p>很遗憾,在我目前的 swift1.2 中无法执行这套方法,原因是这个函数需要传入一个函数指针 CFunctionPointer<T> 类型,但是目前 swift 无法将方法转换成此类型。听说 swift 2.0 引入一个新特性 @convention(c), 可以完成 Swift 方法转换成 c 语言指针的。在这里可以看到</p>
<p>那么,Pthreads 方案的多线程我就介绍这么多,毕竟做 iOS 开发几乎不可能用到。但是如果你感兴趣的话,或者说想要自己实现一套多线程方案,从底层开始定制,那么可以去搜一下相关资料。</p>
<h2>NSThread</h2>
<hr />
<p>这套方案是经过苹果封装后的,并且完全面向对象的。所以你可以直接操控线程对象,非常直观和方便。但是,它的生命周期还是需要我们手动管理,所以这套方案也是偶尔用用,比如 [NSThread currentThread],它可以获取当前线程类,你就可以知道当前线程的各种属性,用于调试十分方便。下面来看看它的一些用法。</p>
<p><strong>创建并启动</strong></p>
<p>先创建线程类,再启动</p>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">// 创建
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];</p>
<p>// 启动
[thread start];</code></pre></figure></p>
<p><strong>SWIFT</strong></p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//创建</span>
<span class="k">let</span> <span class="nv">thread</span> <span class="o">=</span> <span class="kt">NSThread</span><span class="p">(</span><span class="nv">target</span><span class="p">:</span> <span class="k">self</span><span class="p">,</span> <span class="nv">selector</span><span class="p">:</span> <span class="s">"run:"</span><span class="p">,</span> <span class="nv">object</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span></p>
<p><span class="c1">//启动</span>
<span class="n">thread</span><span class="o">.</span><span class="nf">start</span><span class="p">()</span></code></pre></figure></p>
<ul>
<li>创建并自动启动</li>
</ul>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];</code></pre></figure></p>
<p><strong>SWIFT</strong></p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kt">NSThread</span><span class="o">.</span><span class="nf">detachNewThreadSelector</span><span class="p">(</span><span class="s">"run:"</span><span class="p">,</span> <span class="nv">toTarget</span><span class="p">:</span> <span class="k">self</span><span class="p">,</span> <span class="nv">withObject</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span></code></pre></figure></p>
<ul>
<li>使用 NSObject 的方法创建并自动启动</li>
</ul>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">[self performSelectorInBackground:@selector(run:) withObject:nil];</code></pre></figure></p>
<p><strong>SWIFT</strong></p>
<p>很遗憾 too! 苹果认为 performSelector: 不安全,所以在 Swift 去掉了这个方法。</p>
<blockquote><p>Note: The performSelector: method and related selector-invoking methods are not imported in Swift because they are inherently unsafe.</p></blockquote>
<h3>其他方法</h3>
<p>除了创建启动外,NSThread 还以很多方法,下面我列举一些常见的方法,当然我列举的并不完整,更多方法大家可以去类的定义里去看。</p>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">//取消线程
- (void)cancel;</p>
<p>//启动线程
- (void)start;</p>
<p>//判断某个线程的状态的属性
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isCancelled) BOOL cancelled;</p>
<p>//设置和获取线程名字
-(void)setName:(NSString <em>)n;
-(NSString </em>)name;</p>
<p>//获取当前线程信息
+ (NSThread *)currentThread;</p>
<p>//获取主线程信息
+ (NSThread *)mainThread;</p>
<p>//使当前线程暂停一段时间,或者暂停到某个时刻
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;</code></pre></figure></p>
<p><strong>SWIFT</strong></p>
<p>Swift的方法名字和OC的方法名都一样,我就不浪费空间列举出来了。</p>
<p>其实,NSThread 用起来也挺简单的,因为它就那几种方法。同时,我们也只有在一些非常简单的场景才会用 NSThread, 毕竟它还不够智能,不能优雅地处理多线程中的其他高级概念。所以接下来要说的内容才是重点。</p>
<h2>GCD</h2>
<hr />
<p>Grand Central Dispatch,听名字就霸气。它是苹果为多核的并行运算提出的解决方案,所以会自动合理地利用更多的CPU内核(比如双核、四核),最重要的是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,我们只需要告诉干什么就行。同时它使用的也是 c语言,不过由于使用了 Block(Swift里叫做闭包),使得使用起来更加方便,而且灵活。所以基本上大家都使用 GCD 这套方案,老少咸宜,实在是居家旅行、杀人灭口,必备良药。不好意思,有点中二,咱们继续。</p>
<p>任务和队列</p>
<p>在 GCD 中,加入了两个非常重要的概念: <em>任务</em> 和 <em>队列</em>。</p>
<ul>
<li>任务:即操作,你想要干什么,说白了就是一段代码,在 GCD 中就是一个 Block,所以添加任务十分方便。任务有两种执行方式: 同步执行 和 异步执行,他们之间的区别是 是否会创建新的线程。</li>
</ul>
<blockquote><p>更新:</p>
<p>这里说的并不准确,同步(sync) 和 异步(async) 的主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕!</p>
<p>如果是 同步(sync) 操作,它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行。</p>
<p>如果是 异步(async)操作,当前线程会直接往下执行,它不会阻塞当前线程。</p></blockquote>
<ul>
<li>队列:用于存放任务。一共有两种队列, <em>串行队列</em> 和 <em>并行队列</em>。</li>
</ul>
<p><strong>串行队列</strong> 中的任务会根据队列的定义 FIFO 的执行,一个接一个的先进先出的进行执行。</p>
<blockquote><p>更新:放到串行队列的任务,GCD 会 FIFO(先进先出) 地取出来一个,执行一个,然后取下一个,这样一个一个的执行。</p></blockquote>
<p><strong>并行队列</strong> 中的任务</p>
<blockquote><p>更新:放到串行队列的任务,GCD 也会 FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。这样由于取的动作很快,忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。</p></blockquote>
<p>虽然很绕,但请看下表:</p>
<table>
<thead>
<tr>
<th style="text-align:left;"> </th>
<th style="text-align:center;">同步执行</th>
<th style="text-align:right;">异步执行</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"> 串行队列 </td>
<td style="text-align:center;"> 当前线程,一个一个执行 </td>
<td style="text-align:right;"> 其他线程,一个一个执行 </td>
</tr>
<tr>
<td style="text-align:left;"> 并行队列 </td>
<td style="text-align:center;"> 当前线程,一个一个执行 </td>
<td style="text-align:right;"> 开很多线程,一起执行 </td>
</tr>
</tbody>
</table>
<h3>创建队列</h3>
<ul>
<li><strong>主队列</strong>:这是一个特殊的 串行队列。什么是主队列,大家都知道吧,它用于刷新 UI,任何需要刷新 UI 的工作都要在主队列执行,所以一般耗时的任务都要放到别的线程执行。</li>
</ul>
<p>//OBJECTIVE-C</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">dispatch_queue_t queue = ispatch_get_main_queue();</code></pre></figure></p>
<p>//SWIFT</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">queue</span> <span class="o">=</span> <span class="nf">ispatch_get_main_queue</span><span class="p">()</span></code></pre></figure></p>
<ul>
<li><strong>自己创建的队列</strong>:其中第一个参数是标识符,用于 DEBUG 的时候标识唯一的队列,可以为空。大家可以看xcode的文档查看参数意义。</li>
</ul>
<blockquote><p>更新:自己可以创建 串行队列, 也可以创建 并行队列。看下面的代码(代码已更新),它有两个参数,第一个上面已经说了,第二个才是最重要的。</p>
<p>第二个参数用来表示创建的队列是串行的还是并行的,传入 DISPATCH_QUEUE_SERIAL 或 NULL 表示创建串行队列。传入 DISPATCH_QUEUE_CONCURRENT 表示创建并行队列。</p></blockquote>
<p>//OBJECTIVE-C</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">//串行队列
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);
//并行队列
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);</code></pre></figure></p>
<p>//SWIFT</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//串行队列</span>
<span class="k">let</span> <span class="nv">queue</span> <span class="o">=</span> <span class="nf">dispatch_queue_create</span><span class="p">(</span><span class="s">"tk.bourne.testQueue"</span><span class="p">,</span> <span class="kc">nil</span><span class="p">);</span>
<span class="k">let</span> <span class="nv">queue</span> <span class="o">=</span> <span class="nf">dispatch_queue_create</span><span class="p">(</span><span class="s">"tk.bourne.testQueue"</span><span class="p">,</span> <span class="kt">DISPATCH_QUEUE_SERIAL</span><span class="p">)</span>
<span class="c1">//并行队列</span>
<span class="k">let</span> <span class="nv">queue</span> <span class="o">=</span> <span class="nf">dispatch_queue_create</span><span class="p">(</span><span class="s">"tk.bourne.testQueue"</span><span class="p">,</span> <span class="kt">DISPATCH_QUEUE_CONCURRENT</span><span class="p">)</span></code></pre></figure></p>
<ul>
<li><strong>全局并行队列</strong>:只要是并行任务一般都加入到这个队列。这是系统提供的一个并发队列。</li>
</ul>
<p>//OBJECTIVE-C</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);</code></pre></figure></p>
<p>//SWIFT</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">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></code></pre></figure></p>
<h3>创建任务</h3>
<ul>
<li><strong>同步任务</strong>:会阻塞当前线程 (SYNC)</li>
</ul>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">dispatch_sync(<#queue#>, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});</code></pre></figure></p>
<p><strong>SWIFT</strong></p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="nf">dispatch_sync</span><span class="p">(</span><span class="o"><</span><span class="err">#</span><span class="n">queue</span><span class="err">#</span><span class="o">></span><span class="p">,</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
<span class="c1">//code here</span>
<span class="nf">println</span><span class="p">(</span><span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="p">})</span></code></pre></figure></p>
<p>异步任务:会另开线程 改:不会阻塞当前线程 (ASYNC)</p>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">dispatch_async(<#queue#>, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});</code></pre></figure></p>
<p><strong>SWIFT</strong></p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="nf">dispatch_async</span><span class="p">(</span><span class="o"><</span><span class="err">#</span><span class="n">queue</span><span class="err">#</span><span class="o">></span><span class="p">,</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
<span class="c1">//code here</span>
<span class="nf">println</span><span class="p">(</span><span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="p">})</span></code></pre></figure></p>
<blockquote><p>更新:</p>
<p>为了更好的理解同步和异步,和各种队列的使用,下面看两个示例:</p></blockquote>
<h4>示例一:</h4>
<p>以下代码在主线程调用,结果是什么?</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">NSLog("之前 - %@", NSThread.currentThread())
dispatch_sync(dispatch_get_main_queue(), { () -> Void in
NSLog("sync - %@", NSThread.currentThread())
})
NSLog("之后 - %@", NSThread.currentThread())</code></pre></figure></p>
<p><strong>答案:</strong></p>
<p>只会打印第一句:之前 - <NSThread: 0x7fb3a9e16470>{number = 1, name = main} ,然后主线程就卡死了,你可以在界面上放一个按钮,你就会发现点不了了。</p>
<p><strong>解释:</strong></p>
<p>同步任务会阻塞当前线程,然后把 Block 中的任务放到指定的队列中执行,只有等到 Block 中的任务完成后才会让当前线程继续往下运行。</p>
<p>那么这里的步骤就是:打印完第一句后,dispatch_sync 立即阻塞当前的主线程,然后把 Block 中的任务放到 main_queue 中,可以 main_queue 中的任务会被取出来放到主线程中执行,但主线程这个时候已经被阻塞了,所以 Block 中的任务就不能完成,它不完成,dispatch_sync 就会一直阻塞主线程,这就是死锁现象。导致主线程一直卡死。</p>
<h4>示例二:</h4>
<p>以下代码会产生什么结果?</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">queue</span> <span class="o">=</span> <span class="nf">dispatch_queue_create</span><span class="p">(</span><span class="s">"myQueue"</span><span class="p">,</span> <span class="kt">DISPATCH_QUEUE_SERIAL</span><span class="p">)</span></p>
<p><span class="kt">NSLog</span><span class="p">(</span><span class="s">"之前 - %@"</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span></p>
<p><span class="nf">dispatch_async</span><span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
<span class="kt">NSLog</span><span class="p">(</span><span class="s">"sync之前 - %@"</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="nf">dispatch_sync</span><span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
<span class="kt">NSLog</span><span class="p">(</span><span class="s">"sync - %@"</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="p">})</span>
<span class="kt">NSLog</span><span class="p">(</span><span class="s">"sync之后 - %@"</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="p">})</span></p>
<p><span class="kt">NSLog</span><span class="p">(</span><span class="s">"之后 - %@"</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span></code></pre></figure></p>
<p><strong>答案:</strong></p>
<blockquote><p>2015-07-30 02:06:51.058 test[33329:8793087] 之前 - <NSThread: 0x7fe32050dbb0>{number = 1, name = main}</p>
<p>2015-07-30 02:06:51.059 test[33329:8793356] sync之前 - <NSThread: 0x7fe32062e9f0>{number = 2, name = (null)}</p>
<p>2015-07-30 02:06:51.059 test[33329:8793087] 之后 - <NSThread: 0x7fe32050dbb0>{number = 1, name = main}</p></blockquote>
<p>很明显 sync - %@ 和 sync之后 - %@ 没有被打印出来!这是为什么呢?我们再来分析一下:</p>
<p><strong>分析:</strong></p>
<p>我们按执行顺序一步步来哦:</p>
<ol>
<li>使用 DISPATCH_QUEUE_SERIAL 这个参数,创建了一个 串行队列。</li>
<li>打印出 之前 - %@ 这句。</li>
<li>dispatch_async 异步执行,所以当前线程不会被阻塞,于是有了两条线程,一条当前线程继续往下打印出 之后 - %@这句, 另一台执行 Block 中的内容打印 sync之前 - %@ 这句。因为这两条是并行的,所以打印的先后顺序无所谓。</li>
<li>注意,高潮来了。现在的情况和上一个例子一样了。dispatch_sync同步执行,于是它所在的线程会被阻塞,一直等到 sync 里的任务执行完才会继续往下。于是 sync 就高兴的把自己 Block 中的任务放到 queue 中,可谁想 queue 是一个串行队列,一次执行一个任务,所以 sync 的 Block 必须等到前一个任务执行完毕,可万万没想到的是 queue 正在执行的任务就是被 sync 阻塞了的那个。于是又发生了死锁。所以 sync 所在的线程被卡死了。剩下的两句代码自然不会打印。</li>
</ol>
<h4>队列组</h4>
<p>队列组可以将很多队列添加到一个组里,这样做的好处是,当这个组里所有的任务都执行完了,队列组会通过一个方法通知我们。下面是使用方法,这是一个很实用的功能。</p>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">//1.创建队列组
dispatch_group_t group = dispatch_group_create();
//2.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);</p>
<p>//3.多次使用队列组的方法执行任务, 只有异步方法
//3.1.执行3次循环
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-01 - %@", [NSThread currentThread]);
}
});</p>
<p>//3.2.主队列执行8次循环
dispatch_group_async(group, dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 8; i++) {
NSLog(@"group-02 - %@", [NSThread currentThread]);
}
});</p>
<p>//3.3.执行5次循环
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"group-03 - %@", [NSThread currentThread]);
}
});</p>
<p>//4.都完成后会自动通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});</code></pre></figure></p>
<p><strong>SWIFT</strong></p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//1.创建队列组</span>
<span class="k">let</span> <span class="nv">group</span> <span class="o">=</span> <span class="nf">dispatch_group_create</span><span class="p">()</span>
<span class="c1">//2.创建队列</span>
<span class="k">let</span> <span class="nv">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></p>
<pre><code><span class="c1">//3.多次使用队列组的方法执行任务, 只有异步方法</span>
<span class="c1">//3.1.执行3次循环</span>
<span class="nf">dispatch_group_async</span><span class="p">(</span><span class="n">group</span><span class="p">,</span> <span class="n">queue</span><span class="p">)</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Void</span> <span class="k">in</span>
<span class="k">for</span> <span class="n">_</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..&lt;</span><span class="mi">3</span> <span class="p">{</span>
<span class="kt">NSLog</span><span class="p">(</span><span class="s">"group-01 - %@"</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre>
<p><span class="c1">//3.2.主队列执行8次循环</span>
<span class="nf">dispatch_group_async</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="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
<span class="k">for</span> <span class="n">_</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..<</span><span class="mi">8</span> <span class="p">{</span>
<span class="kt">NSLog</span><span class="p">(</span><span class="s">"group-02 - %@"</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="p">}</span>
<span class="p">}</span></p>
<p><span class="c1">//3.3.执行5次循环</span>
<span class="nf">dispatch_group_async</span><span class="p">(</span><span class="n">group</span><span class="p">,</span> <span class="n">queue</span><span class="p">)</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
<span class="k">for</span> <span class="n">_</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..<</span><span class="mi">5</span> <span class="p">{</span>
<span class="kt">NSLog</span><span class="p">(</span><span class="s">"group-03 - %@"</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="p">}</span>
<span class="p">}</span></p>
<p><span class="c1">//4.都完成后会自动通知</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="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
<span class="kt">NSLog</span><span class="p">(</span><span class="s">"完成 - %@"</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="p">}</span></code></pre></figure></p>
<p>打印结果</p>
<blockquote><p>2015-07-28 03:40:34.277 test[12540:3319271] group-03 - <NSThread: 0x7f9772536f00>{number = 3, name = (null)}</p>
<p>2015-07-28 03:40:34.277 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}</p>
<p>2015-07-28 03:40:34.277 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}</p>
<p>2015-07-28 03:40:34.277 test[12540:3319271] group-03 - <NSThread: 0x7f9772536f00>{number = 3, name = (null)}</p>
<p>2015-07-28 03:40:34.278 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}</p>
<p>2015-07-28 03:40:34.278 test[12540:3319271] group-03 - <NSThread: 0x7f9772536f00>{number = 3, name = (null)}</p>
<p>2015-07-28 03:40:34.278 test[12540:3319271] group-03 - <NSThread: 0x7f9772536f00>{number = 3, name = (null)}</p>
<p>2015-07-28 03:40:34.278 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}</p>
<p>2015-07-28 03:40:34.277 test[12540:3319273] group-01 - <NSThread: 0x7f977272e8d0>{number = 2, name = (null)}</p>
<p>2015-07-28 03:40:34.278 test[12540:3319271] group-03 - <NSThread: 0x7f9772536f00>{number = 3, name = (null)}</p>
<p>2015-07-28 03:40:34.278 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}</p>
<p>2015-07-28 03:40:34.278 test[12540:3319273] group-01 - <NSThread: 0x7f977272e8d0>{number = 2, name = (null)}</p>
<p>2015-07-28 03:40:34.278 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}</p>
<p>2015-07-28 03:40:34.278 test[12540:3319273] group-01 - <NSThread: 0x7f977272e8d0>{number = 2, name = (null)}</p>
<p>2015-07-28 03:40:34.279 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}</p>
<p>2015-07-28 03:40:34.279 test[12540:3319146] group-02 - <NSThread: 0x7f977240ba60>{number = 1, name = main}</p>
<p>2015-07-28 03:40:34.279 test[12540:3319146] 完成 - <NSThread: 0x7f977240ba60>{number = 1, name = main}</p></blockquote>
<p>这些就是 GCD 的基本功能,但是它的能力远不止这些,等讲完 NSOperation 后,我们再来看看它的一些其他方面用途。而且,只要你想象力够丰富,你可以组合出更好的用法。</p>
<blockquote><p>更新:关于GCD,还有两个需要说的:</p>
<ul>
<li>func dispatch_barrier_async(_ queue: dispatch_queue_t, _ block: dispatch_block_t):</li>
</ul>
<p>这个方法重点是你传入的 queue,当你传入的 queue 是通过 DISPATCH_QUEUE_CONCURRENT 参数自己创建的 queue 时,这个方法会阻塞这个 queue(注意是阻塞 queue ,而不是阻塞当前线程),一直等到这个 queue 中排在它前面的任务都执行完成后才会开始执行自己,自己执行完毕后,再会取消阻塞,使这个 queue 中排在它后面的任务继续执行。</p>
<p>如果你传入的是其他的 queue, 那么它就和 dispatch_async 一样了。</p>
<ul>
<li>func dispatch_barrier_sync(_ queue: dispatch_queue_t, _ block: dispatch_block_t):</li>
</ul>
<p>这个方法的使用和上一个一样,传入 自定义的并发队列(DISPATCH_QUEUE_CONCURRENT),它和上一个方法一样的阻塞 queue,不同的是 这个方法还会 阻塞当前线程。</p>
<p>如果你传入的是其他的 queue, 那么它就和 dispatch_sync 一样了。</p></blockquote>
<h2>NSOperation和NSOperationQueue</h2>
<hr />
<p>NSOperation 是苹果公司对 GCD 的封装,完全面向对象,所以使用起来更好理解。 大家可以看到 NSOperation 和 NSOperationQueue 分别对应 GCD 的 任务 和 队列 。操作步骤也很好理解:</p>
<ol>
<li>将要执行的任务封装到一个 NSOperation 对象中。</li>
<li>将此任务添加到一个 NSOperationQueue 对象中。</li>
</ol>
<p>然后系统就会自动在执行任务。至于同步还是异步、串行还是并行请继续往下看:</p>
<p><strong>添加任务</strong></p>
<p>值得说明的是,NSOperation 只是一个抽象类,所以不能封装任务。但它有 2 个子类用于封装任务。分别是:NSInvocationOperation 和 NSBlockOperation 。创建一个 Operation 后,需要调用 start 方法来启动任务,它会 默认在当前队列同步执行。当然你也可以在中途取消一个任务,只需要调用其 cancel 方法即可。</p>
<ul>
<li>NSInvocationOperation : 需要传入一个方法名。</li>
</ul>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">//1.创建NSInvocationOperation对象
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];</p>
<p>//2.开始执行
[operation start];</code></pre></figure></p>
<p><strong>SWIFT</strong></p>
<p>在 Swift 构建的和谐社会里,是容不下 NSInvocationOperation 这种不是类型安全的败类的。苹果如是说。这里有相关解释</p>
<p>NSBlockOperation</p>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">//1.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];</p>
<p>//2.开始任务
[operation start];</code></pre></figure></p>
<p><strong>SWIFT</strong></p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//1.创建NSBlockOperation对象</span>
<span class="k">let</span> <span class="nv">operation</span> <span class="o">=</span> <span class="kt">NSBlockOperation</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
<span class="nf">println</span><span class="p">(</span><span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="p">}</span></p>
<p><span class="c1">//2.开始任务</span>
<span class="n">operation</span><span class="o">.</span><span class="nf">start</span><span class="p">()</span></code></pre></figure></p>
<p>之前说过这样的任务,默认会在当前线程执行。但是 NSBlockOperation 还有一个方法:addExecutionBlock: ,通过这个方法可以给 Operation 添加多个执行 Block。这样 Operation 中的任务 会并发执行,它会 在主线程和其它的多个线程 执行这些任务,注意下面的打印结果:</p>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">//1.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];</p>
<p>//添加多个Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}</p>
<p>//2.开始任务
[operation start];
<strong>SWIFT</strong>
//1.创建NSBlockOperation对象
let operation = NSBlockOperation { () -> Void in
NSLog("%@", NSThread.currentThread())
}</p>
<p>//2.添加多个Block
for i in 0..<5 {
operation.addExecutionBlock { () -> Void in
NSLog("第%ld次 - %@", i, NSThread.currentThread())
}
}</p>
<p>//2.开始任务
operation.start()</code></pre></figure></p>
<p><em>*打印输出</em>8</p>
<blockquote><p>2015-07-28 17:50:16.585 test[17527:4095467] 第2次 - <NSThread: 0x7ff5c9701910>{number = 1, name = main}</p>
<p>2015-07-28 17:50:16.585 test[17527:4095666] 第1次 - <NSThread: 0x7ff5c972caf0>{number = 4, name = (null)}</p>
<p>2015-07-28 17:50:16.585 test[17527:4095665] <NSThread: 0x7ff5c961b610>{number = 3, name = (null)}</p>
<p>2015-07-28 17:50:16.585 test[17527:4095662] 第0次 - <NSThread: 0x7ff5c948d310>{number = 2, name = (null)}</p>
<p>2015-07-28 17:50:16.586 test[17527:4095666] 第3次 - <NSThread: 0x7ff5c972caf0>{number = 4, name = (null)}</p>
<p>2015-07-28 17:50:16.586 test[17527:4095467] 第4次 - <NSThread: 0x7ff5c9701910>{number = 1, name = main}</p></blockquote>
<pre><code>**NOTE**:addExecutionBlock 方法必须在 start() 方法之前执行,否则就会报错:
</code></pre>
<blockquote><p>‘*** -[NSBlockOperation addExecutionBlock:]: blocks cannot be added after the operation has started executing or finished'</p></blockquote>
<pre><code>**NOTE**:大家可能发现了一个问题,为什么我在 Swift 里打印输出使用 NSLog() 而不是 println() 呢?原因是使用 print() / println() 输出的话,它会简单地使用 流(stream) 的概念,学过 C++ 的都知道。它会把需要输出的每个字符一个一个的输出到控制台。普通使用并没有问题,可是当多线程同步输出的时候问题就来了,由于很多 println() 同时打印,就会导致控制台上的字符混乱的堆在一起,而NSLog() 就没有这个问题。到底是什么样子的呢?你可以把上面 NSLog() 改为 println() ,然后一试便知。 更多 NSLog() 与 println() 的区别看这里
</code></pre>
<ul>
<li><p>自定义Operation</p>
<p> 除了上面的两种 Operation 以外,我们还可以自定义 Operation。自定义 Operation 需要继承 NSOperation 类,并实现其 main() 方法,因为在调用 start() 方法的时候,内部会调用 main() 方法完成相关逻辑。所以如果以上的两个类无法满足你的欲望的时候,你就需要自定义了。你想要实现什么功能都可以写在里面。除此之外,你还需要实现 cancel() 在内的各种方法。所以这个功能提供给高级玩家,我在这里就不说了,等我需要用到时在研究它,到时候可能会再做更新。</p></li>
</ul>
<p><strong>创建队列</strong></p>
<p>看过上面的内容就知道,我们可以调用一个 NSOperation 对象的 start() 方法来启动这个任务,但是这样做他们默认是 同步执行 的。就算是 addExecutionBlock 方法,也会在 当前线程和其他线程 中执行,也就是说还是会占用当前线程。这是就要用到队列 NSOperationQueue 了。而且,按类型来说的话一共有两种类型:主队列、其他队列。只要添加到队列,会自动调用任务的 start() 方法</p>
<ul>
<li>主队列</li>
</ul>
<p>细心的同学就会发现,每套多线程方案都会有一个主线程(当然啦,说的是iOS中,像 pthread 这种多系统的方案并没有,因为 UI线程 理论需要每种操作系统自己定制)。这是一个特殊的线程,必须串行。所以添加到主队列的任务都会一个接一个地排着队在主线程处理。</p>
<p>//OBJECTIVE-C</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">NSOperationQueue *queue = [NSOperationQueue mainQueue];</code></pre></figure></p>
<p>//SWIFT</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">queue</span> <span class="o">=</span> <span class="kt">NSOperationQueue</span><span class="o">.</span><span class="nf">mainQueue</span><span class="p">()</span></code></pre></figure></p>
<ul>
<li>其他队列</li>
</ul>
<p>因为主队列比较特殊,所以会单独有一个类方法来获得主队列。那么通过初始化产生的队列就是其他队列了,因为只有这两种队列,除了主队列,其他队列就不需要名字了。</p>
<blockquote><p>注意:其他队列的任务会在其他线程并行执行。</p></blockquote>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">//1.创建一个其他队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];</p>
<p>//2.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];</p>
<p>//3.添加多个Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}</p>
<p>//4.队列添加任务
[queue addOperation:operation];</code></pre></figure></p>
<p><strong>SWIFT</strong></p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//1.创建其他队列</span>
<span class="k">let</span> <span class="nv">queue</span> <span class="o">=</span> <span class="kt">NSOperationQueue</span><span class="p">()</span></p>
<pre><code><span class="c1">//2.创建NSBlockOperation对象</span>
<span class="k">let</span> <span class="nv">operation</span> <span class="o">=</span> <span class="kt">NSBlockOperation</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Void</span> <span class="k">in</span>
<span class="kt">NSLog</span><span class="p">(</span><span class="s">"%@"</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="p">}</span>
</code></pre>
<p><span class="c1">//3.添加多个Block</span>
<span class="k">for</span> <span class="n">i</span> <span class="k">in</span> <span class="mi">0</span><span class="o">..<</span><span class="mi">5</span> <span class="p">{</span>
<span class="n">operation</span><span class="o">.</span><span class="n">addExecutionBlock</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
<span class="kt">NSLog</span><span class="p">(</span><span class="s">"第%ld次 - %@"</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="p">}</span>
<span class="p">}</span></p>
<p><span class="c1">//4.队列添加任务</span>
<span class="n">queue</span><span class="o">.</span><span class="nf">addOperation</span><span class="p">(</span><span class="n">operation</span><span class="p">)</span></code></pre></figure></p>
<p><strong>打印输出</strong></p>
<blockquote><p>2015-07-28 20:26:28.463 test[18622:4443534] <NSThread: 0x7fd022c3ac10>{number = 5, name = (null)}</p>
<p>2015-07-28 20:26:28.463 test[18622:4443536] 第2次 - <NSThread: 0x7fd022e36d50>{number = 2, name = (null)}</p>
<p>2015-07-28 20:26:28.463 test[18622:4443535] 第0次 - <NSThread: 0x7fd022f237f0>{number = 4, name = (null)}</p>
<p>2015-07-28 20:26:28.463 test[18622:4443533] 第1次 - <NSThread: 0x7fd022d372b0>{number = 3, name = (null)}</p>
<p>2015-07-28 20:26:28.463 test[18622:4443534] 第3次 - <NSThread: 0x7fd022c3ac10>{number = 5, name = (null)}</p>
<p>2015-07-28 20:26:28.463 test[18622:4443536] 第4次 - <NSThread: 0x7fd022e36d50>{number = 2, name = (null)}</p></blockquote>
<p>OK, 这时应该发问了,大家将 NSOperationQueue 与 GCD的队列 相比较就会发现,这里没有并行队列,那如果我想要10个任务在其他线程串行的执行怎么办?</p>
<p>这就是苹果封装的妙处,你不用管串行、并行、同步、异步这些名词。NSOperationQueue 有一个参数 maxConcurrentOperationCount 最大并发数,用来设置最多可以让多少个任务同时执行。当你把它设置为 1 的时候,他不就是串行了嘛!</p>
<p>NSOperationQueue 还有一个添加任务的方法,- (void)addOperationWithBlock:(void (^)(void))block; ,这是不是和 GCD 差不多?这样就可以添加一个任务到队列中了,十分方便。</p>
<p>NSOperation 有一个非常实用的功能,那就是添加依赖。比如有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就可以用到依赖了:</p>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">//1.任务一:下载图片
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载图片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];</p>
<p>//2.任务二:打水印
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"打水印 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];</p>
<p>//3.任务三:上传图片
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"上传图片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];</p>
<p>//4.设置依赖
[operation2 addDependency:operation1]; //任务二依赖任务一
[operation3 addDependency:operation2]; //任务三依赖任务二</p>
<p>//5.创建队列并加入任务
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];</code></pre></figure></p>
<p><strong>SWIFT</strong></p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//1.任务一:下载图片</span>
<span class="k">let</span> <span class="nv">operation1</span> <span class="o">=</span> <span class="kt">NSBlockOperation</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
<span class="kt">NSLog</span><span class="p">(</span><span class="s">"下载图片 - %@"</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="kt">NSThread</span><span class="o">.</span><span class="nf">sleepForTimeInterval</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span>
<span class="p">}</span></p>
<p><span class="c1">//2.任务二:打水印</span>
<span class="k">let</span> <span class="nv">operation2</span> <span class="o">=</span> <span class="kt">NSBlockOperation</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
<span class="kt">NSLog</span><span class="p">(</span><span class="s">"打水印 - %@"</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="kt">NSThread</span><span class="o">.</span><span class="nf">sleepForTimeInterval</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span>
<span class="p">}</span></p>
<p><span class="c1">//3.任务三:上传图片</span>
<span class="k">let</span> <span class="nv">operation3</span> <span class="o">=</span> <span class="kt">NSBlockOperation</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
<span class="kt">NSLog</span><span class="p">(</span><span class="s">"上传图片 - %@"</span><span class="p">,</span> <span class="kt">NSThread</span><span class="o">.</span><span class="nf">currentThread</span><span class="p">())</span>
<span class="kt">NSThread</span><span class="o">.</span><span class="nf">sleepForTimeInterval</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span>
<span class="p">}</span></p>
<p><span class="c1">//4.设置依赖</span>
<span class="n">operation2</span><span class="o">.</span><span class="nf">addDependency</span><span class="p">(</span><span class="n">operation1</span><span class="p">)</span> <span class="c1">//任务二依赖任务一</span>
<span class="n">operation3</span><span class="o">.</span><span class="nf">addDependency</span><span class="p">(</span><span class="n">operation2</span><span class="p">)</span> <span class="c1">//任务三依赖任务二</span></p>
<p><span class="c1">//5.创建队列并加入任务</span>
<span class="k">let</span> <span class="nv">queue</span> <span class="o">=</span> <span class="kt">NSOperationQueue</span><span class="p">()</span>
<span class="n">queue</span><span class="o">.</span><span class="nf">addOperations</span><span class="p">([</span><span class="n">operation3</span><span class="p">,</span> <span class="n">operation2</span><span class="p">,</span> <span class="n">operation1</span><span class="p">],</span> <span class="nv">waitUntilFinished</span><span class="p">:</span> <span class="kc">false</span><span class="p">)</span></code></pre></figure></p>
<p><strong>打印结果</strong></p>
<blockquote><p>2015-07-28 21:24:28.622 test[19392:4637517] 下载图片 - <NSThread: 0x7fc10ad4d970>{number = 2, name = (null)}</p>
<p>2015-07-28 21:24:29.622 test[19392:4637515] 打水印 - <NSThread: 0x7fc10af20ef0>{number = 3, name = (null)}</p>
<p>2015-07-28 21:24:30.627 test[19392:4637515] 上传图片 - <NSThread: 0x7fc10af20ef0>{number = 3, name = (null)}</p>
<p>注意:不能添加相互依赖,会死锁,比如 A依赖B,B依赖A。
可以使用 removeDependency 来解除依赖关系。
可以在不同的队列之间依赖,反正就是这个依赖是添加到任务身上的,和队列没关系。</p></blockquote>
<h4>其他方法</h4>
<p>以上就是一些主要方法, 下面还有一些常用方法需要大家注意:</p>
<ul>
<li>NSOperation</li>
</ul>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">BOOL executing; //判断任务是否正在执行</p>
<p>BOOL finished; //判断任务是否完成</p>
<p>void (^completionBlock)(void); //用来设置完成后需要执行的操作</p>
<ul>
<li><p>(void)cancel; //取消任务</p></li>
<li><p>(void)waitUntilFinished; //阻塞当前线程直到此任务执行完毕
NSOperationQueue</code></pre></figure></p></li>
<li><p>NSUInteger operationCount; //获取队列的任务数</p></li>
</ul>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)cancelAllOperations; //取消队列中所有的任务</p>
<ul>
<li>(void)waitUntilAllOperationsAreFinished; //阻塞当前线程直到此队列中的所有任务执行完毕</li>
</ul>
<p>[queue setSuspended:YES]; // 暂停queue</p>
<p>[queue setSuspended:NO]; // 继续queue</code></pre></figure></p>
<p>好啦,到这里差不多就讲完了。当然,我讲的并不完整,可能有一些知识我并没有讲到,但作为常用方法,这些已经足够了。不过我在这里只是告诉你了一些方法的功能,只是怎么把他们用到合适的地方,就需要多多实践了。下面我会说一些关于多线程的案例,是大家更加什么地了解。</p>
<h4>其他用法</h4>
<p>在这部分,我会说一些和多线程知识相关的案例,可能有些很简单,大家早都知道的,不过因为这篇文章讲的是多线程嘛,所以应该尽可能的全面嘛。还有就是,我会尽可能的使用多种方法实现,让大家看看其中的区别。</p>
<h3>线程同步</h3>
<p>所谓线程同步就是为了防止多个线程抢夺同一个资源造成的数据安全问题,所采取的一种措施。当然也有很多实现方法,请往下看:</p>
<ul>
<li>互斥锁 :给需要同步的代码块加一个互斥锁,就可以保证每次只有一个线程访问此代码块。</li>
</ul>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@synchronized(self) {
//需要执行的代码块
}</code></pre></figure></p>
<p><strong>SWIFT</strong></p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="nf">objc_sync_enter</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
<span class="c1">//需要执行的代码块</span>
<span class="nf">objc_sync_exit</span><span class="p">(</span><span class="k">self</span><span class="p">)</span></code></pre></figure></p>
<ul>
<li>同步执行 :我们可以使用多线程的知识,把多个线程都要执行此段代码添加到同一个串行队列,这样就实现了线程同步的概念。当然这里可以使用 GCD 和 NSOperation 两种方案,我都写出来。</li>
</ul>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c"><strong>OBJECTIVE-C</strong>
//GCD
//需要一个全局变量queue,要让所有线程的这个操作都加到一个queue中
dispatch_sync(queue, ^{
NSInteger ticket = lastTicket;
[NSThread sleepForTimeInterval:0.1];
NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
ticket -= 1;
lastTicket = ticket;
});</p>
<p>//NSOperation & NSOperationQueue
//重点:1. 全局的 NSOperationQueue, 所有的操作添加到同一个queue中
// 2. 设置 queue 的 maxConcurrentOperationCount 为 1
// 3. 如果后续操作需要Block中的结果,就需要调用每个操作的waitUntilFinished,阻塞当前线程,一直等到当前操作完成,才允许执行后面的。waitUntilFinished 要在添加到队列之后!</p>
<p>NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSInteger ticket = lastTicket;
[NSThread sleepForTimeInterval:1];
NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
ticket -= 1;
lastTicket = ticket;
}];</p>
<p>[queue addOperation:operation];</p>
<p>[operation waitUntilFinished];</code></pre></figure></p>
<p>//后续要做的事
<strong>SWIFT</strong>
这里的 swift 代码,我就不写了,因为每句都一样,只是语法不同而已,照着 OC 的代码就能写出 Swift 的。这篇文章已经老长老长了,我就不浪费篇幅了,又不是高中写作文。</p>
<h3>延迟执行</h3>
<p>所谓延迟执行就是延时一段时间再执行某段代码。下面说一些常用方法。</p>
<ul>
<li>perform</li>
</ul>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">// 3秒后自动调用self的run:方法,并且传递参数:@"abc"
[self performSelector:@selector(run:) withObject:@"abc" afterDelay:3];</code></pre></figure></p>
<p><strong>SWIFT</strong>
之前就已经说过,Swift 里去掉了这个方法。</p>
<ul>
<li>GCD</li>
</ul>
<p>可以使用 GCD 中的 dispatch_after 方法,OC 和 Swift 都可以使用,这里只写 OC 的,Swift 的是一样的。</p>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">// 创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 设置延时,单位秒
double delay = 3;</p>
<p>dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{
// 3秒后需要执行的任务
});</code></pre></figure></p>
<ul>
<li>NSTimer</li>
</ul>
<p>NSTimer 是iOS中的一个计时器类,除了延迟执行还有很多用法,不过这里直说延迟执行的用法。同样只写 OC 版的,Swift 也是相同的。</p>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run:) userInfo:@"abc" repeats:NO];</code></pre></figure></p>
<h3>单例模式</h3>
<p>至于什么是单例模式,我也不多说,我只说说一般怎么实现。在 Objective-C 中,实现单例的方法已经很具体了,虽然有别的方法,但是一般都是用一个标准的方法了,下面来看看。</p>
<p><strong>OBJECTIVE-C</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@interface Tool : NSObject <NSCopying></p>
<ul>
<li>(instancetype)sharedTool;</li>
</ul>
<p>@end</p>
<p>@implementation Tool</p>
<p>static id _instance;</p>
<ul>
<li><p>(instancetype)sharedTool {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[Tool alloc] init];
});</p>
<p> return _instance;
}</p></li>
</ul>
<p>@end</code></pre></figure></p>
<p>这里之所以将单例模式,是因为其中用到了 GCD 的 dispatch_once 方法。下面看 Swift 中的单例模式,在Swift中单例模式非常简单!想知道怎么从 OC 那么复杂的方法变成下面的写法的,请看这里</p>
<p><strong>SWIFT</strong></p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">Tool</span><span class="p">:</span> <span class="kt">NSObject</span> <span class="p">{</span>
<span class="kd">static</span> <span class="k">let</span> <span class="nv">sharedTool</span> <span class="o">=</span> <span class="kt">Tool</span><span class="p">()</span></p>
<pre><code><span class="c1">// 私有化构造方法,阻止其他对象使用这个类的默认的'()'构造方法</span>
<span class="kd">private</span> <span class="k">override</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<h3>从其他线程回到主线程的方法</h3>
<p>我们都知道在其他线程操作完成后必须到主线程更新UI。所以,介绍完所有的多线程方案后,我们来看看有哪些方法可以回到主线程。</p>
<ul>
<li>NSThread</li>
</ul>
<p>//Objective-C</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];</code></pre></figure></p>
<p>//Swift
//swift 取消了 performSelector 方法。</p>
<ul>
<li>GCD</li>
</ul>
<p>//Objective-C</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">dispatch_async(dispatch_get_main_queue(), ^{</p>
<p>});</code></pre></figure></p>
<p>//Swift</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><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="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span></p>
<p><span class="p">})</span></code></pre></figure></p>
<ul>
<li>NSOperationQueue</li>
</ul>
<p>//Objective-C</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">[[NSOperationQueue mainQueue] addOperationWithBlock:^{</p>
<p>}];</code></pre></figure></p>
<p>//Swift</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kt">NSOperationQueue</span><span class="o">.</span><span class="nf">mainQueue</span><span class="p">()</span><span class="o">.</span><span class="n">addOperationWithBlock</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span></p>
<p><span class="p">}</span></code></pre></figure></p>
<h2>总结</h2>
<hr />
<p>好的吧,总算写完了,纯手敲6k多字,感动死我了。花了两天,时间跨度有点大,所以可能有些地方上段不接下段或者有的地方不完整,如果你看着比较费力或者有什么地方有问题,都可以在评论区告诉我,我会及时修改的。当然啦,多线程的东西也不止这些,题目也就只是个题目,不要当真。想要了解更多的东西,还得自己去网上挖掘相关资料。多看看官方文档。实在是编不下去了,大家好好看~。对了,看我写的这么卖力,不打赏的话得点个喜欢也是极好的。</p>
<blockquote><p>更新:第一次放出来的时候,有很多地方有错误,很感谢有朋友提出来了。如果你看到有错误的地方,一定记得指出来,这样对大家都有帮助。还有一点对初学者来说,遇到不懂的方法,最好的办法就是查看官方文档,那里是最准确的,就算有几个单词不认识,查一下就好了,不会影响对整体的理解。</p></blockquote>
<p>我看到有网站转载了我的文章,但转载的可能存在问题,而我只能在简书上更新,所以如果要看 完整版本 还是到简书来看吧:<a href="http://www.jianshu.com/p/0b0d9b1f1f19">这里是地址</a>。</p>
项目第二十二天 使用Storyboards开发的10个小技巧2015-07-27T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/27/gpxj-22-day<h2>原文:<a href="http://www.xmcgraw.com/10-practical-tips-for-ios-developers-using-storyboards/">原文10 Practical Tips for iOS Developers Using Storyboards</a></h2>
<h2>翻译: <a href="http://www.cocoachina.com/ios/20150727/12772.html">使用Storyboards开发的10个小技巧</a></h2>
<hr />
<p>在这里我将着重讲述10件事情,而不会去全面讲述如何使用Storyboard去创建一些事物。这些知识点没有特定的顺序,但它们也许可以帮到你在这条道路上前行。</p>
<p>Storyboard是我花时间钻研最多的一个领域。我非常喜欢可视化编程。只需要简单地将项目拖到画布中,更新位置信息,再设置一些描述信息,就已创建了一个用户界面而不用编写任何代码。这非常重要,因为用户界面的代码可以很快让你的代码变得一团糟。</p>
<p>当我参与到一个新的项目时,我首先要做的就是找到其中的Storyboard。这是概览程序整体框架的一处重要之地。</p>
<p>假如没有使用可视化编辑器,你就需要手动化操作才能发现工作的进展。你在代码中来回往复耗费大量时间,也只能大致了解给定的视图的情况。您可以明确参考设计文件或运行应用程序并导航到所需的区域,但我更希望避免这种情况。最后,在某些情况下,调整界面组件成为一个繁琐的过程。你不断编译并运行应用程序去验证它们是否都在正确地位置,而不是通过Storyboard快速调整。</p>
<p><img src="/assets/img/ios/gpxj/22/1.png" alt="1" /></p>
<p>看看,只是创建右边这么一个简单的界面就编写了这么多的代码,我甚至还没有编写任何自动布局代码来帮助我们定位。我知道会有顽固的代码狂人。但我还是不想这些让我的代码膨胀,这实在是让人不快。不要误会我。对于初学者来说,这样做的价值是理解如何通过代码创建一个用户界面。通过一个给定的用户界面,你有了一个大概的认识,知道它能做什么。而不是查阅文档。</p>
<h2>列表</h2>
<hr />
<h3>也许可以归为一件,不过不用在意</h3>
<p>你不需要将整个程序搭建在一个故事板中。可以将它们分成好几个故事板。假设有管理面板,设置面板,以及主面板。当你的程序扩大后可以节省你的很多精力。在团队中工作时与故事板交互也会变得轻松,而且查找需要的故事板也会更快捷。</p>
<h3>什么是exit segue,如何使用它</h3>
<p>首先,我说一下什么是segue。假设你现在的故事板中有两个场景,其中第一个场景中有一个按钮。当你右击场景1的按钮,然后拖动到场景2,你这样就是创建了一个segue。</p>
<p><img src="/assets/img/ios/gpxj/22/2.png" alt="2" /></p>
<p>现在假设我们选择present modally。该模态表示这是用户优先关注的场景。这没有简单的方法来回到第一个场景,就像你可能看过的如果你推送一个场景到导航栈上。我们可以创建一个委托来通知第一个控制器我们已经完成。但这样有些乏味,我们也可以发送通知给第一个控制器,但这就是有点大材小用。这是一个机会使用exit segue,exit segue就像segue一样工作,不同的是会返回到执行UIStoryboardSegue动作的地方。这就是关键!</p>
<p>通过你创建的segue,exit segue可以一直后退导航,无论segue是在那创建的,都可以找到exit segue。</p>
<p>查看一下下方的完整地视图结构。</p>
<p><img src="/assets/img/ios/gpxj/22/3.png" alt="3" /></p>
<p>如果你只是右击按钮的exit segue指示器,你不会看到任何东西。它需要检测你在目标控制器中创建一个UIStoryboardSegue动作的方法(绿色箭头指向)。例如:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">@IBAction</span> <span class="kd">func</span> <span class="nf">unwindToSceneA</span><span class="p">(</span><span class="nv">unwindSegue</span><span class="p">:</span> <span class="kt">UIStoryboardSegue</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// be sure to give your unwind segue an identifier so you know where we're coming from </span>
<span class="p">}</span></code></pre></figure></p>
<h3>Storyboard(故事板)跳转</h3>
<p>你无需在故事板中绘制漂亮的segue到你想去的地方。你只需初始化故事板以及获取你想要展示的控制器。一旦你有了控制器,你可以调用必要的显示方法。</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">var</span> <span class="nv">storyboard</span><span class="p">:</span> <span class="kt">UIStoryboard</span> <span class="o">=</span> <span class="kt">UIStoryboard</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"Settings"</span><span class="p">,</span> <span class="nv">bundle</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">modal</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="o">=</span> <span class="n">storyboard</span><span class="o">.</span><span class="nf">instantiateViewControllerWithIdentifier</span><span class="p">(</span><span class="s">"settingsStoryboardId"</span><span class="p">)</span> <span class="k">as</span> <span class="kt">UIViewController</span>
<span class="k">self</span><span class="o">.</span><span class="nf">presentViewController</span><span class="p">(</span><span class="n">modal</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nv">completion</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span></p>
<pre><code><span class="o">/*</span> <span class="kt">If</span> <span class="n">you</span><span class="err">'</span><span class="n">re</span> <span class="n">fetching</span> <span class="n">a</span> <span class="n">controller</span> <span class="k">in</span> <span class="n">the</span> <span class="n">same</span> <span class="n">storyboard</span> <span class="n">you</span><span class="err">'</span><span class="n">re</span> <span class="n">already</span> <span class="n">on</span><span class="p">,</span>
<span class="o">*</span> <span class="n">then</span> <span class="n">you</span> <span class="n">can</span> <span class="n">skip</span> <span class="n">initializing</span> <span class="n">a</span> <span class="k">new</span> <span class="kt">UIStoryboard</span> <span class="n">object</span><span class="o">.</span>
<span class="o">*/</span>
<span class="k">var</span> <span class="nv">modal</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">storyboard</span><span class="p">?</span><span class="o">.</span><span class="nf">instantiateViewControllerWithIdentifier</span><span class="p">(</span><span class="s">"customStoryboardId"</span><span class="p">)</span> <span class="k">as</span> <span class="kt">UIViewController</span>
</code></pre>
<p><span class="k">self</span><span class="o">.</span><span class="nf">presentViewController</span><span class="p">(</span><span class="n">modal</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nv">completion</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span></code></pre></figure></p>
<h3>预览编辑器</h3>
<p>如果花所有时间去构建并运行应用程序以观察用户界面是否调整到你想要的结果,是很乏味的。在处理自动布局时尤其如此。</p>
<p><img src="/assets/img/ios/gpxj/22/4.png" alt="4" /></p>
<p>现在打开预览编辑器,你可以修改视图,观察它如何变化。你也可以按下左下角助理编辑面板上的+按钮,以便在多个屏幕尺寸预览界面。</p>
<h3>让手指休息一下</h3>
<p>如果你有一个按钮需要连接到源代码中,你可以右键单击,然后拖一条线到源文件中为你生成一个outlet.</p>
<p><img src="/assets/img/ios/gpxj/22/5.png" alt="5" /></p>
<p>此外,对于一个给定的事件,你可以通过单击再拖到你的源文件来生成一个action。</p>
<p><img src="/assets/img/ios/gpxj/22/6.png" alt="6" /></p>
<p>以上操作的最终结果。</p>
<p><img src="/assets/img/ios/gpxj/22/7.png" alt="7" /></p>
<p>那,为什么需要做这些操作呢? 这么说吧,最明显的就是action。如果你不创建一个@IBAction函数,那么当你按下按钮的时候不会有任何事情发生。你可以假设需要添加一些代码来改变UIImageView中最初设置的的图片。为了改变这个图片我们就需要一个@IBOutlet以便我们访问它。</p>
<h3>避免极其复杂的控制器</h3>
<p>尽管你的控制器可以管理大量子视图,但你的风险在于一层一层的添加视图,这会使事情完全被破坏。很快你就会发现,你偏离了使用可视化编辑器的目的--提供清晰的视图层次。如果你有一个复杂的视图结构,那么是时候考虑这些设置。</p>
<p>你可以使用一个xib,或者你可以添加一个容器视图对象到你的场景,并隐藏它直到需要的时候。通常我使用xib,但在某些情况下则使用容器视图对象。</p>
<p>当你添加一个xib的同时还会添加一个源文件,你将使用它来初始化xib.例如,我们创建了ExampleView.xib以及一个漂亮的场景。为了加载这个视图我们需要创建ExampleView.swift以及初始化这个xib。</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">import</span> <span class="kt">UIKit</span></p>
<p><span class="kd">class</span> <span class="kt">ExampleView</span><span class="p">:</span> <span class="kt">UIView</span> <span class="p">{</span></p>
<pre><code><span class="c1">// normal initialization</span>
<span class="k">override</span> <span class="nf">init</span><span class="p">(</span><span class="nv">frame</span><span class="p">:</span> <span class="kt">CGRect</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">frame</span><span class="p">:</span> <span class="n">frame</span><span class="p">)</span>
<span class="k">self</span><span class="o">.</span><span class="nf">addExampleViewSubview</span><span class="p">()</span>
<span class="p">}</span>
<span class="c1">// will be loaded if we use this class in a storyboard, for example</span>
<span class="kd">required</span> <span class="nf">init</span><span class="p">(</span><span class="n">coder</span> <span class="nv">aDecoder</span><span class="p">:</span> <span class="kt">NSCoder</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">coder</span><span class="p">:</span> <span class="n">aDecoder</span><span class="p">)</span>
<span class="k">self</span><span class="o">.</span><span class="nf">addExampleViewSubview</span><span class="p">()</span>
<span class="p">}</span>
<span class="kd">func</span> <span class="nf">addExampleViewSubview</span><span class="p">()</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">xib</span> <span class="o">=</span> <span class="kt">NSBundle</span><span class="o">.</span><span class="nf">mainBundle</span><span class="p">()</span><span class="o">.</span><span class="nf">loadNibNamed</span><span class="p">(</span><span class="s">"ExampleView"</span><span class="p">,</span> <span class="nv">owner</span><span class="p">:</span> <span class="k">self</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
<span class="k">var</span> <span class="nv">view</span><span class="p">:</span> <span class="kt">UIView</span> <span class="o">=</span> <span class="n">xib</span><span class="o">.</span><span class="n">first</span> <span class="k">as</span> <span class="kt">UIView</span>
<span class="n">view</span><span class="o">.</span><span class="n">frame</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">frame</span>
<span class="k">self</span><span class="o">.</span><span class="nf">addSubview</span><span class="p">(</span><span class="n">view</span><span class="p">)</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<h3>占位符约束</h3>
<p>这是给那些喜欢混合使用代码与故事板操纵约束的人的。即使我个人尽可能地避免在代码中编写约束,但这对于那些不属于故事板的视图确实是很有作用的。</p>
<p>假如你试图在代码中创建约束,且需要与故事板中的用户界面交互,这可能会是一个非常可怕的经历。不过不要害怕,你可以很轻松地告诉Xcode这个特定约束是一个占位符。这意味着在构建和运行应用程序时它将被忽略。</p>
<p><img src="/assets/img/ios/gpxj/22/8.png" alt="8" /></p>
<h3>默认的试图控制器</h3>
<p>你可能需要更改哪个场景与故事板一起加载。在早期版本的Xcode中你也许会选择一个场景,然后选择<strong>Is Initial View Controller</strong>。在Xcode的最新版本变了。现在你需要搜索对象库,找到 <strong>Storyboard Entry Point</strong>,然后你可以拖拽到想要的场景。这其中一次只能有一个是活跃的,所以你可以可以拖拽它到你想想要的任一控制器中。</p>
<p>为什么你会担心入口点发生改变?就我个人而言,我使用它来测试不同的控制器,我不想创建大量的按钮才能进到控制器里去。如果你只是更新故事板的入口点,它就会立即加载。</p>
<h3>自定义Segue转场效果</h3>
<p>如果你选中一个segue,你可能已经注意到,它有一些预加载的转场效果,比如垂直覆盖、水平翻转、淡入淡出以及部分卷曲等。如果你想要更多的自定义效果呢?在这种情况下你需要创建一个自定义UIStoryboardSegue。</p>
<p>举个简单的粟子,创建一个新的Swift文件,取名为<strong>CustomSegue</strong>。 当我们的segue被执行的时候(上面的按钮被点击),下面这段代码也会被执行。</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">import</span> <span class="kt">UIKit</span></p>
<p><span class="kd">class</span> <span class="kt">CustomSegue</span><span class="p">:</span> <span class="kt">UIStoryboardSegue</span> <span class="p">{</span></p>
<pre><code><span class="k">var</span> <span class="nv">startingPoint</span><span class="p">:</span> <span class="kt">CGPoint</span> <span class="o">=</span> <span class="kt">CGPoint</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="mi">0</span><span class="p">)</span>
<span class="k">override</span> <span class="kd">func</span> <span class="nf">perform</span><span class="p">()</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">source</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">sourceViewController</span> <span class="k">as</span> <span class="kt">UIViewController</span>
<span class="k">var</span> <span class="nv">destination</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">destinationViewController</span> <span class="k">as</span> <span class="kt">UIViewController</span>
<span class="c1">// Add the destination view as a subview (temporarily)</span>
<span class="n">source</span><span class="o">.</span><span class="n">view</span><span class="p">?</span><span class="o">.</span><span class="nf">addSubview</span><span class="p">(</span><span class="n">destination</span><span class="o">.</span><span class="n">view</span><span class="p">)</span>
<span class="c1">// Set the start scale</span>
<span class="n">destination</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">transform</span> <span class="o">=</span> <span class="kt">CGAffineTransformMakeScale</span><span class="p">(</span><span class="mf">0.05</span><span class="p">,</span> <span class="mf">0.05</span><span class="p">)</span>
<span class="c1">// Original center point</span>
<span class="k">var</span> <span class="nv">originalCenter</span> <span class="o">=</span> <span class="n">destination</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">center</span>
<span class="n">destination</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">center</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">startingPoint</span>
<span class="kt">UIView</span><span class="o">.</span><span class="nf">animateWithDuration</span><span class="p">(</span><span class="mf">0.225</span><span class="p">,</span> <span class="nv">delay</span><span class="p">:</span> <span class="mf">0.0</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="kt">UIViewAnimationOptions</span><span class="o">.</span><span class="kt">CurveEaseOut</span><span class="p">,</span> <span class="nv">animations</span><span class="p">:</span> <span class="p">{</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Void</span> <span class="k">in</span>
<span class="n">destination</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">transform</span> <span class="o">=</span> <span class="kt">CGAffineTransformMakeScale</span><span class="p">(</span><span class="mf">1.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="n">destination</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="n">center</span> <span class="o">=</span> <span class="n">originalCenter</span>
<span class="p">})</span> <span class="p">{</span> <span class="p">(</span><span class="n">finished</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Void</span> <span class="k">in</span>
<span class="n">destination</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="nf">removeFromSuperview</span><span class="p">()</span>
<span class="n">source</span><span class="o">.</span><span class="nf">presentViewController</span><span class="p">(</span><span class="n">destination</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span><span class="kc">false</span><span class="p">,</span> <span class="nv">completion</span><span class="p">:</span><span class="kc">nil</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<p>按下钮按时,这将从我们设置的一个起始位置展开目标视图。在这种情况下,我设置起始位置为场景1中按钮的中心位置(源场景)。</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">override</span> <span class="kd">func</span> <span class="nf">prepareForSegue</span><span class="p">(</span><span class="nv">segue</span><span class="p">:</span> <span class="kt">UIStoryboardSegue</span><span class="p">,</span> <span class="nv">sender</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">custom</span> <span class="o">=</span> <span class="n">segue</span> <span class="k">as</span> <span class="kt">CustomSegue</span>
<span class="n">custom</span><span class="o">.</span><span class="n">startingPoint</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">nextImage</span><span class="o">.</span><span class="n">center</span>
<span class="p">}</span></code></pre></figure></p>
<p>在故事板文件中我们选择从场景1到场景2的segue。更改Segue为Custom,然后输入Segue类来匹配我们刚刚创建的一个CustomSegue。</p>
<h3>避免源代码管理的噩梦</h3>
<p>对于那些在团队中的人来说,虽然他们也在逐渐变得更好,但当涉及到源代码管理时,故事板仍是一个大痛处。这也是为什么你应该将故事板拆分成若干个。如果你可以避免这个,请确保同一时刻只有一个人操作故事板。这会避免其他人提交故事板的更改到项目中产生的冲突。即使这并不那么容易去避免,但请做好准备防止这种情况出现。这也许是团队与小公司不想使用故事板最大的原因。</p>
<p>尽管当我与的团队共享故事板时很有可能会导致源代码控制的冲突,我仍然觉得,速度和效益的重要性大于修补因为冲突导致的恼人补丁。在大多数情况下我确保其他人避免与我同时处理。但是,它仍然一次又一次发生。</p>
<p>这里是我如何处理冲突的方法:</p>
<p>(第一道防线)首先主动避免它。在与故事板工作时我经常尽早的提交,并且对其他也在与这个故事板工作的人打个招呼。当与其他人一起工作时,尽量保持小任务量的工作,以及互相帮助。不要推送整体的大更新。
如果我遇到了冲突,我会通过差异工具<a href="http://www%E3%80%82kaleidoscopeapp%E3%80%82com/">Kaleidoscope</a>来浏览。这确实需要一些经验来理解故事板的原理。如果你还没看过,现在就是时候。右键点击 storyboard > open as > source code,观察控制器区域(包括连接信息)是如何陈列布局的。
(糟糕的情况)我找出了谁做的最大的更改,谁接收的这些变化信息,覆盖其了其他什么,然后我们会恢复这些改变。
我做过一些大型的项目。虽然这些合并的issues不是经常严重到diff工具不能恢复。但你可以获得更多的经验。</p>
<p>我不能强调第一点是足够的。积极主动很重要。知道你的团队是什么样地情况。如果你有一个令人难以置信的复杂的故事板,就将它板划分成不同的区域。</p>
<h3>拿走不谢</h3>
<p>故事板是非常有用的,尤其是对于那些非常直观的东西。当做原型时,也可以给你一个不错的速度优势。无需为代码烦忧,你可以快速将各种元素放在适当的地方,连接它们,然后从驱动界面的逻辑开始做起。</p>
项目第二十一天 iOS自动处理键盘事件的第三方库:IQKeyboardManager2015-07-26T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/26/gpxj-21-day<h2>原文:<a href="http://www.open-open.com/lib/view/open1425955017138.html">iOS自动处理键盘事件的第三方库:IQKeyboardManager</a></h2>
<hr />
<p>我们写界面要考虑很多用户体验问题,键盘事件的响应就是比较麻烦的一种。我们需要监听键盘事件,考虑点击背景收起键盘、考虑键盘遮挡输入框问题等等,而且每个界面都要做这么一套。这个库帮我们解决了这个事情。</p>
<p>这个库的下载地址:<a href="https://github.com/hackiftekhar/IQKeyboardManager">https://github.com/hackiftekhar/IQKeyboardManager</a></p>
<p>这个库是一个单例,它一旦生效,全项目任何界面都有效。让它生效的代码可以写在任意位置,我写在AppDelegate里。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (BOOL)application:(UIApplication <em>)application didFinishLaunchingWithOptions:(NSDictionary </em>)launchOptions {</p>
<pre><code>IQKeyboardManager *manager = [IQKeyboardManager sharedManager];
manager.enable = YES;
manager.shouldResignOnTouchOutside = YES;
manager.shouldToolbarUsesTextFieldTintColor = YES;
manager.enableAutoToolbar = NO;
return YES;
</code></pre>
<p>}</code></pre></figure></p>
<p>enable控制整个功能是否启用。</p>
<p>shouldResignOnTouchOutside控制点击背景是否收起键盘。</p>
<p>shouldToolbarUsesTextFieldTintColor 控制键盘上的工具条文字颜色是否用户自定义。</p>
<p>enableAutoToolbar控制是否显示键盘上的工具条。</p>
<p>以上设置,就启用了这个库。</p>
<p>使用方法,代码如下:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import "ViewController.h"</p>
<p>@interface ViewController ()</p>
<p>@property (nonatomic, strong) IQKeyboardReturnKeyHandler *returnKeyHandler;</p>
<p>@end</p>
<p>@implementation ViewController</p>
<ul>
<li><p>(void)viewDidLoad {
[super viewDidLoad];</p>
<p> self.returnKeyHandler = [[IQKeyboardReturnKeyHandler alloc] initWithViewController:self];
self.returnKeyHandler.lastTextFieldReturnKeyType = UIReturnKeyDone;
self.returnKeyHandler.toolbarManageBehaviour = IQAutoToolbarBySubviews;
}</p></li>
<li><p>(void)dealloc
{
self.returnKeyHandler = nil;
}</p></li>
</ul>
<p>@end</code></pre></figure></p>
<p>设置returnKeyHandler,可以点击键盘上的next键,自动跳到下一个输入框。最后一个输入框点击done自动收起键盘。</p>
<p>运行后,可以看到输入框随着键盘的弹出自动上下浮动。点击背景,键盘收起。全自动了。</p>
<p>这个库默认支持UITextField、UITextView、UIWebView、UIScrollView、UITableView、UICollectionView</p>
<blockquote><p>最后要注意一点,它可以自动计算多个textField之间的先后顺序,排列依据是看addSubView的先后顺序。</p></blockquote>
项目第二十天 10个加速Table Views开发的Tips2015-07-25T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/25/gpxj-20-day<h2>原文:<a href="http://www.xmcgraw.com/10-actionable-performance-tips-to-speed-up-your-table-view/">10 Actionable Performance Tips To Speed Up Your Table View</a></h2>
<h2>翻译: <a href="http://www.cocoachina.com/ios/20150729/12795.html">10个加速Table Views开发的Tips</a></h2>
<hr />
<p>在我们开始之前,我准备从今年开始多听取一个意见。请花一些时间通过这篇简短的调查给我们一些反馈。这将会帮助我来帮助你。</p>
<p>如果你曾经跟collectionview打过交道,你可能已经意识到了这篇文章的价值。如果你没有注意速度这将会是一个大问题,你的用户会让你了解的。当你的scrollview没有你设备上的其他app的速度快的时候你将会很快意识到。Table views是每一个iOS初级开发者最先使用到的,也可能很快就陷入困惑。这篇文章将会深入讲解一些也许你正在查找的问题。</p>
<h2>龟兔问题</h2>
<hr />
<p>Table views是一种交互对象,许多app利用它展示结构化的数据。想要很好的利用它是很琐细的,这使得他们使用起来犹如曲折的冒险。设计者在设计之初不考虑性能的问题。设计者甚至可能是你自己。很快你将会做一个图片类型的app,它需要在cell上展示许多信息。开始的时候可能很快但是很快就慢的像乌龟一样。你想让你的Table views顺畅得像一片黄油。你的app的这些使用效果如果不好的话很快就会能够注意到。</p>
<h2>加速你的Table Views</h2>
<hr />
<p>我们会通过一个实际的例子来探索这些小提示,这个例子中的Table views实现得很不好。</p>
<p>通常你会发现一个图片类的app会在一个imageview上做下面这些事情:</p>
<ul>
<li>下载图片(主要的内容图片+用户头像图片)</li>
<li>更新时间戳</li>
<li>展示评论</li>
<li>计算动态的cell的高度</li>
</ul>
<p>在这个例子中我们打算集中分析以上几点内容。</p>
<p>我建议你去克隆下来那个demo的目录(github)去体验一下一开始的时候它是多么糟糕。跳到XMCFeedTableViewCell看下它的提升并且感受一下它的性能。如果你在iphone 6+上面运行的话优化感受起来可能不那么好,意识到这点很重要。不要忘了在一台更旧的设备上体验一下。</p>
<h3>Tip#1 学习怎么提升速度</h3>
<p>我可以写一整篇关于Instruments的文章。在这儿我将给你一个大致的介绍因为这会很有帮助的。</p>
<p>如果你对于Instruments不是很有经验,我劝你周末花些时间来研究一些。当你想要测量内存与时间消耗,他们会帮你很大的忙。然而当你着手做一个app你将会在开发过程中遇到很多问题,代码会变得越来越糟,这时你可能还无暇顾及性能的问题。但是重构是潜在的。为了合适的重构你应该花费精力在分析性能上面。</p>
<p>所以,下面是周末探索内容:</p>
<ol>
<li><p>打开你的项目并点击Product>Profile</p></li>
<li><p>在那儿选择Custom</p></li>
<li><p>找到添加按钮并且添加工具:Allocations,Time,Profile,Leaks</p></li>
<li><p>观察你的应用,以及他的表现。</p></li>
</ol>
<p>例如,我们关心的是速度(但是内存也是一个大问题)。我们需要哪个工具呢?如果你选择Time Profile那你就对了。让我们打开它并观察下运行中的app。</p>
<p><img src="/assets/img/ios/gpxj/20/1.jpg" alt="1.jpg" /></p>
<p>下面你就能看到我们的app的概况。你所看到的就是我打开app并且尽我可能快的上下滚动tableview。这就模拟出了一个很好的“最坏情况假设”,然后我们就可以采取行动了。</p>
<p><img src="/assets/img/ios/gpxj/20/2.jpg" alt="2.jpg" /></p>
<p>这个区域就是我开始滚动app时会执行的代码,我们只想知道在这个区域的时间消耗。</p>
<p><img src="/assets/img/ios/gpxj/20/3.jpg" alt="3.jpg" /></p>
<p>现在你可以开始研究我们上面讨论的代码了。双击这些行中的任何一行(最好是最上面一行,那就是时间被消耗最多的地方)</p>
<p>需要指出很重要的一点,那就是Call Tree下面的选项不是为你在Instruments加载时设置的。你需要自己去设置。</p>
<h3>Tip#2 避免阻塞主线程</h3>
<p>在这个例子中你会看到第一个图片相关的方法在数据下载并转换成图片对象时阻塞了主线程。你要尽量避免阻塞主线程,这对于collection中的交互对象尤为重要。网络请求?保持他们在后台运行(异步的)并且缓存传回的响应。你肯定不想重复处理任何操作。想象你的cell在一段沉默时间内被绘制。你的cell应该只展示已经保存在你的设备上的数据。这会使你感觉更好的。</p>
<h3>Tip#3 重用cells</h3>
<p>如果你已经花了一些时间学习iOS,那么不好意思 。这条建议是给那些新接触iOS的同学的。你应该使用dequeueReusableCellWithIdentifier 这个方法去获取一个table或者collection上面的cell。如果你不是这样做的,你就浪费了一段无意义的时间和数据。</p>
<h3>Tip#4 缓存下载的图片</h3>
<p>这肯定是你在这里读到的最重要的一条建议了。如果你不缓存图片你将会遇到很大的问题。</p>
<p>如果你重用本地的图片那么请使用UIImage的方法imageNamed:。以JPG格式请求图片将会节省时间和资源。如果你是从服务端获取图片那么你就可以获取所需要的那些图片( If you’re getting your image from a server you have the luxury of sending the exact image that’s needed.)。 PNG文件在内存中会占用很大一部分空间。如果你对此感觉好奇你可以在示例中将JPG换成PNG来下载一系列的PNG图片。</p>
<p>使用 SDWebImage 或者 Heneke 来管理图片。在提供的示例中我就是用的 Heneke,在那之前我没有听说过它也没有听说过它的好用之处。</p>
<h3>Tip#5 使用富文本标签代价是很昂贵的</h3>
<p>费尽周折用富文本标签,代价太昂贵了。尽可能地避免使用这个。问问你自己是否真的需要这个。如果是的话,尽可能的做缓存。</p>
<h3>Tip#6 cell高度计算</h3>
<p>如果你的table有复杂的动态高度那么你需要缓存计算的高度。考虑多久计算一次(尤其是对于collection views来说),你希望这些高度都是直接可用的。</p>
<h3>Tip#7 NsDateFormatter 的痛苦</h3>
<p>就像富文本,如果你频繁地初始化,date formatter可以引起大量的内存消耗。比较理想的是你的web端为你提供可读的文字(比起在最后的时间计算要容易很多)。如果没有的话你可以创建一个NSDateFormatter的单例来使用。NSDateFormatter不是线程安全的,但是iOS7以及之后就不再是这样了。多谢quellish提醒我这一点。</p>
<h3>Tip#8 透明度</h3>
<p>如果你能避免的话你创建的对象最好是不透明的(非透明的,你不能透过它看过去)。如果你有透明的图片,系统必要要很努力地重绘这些图片。实际上你可以在模拟器中通过点击Debug>Clolor Blended Areas来看这些区域的问题。</p>
<p><img src="/assets/img/ios/gpxj/20/4.jpg" alt="4.jpg" /></p>
<p>看到红色的了么,那就意味着这些区域是透明的。当你在跟一个Collectionview打交道时这将是非常耗时。理想的,你想看到整个屏幕都是绿色的。对于你的设计来说那可能是不可行的,但是力求减少你看到的红色的数量。在示例中你可以看到label延伸到了view的尾部,可以被清除掉。</p>
<h3>Tip#9 不要过多使用Xib(如果可以的话使用storyboard)</h3>
<p>如果要使用xib就要小心一点。当你加载一个Xib,整个的内容会被加载到内存中(图片,隐藏的views)。但是这在storyboard中不会发生他只会实例化当前要用的东西。</p>
<p>有一些特殊的场景下使用xib很有意义。比如你可能会要使用一些第三方的框架而他们采用纯代码的方式来写collection的UI部分。如果你想用xib来创建一个原型cell你可以用xib来做。只是要小心不要过载。</p>
<h3>Tip#10 使用CoreGraphics</h3>
<p>我很少需要这个,但是当你需要的时候你可以用。使用CoreGraphics并在一个view的drawRect的方法中写你的UI代码。</p>
<h2>挑战</h2>
<hr />
<p>谁不喜欢一个好的挑战?让我们在这儿来把它保持下来。这周花时间来做下面两件事。</p>
<p>学习怎么使用Instruments(time profiler, allocations)</p>
<p>通过测试图片缓存(关掉,打开,观察)来检查学习Instruments使用的情况。</p>
<p><a href="https://github.com/mcgraw/dojo-table-performance">https://github.com/mcgraw/dojo-table-performance</a></p>
<h3>问题和回答</h3>
<p>还没有问题!请留下评论或者发送给david@xmcgraw.com。</p>
<h3>Takeaway</h3>
<p>在iOS中界面的交互很重要。这是不可妥协的。如果你不花时间在稳固你的应用在设备上的体验,那么人们可能就会离你很远。当我回顾应用中的内容时应用应该是顺畅的。</p>
<h3>轮到你了</h3>
<p>对于提升性能你的首要建议是什么?我很乐意听取你的建议。请您有空的时候在下面分享。</p>
项目第十九天 探究UIScrollView2015-07-24T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/24/gpxj-19-day<h2>原文:<a href="http://www.cocoachina.com/ios/20140512/8378.html">探究UIScrollView</a></h2>
<h2>翻译: <a href="http://oleb.net/blog/2014/04/understanding-uiscrollview/">Understanding UIScrollView</a></h2>
<hr />
<p>我是Mike Ash的一系列的文章Let?s Build … 的忠实粉丝,在文章中他通过从零开始重建来解释Cocoa功能是如何工作的。在这篇文章中,我尝试着做了一些简单的东西,通过几行简单的代码来实现一个小的滚动视图。</p>
<p>但首先,让我们来仔细看看在UIKit中的坐标系是如何工作。当然你可以随个人意愿跳过下一节,如果你只关心滚动视图的实现。</p>
<h2>坐标系</h2>
<hr />
<p>每个视图都定义了自己的坐标系。它看起来像这样,x轴指向右边,y轴指向下方:</p>
<p><img src="/assets/img/ios/gpxj/19/1.png" alt="1.png" /></p>
<p>一个UIView的坐标系</p>
<blockquote><p>注意,逻辑坐标系统本身并不关注视图的宽度和高度。它没有边界,可以在四个方向上无限延伸。现在让我们这个坐标系中布局了几个项目(又名子视图)。每个彩色矩形代表一个子视图:</p></blockquote>
<p><img src="/assets/img/ios/gpxj/19/2.png" alt="2.png" /></p>
<p>在坐标系中添加子视图。在代码中,是这样设置的:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 100, 100)];
redView.backgroundColor = [UIColor colorWithRed:0.815 green:0.007
blue:0.105 alpha:1];</p>
<p>UIView *greenView = [[UIView alloc] initWithFrame:CGRectMake(150, 160, 150, 200)];
greenView.backgroundColor = [UIColor colorWithRed:0.494 green:0.827
blue:0.129 alpha:1];</p>
<p>UIView *blueView = [[UIView alloc] initWithFrame:CGRectMake(40, 400, 200, 150)];
blueView.backgroundColor = [UIColor colorWithRed:0.29 green:0.564
blue:0.886 alpha:1];</p>
<p>UIView *yellowView = [[UIView alloc] initWithFrame:CGRectMake(100, 600, 180, 150)];
yellowView.backgroundColor = [UIColor colorWithRed:0.972 green:0.905
blue:0.109 alpha:1];</p>
<p>[mainView addSubview:redView];
[mainView addSubview:greenView];
[mainView addSubview:blueView];
[mainView addSubview:yellowView]; </code></pre></figure></p>
<h2>边界</h2>
<hr />
<p>在UIView的文档中是这样描述有关bounds属性的:</p>
<p>边界矩形...描述了自己的坐标系中的视图的位置和大小。</p>
<p>一个视图可以被认为是一个窗口或窗口的矩形区域定义的平面坐标系。这个视图的边界表达了这个矩形的位置和大小。</p>
<p>来说我们的视图的边界矩形--320×480 points的宽度和高度,它的原点是默认的(0,0)。视图平面坐标系统变成一个视窗,显示整个平面上的一小部分。范围之外的一切都还在那里,只是隐藏而已:</p>
<p><img src="/assets/img/ios/gpxj/19/3.png" alt="3.png" /></p>
<p>视图提供了一个坐标系统定义的视窗。该视图边界矩形描述可见区域的位置和大小。</p>
<h2>Frame</h2>
<hr />
<p>下一步,我们将修改边界矩形的原点:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">CGRect bounds = mainView.bounds;
bounds.origin = CGPointMake(0, 100);
mainView.bounds = bounds; </code></pre></figure></p>
<p>当边界矩形的原点是现在的(0,100)时,我们看起来的场景就像这样:</p>
<p><img src="/assets/img/ios/gpxj/19/4.png" alt="4.png" /></p>
<p>修改边界矩形的原点,相当于移动视窗。</p>
<p>看起来好像向下移动了100 points,而且也好像真实的关联了自己的坐标系。屏幕上的视窗实际位置(或它的父视图,为了更准确)是保持固定的,然而,这是由它的Frame决定的,它其实并没有改变:</p>
<p>框架矩形...描述它在父坐标系统中视图的位置和大小。</p>
<p>由于视图位置(从自己的角度)是固定的,可以把平面坐标系想象为一块我们可以左右拖动透明的薄膜,并认为我们是通过一个固定的窗口来进行浏览的。</p>
<p>调整边界的原点等同于移动透明薄膜,这样薄膜的另一部分就会在视图中可见。</p>
<p><img src="/assets/img/ios/gpxj/19/5.gif" alt="5.gif" /></p>
<p>修改边界矩形的原点是相当于往相反方向移动坐标系,而视图的位置保持固定,因为它的frame不会改变。</p>
<p>而这正是当UIScrollView滚动时做的。注意,从用户的角度看来好像视图的子视图被移动了,但就其视图坐标系统而言,它的位置(换言之,它们的frames)保持不变。</p>
<h2>让我们来构建UIScrollView</h2>
<hr />
<p>滚动视图并不需要时时更新其子视图的坐标使其滚动。它所要做的就是调整其边界的原点。有了这些知识,实现一个非常简单的滚动视图就易如反掌了。我们建立了一个手势识别来检测用户的平移手势,并通过不断拖动展示视图的边界来响应于这个手势:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">// CustomScrollView.h
@import UIKit;</p>
<p>@interface CustomScrollView : UIView</p>
<p>@property (nonatomic) CGSize contentSize;</p>
<p>@end</p>
<p>// CustomScrollView.m</p>
<h1>import "CustomScrollView.h"</h1>
<p>@implementation CustomScrollView</p>
<ul>
<li><p>(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self == nil) {
return nil;
}
UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc]<br/>
initWithTarget:self action:@selector(handlePanGesture:)];
[self addGestureRecognizer:gestureRecognizer];
return self;
}</p></li>
<li><p>(void)handlePanGesture:(UIPanGestureRecognizer *)gestureRecognizer
{
CGPoint translation = [gestureRecognizer translationInView:self];
CGRect bounds = self.bounds;</p>
<p> // Translate the view's bounds, but do not permit values that would violate contentSize
CGFloat newBoundsOriginX = bounds.origin.x - translation.x;
CGFloat minBoundsOriginX = 0.0;
CGFloat maxBoundsOriginX = self.contentSize.width - bounds.size.width;
bounds.origin.x = fmax(minBoundsOriginX, fmin(newBoundsOriginX, maxBoundsOriginX));</p>
<p> CGFloat newBoundsOriginY = bounds.origin.y - translation.y;
CGFloat minBoundsOriginY = 0.0;
CGFloat maxBoundsOriginY = self.contentSize.height - bounds.size.height;
bounds.origin.y = fmax(minBoundsOriginY, fmin(newBoundsOriginY, maxBoundsOriginY));</p>
<p> self.bounds = bounds;
[gestureRecognizer setTranslation:CGPointZero inView:self];
}</p></li>
</ul>
<p>@end </code></pre></figure></p>
<p>就像这个UIScrollView,我们的类中必须有一个contentSize属性,它必须从外部设定来定义滚动区域的范围。当我们调整边界时,要确保只允许有效的值出现。</p>
<h2>效果:</h2>
<p><img src="/assets/img/ios/gpxj/19/6.png" alt="6.png" /></p>
<p>我们自定义的滚动视图。请注意,它缺乏动力滚动,跳跃和滚动指示器。</p>
<h2>结论</h2>
<hr />
<p>由于坐标系统已经封装嵌套在UIKit中,所以我们仅仅使用不到30行代码重现UIScrollView的精髓。当然,还有真实的UIScrollView决不仅仅只有这一点。动力滚动,跳跃,滚动指示,缩放和委托方法等等这些我们没有在这里实现其功能。</p>
<p>2014年5月2日更新:现在该代码可以在 <a href="https://github.com/ole/CustomScrollView">GitHub</a> 上找到。</p>
<ol>
<li><p>并非真正的无限。坐标系的范围受限于CGFloat类型的大小(在32位系统上是32字节,在64位系统上是64字节),这在实践中其实已经足够的大了。</p></li>
<li><p>实际上,除非clipsToBounds = =YES(默认是NO),子视图边界矩形之外仍将是可见的。视图不会检测边界范围以外的触动。</p></li>
</ol>
项目第十八天 CocoaPods2015-07-23T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/23/gpxj-18-day<h2>原文:<a href="http://nshipster.cn/authors/mattt-thompson/">CocoaPods</a></h2>
<h2>翻译: <a href="http://nshipster.cn/cocoapods/">CocoaPods</a></h2>
<hr />
<p>文明是建立在道路,桥梁,运河,下水道,管线,电线和光纤这些基础设施之上的。只要设计和施工得当,它们可以帮助社会成倍的发展。</p>
<p>唯一的问题就是可扩展性。</p>
<p>不管是在一个新的区域容纳上百万家庭还是整合大量的开发者到新的语言环境中去,挑战都是相同的。</p>
<p>在Objective-C的情况下,CocoaPods提供了一个绝佳的整合合作开发的工具,并且在快速发展的开发社区中起到了一个集结点的作用。</p>
<p>本周的NSHipster,我们将通过讨论CocoaPods的过去,现在以及将来,一起庆祝0.33版本(具有里程碑意义)的发布。</p>
<blockquote><p>接下来的对CocoaPods起源的历史回顾比较冗长,如果你只在乎技术细节,点此直接跳过。</p></blockquote>
<h2>回望</h2>
<hr />
<p>在Objective-C在它存在的前20年左右几乎鲜为人知。NeXT和后来的OS X作为一个边缘平台,只拥有一个相对较小的用户和开发者社区。像所有的社区一样,本地用户小组,邮件列表和网站该有的都有,但是开源合作开发缺很少见。诚然,开源在那时也只处于起步阶段,但是Objective-C却从未有过类似于CPAN (the Comprehensive Perl Archive Network)的组织。所有人除了能从Redwood和Cupertino拿到SDK(或者在论坛搜寻一下可用的代码)以外,剩下的问题只能靠自己解决。</p>
<h3>Objective-C和iPhone</h3>
<p>这种情况一直持续到了2008年的夏天,当iPhone OS开始对第三方开发者开放的时候。几乎一夜之间,Objective-C从无人问津变的炙手可热。上百万开发者的涌入,给这门语言注入了新鲜的血液。</p>
<p>就在此时,GitHub 也刚刚发布,并且开始通过新的分布式合作开发方式改变我们对开源的认知。</p>
<p>一大批开源项目开始涌现,例如ASIHTTPRequest和Facebook的Three20。这些早期的库和框架主要是用来填补iPhone OS 2.0和3.0开发中遇到空白,并且在后续的OS迭代中慢慢被遗弃或取代,但是它们突破了每个开发者“单打独斗”的局面。</p>
<p>在这波新的开发者中,那些来自Ruby背景的对 Objective-C 起来了很大的影响。Ruby作为Perl的精神继承者,有一个类似于CPAN的包管理器:RubyGems</p>
<p>为什么受Ruby的影响这么大?我的理论是:Ruby是在Rails 2005年发布1.0版本的时候开始流行起来。假设创业公司的平均寿命在1.5到2.5年之间,那么此时第一批厌倦Rails的开发者正好可以跳上移动开发的大船上。</p>
<p>就在Objective-C开源开发渐入佳境之时,代码分发的痛点开始显现:</p>
<p>缺乏框架,iOS的代码虽然可以被打包成静态库,但是配置和同步分发却成了一个艰巨的任务。</p>
<p>另外一个思路是用Git Submodules把源码直接放入项目。但是链接框架和配置生成环境的繁琐也使得这种方法也没有好到哪里去,尤其是当ARC和 non-ARC的代码需要分开的时候。</p>
<h3>进入CocoaPods时代</h3>
<p>CocoaPods是由Eloy Durán于2011年8月12日创建。</p>
<p>在Bundler和RubyGems的启发下,CocoaPods被设计成即能处理库之间的依赖关系,又能自动下载并且配置好所需要的库。试想一下开发只有松散文档编制的Xcode项目的难度,CocoaPods的存在简直就是奇迹。</p>
<p>另一个早先的决定就是利用central Git repository作为所有库的总数据库。虽然这带来了一些运筹上的顾虑,好在GitHub能够提供一个稳健的平台,帮助团队在后续的迭代中,开发出更好的工具链。</p>
<p>时至今日,CocoaPods已经壮大拥有14个核心开发人员和多达5000个开源项目。绝大部分项目都是来自于Objective-C开源社区,我们应该感谢每一个参与其中的开发者。</p>
<h2>使用CocoaPods</h2>
<hr />
<p>制作和使用CocoaPods库都十分简单,往往几分钟就能配置完毕。</p>
<blockquote><p>想获取最新的官方教程,请前往此处。</p></blockquote>
<h3>安装CocoaPods</h3>
<p>CocoaPods可以方便地通过RubyGems安装,打开Terminal,然后键入以下命令:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>sudo gem install cocoapods</code></pre></figure></p>
<p>就这么简单,现在你应该可以开始使用pod命令了。</p>
<blockquote><p>如果你使用Ruby版本管理器,如rbenv,你可能需要运行以下指令来重新链接shim的二进制文件(例如:$ rbenv rehash)。</p></blockquote>
<h3>管理相关性</h3>
<p>一个相关性管理器可以将一系列的软件需求转化为具体的标签,然后下载并且整合进入相关的项目。</p>
<p>申明需求可以自动化整个项目配置,这也是软件开发的最佳实践之一,无论是在任何语言中。甚至你不使用第三方库,CocoaPods仍然是一个管理代码相关性的绝佳工具。</p>
<h3>Podfile</h3>
<p>Podfile这个文件是用来用来申明项目代码相关性的,正如Bundler的Gemfile,或者npm的package.json</p>
<p>cd进入.xcodeproj文件所在的目录,通过以下命令来创建一个Podfile</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>pod init</code></pre></figure></p>
<h3>Podfile</h3>
<p><figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">platform</span> <span class="ss">:ios</span><span class="p">,</span> <span class="s1">'7.0'</span></p>
<p><span class="n">target</span> <span class="s2">"AppName"</span> <span class="k">do</span></p>
<p><span class="k">end</span></code></pre></figure></p>
<p>你可以申明需要不同版本的库,大部分情况下,申明到minor或者patch版本就足够了</p>
<p><figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">pod</span> <span class="s1">'X'</span><span class="p">,</span> <span class="s1">'~> 1.1'</span></code></pre></figure></p>
<p>CocoaPods遵循语意化版本规范。</p>
<p>对于那些不在CocoaPods公共Git仓库中的库,你可以用任何一个Git, Mercurial或者SVN仓库取代,并且还可以指定具体的commit, branch或者tag。</p>
<p><figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">pod</span> <span class="s1">'Y'</span><span class="p">,</span> <span class="ss">:git</span> <span class="o">=></span> <span class="s1">'https://github.com/NSHipster/Y.git'</span><span class="p">,</span> <span class="ss">:commit</span> <span class="o">=></span> <span class="s1">'b4dc0ffee'</span></code></pre></figure></p>
<p>一旦所有的相关性都申明完毕,你可以使用以下指令来安装所需要的库:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>pod install</code></pre></figure></p>
<p>安装过程中,CocoPods会使用递归来分析所有的需求,并且建立一个代码相关性的图,最后将Podfile序列化为Podfile.lock</p>
<p>比如,如果两个库都需要使用AFNetworking,CocoaPods会确定一个同时能被这两库使用的版本,然后将同一个安装版本链接到两个不同的库中。</p>
<p>CocoaPods会创建一个新的包含之前安装好的静态库Xcode项目,然后将它们链接成一个新的libPods.a target。你原有的项目将会依赖这个新的静态库。一个xcworkspace文件会被创建,从此之后,你应该只打开这个xcworkspace文件来进行开发。</p>
<p>反复使用pod install命令,只会让CocoaPods重复以上步骤,重新安装这些库。所以,当你需要升级它们时,请使用以下命令:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>pod update</code></pre></figure></p>
<h3>试着使用CocoaPod</h3>
<p>try是一个及其实用但又鲜为人知的CocoaPods命令,通过它你能够在安装一个库之前,先试用一下。</p>
<p>你只需要在try后面加上任意一个CocoaPods公共库的名称,就能试用它了!</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>pod try Ono</code></pre></figure></p>
<h2>建立自己的CocoaPod</h2>
<hr />
<p>作为Objective-C软件分发实际上的标准,CocoaPods几乎是所有开源项目的标配,如果你想让你的项目被大家很方便地使用。</p>
<p>诚然,这会提高一点点你分享项目的门槛,但是,好处是显然易见的。你花几分钟创建一个.podspec文件可以节省下其他开发者无数的时间。</p>
<h3>规范</h3>
<p>.podspec文件作为CocoaPods的一个独立单元,包含了名称,版本,许可证,和源码文件等所有信息。</p>
<blockquote><p>官方指南中有许多信息和范例</p></blockquote>
<p>以下是NSHipsterKit.podspec</p>
<p><figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Pod</span><span class="o">::</span><span class="no">Spec</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span>
<span class="n">s</span><span class="p">.</span><span class="nf">name</span> <span class="o">=</span> <span class="s1">'NSHipsterKit'</span>
<span class="n">s</span><span class="p">.</span><span class="nf">version</span> <span class="o">=</span> <span class="s1">'1.0.0'</span>
<span class="n">s</span><span class="p">.</span><span class="nf">license</span> <span class="o">=</span> <span class="s1">'MIT'</span>
<span class="n">s</span><span class="p">.</span><span class="nf">summary</span> <span class="o">=</span> <span class="s2">"A pretty obscure library.
You've probably never heard of it."</span>
<span class="n">s</span><span class="p">.</span><span class="nf">homepage</span> <span class="o">=</span> <span class="s1">'http://nshipster.com'</span>
<span class="n">s</span><span class="p">.</span><span class="nf">authors</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'Mattt Thompson'</span> <span class="o">=></span>
<span class="s1">'mattt@nshipster.com'</span> <span class="p">}</span>
<span class="n">s</span><span class="p">.</span><span class="nf">social_media_url</span> <span class="o">=</span> <span class="s2">"https://twitter.com/mattt"</span>
<span class="n">s</span><span class="p">.</span><span class="nf">source</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">:git</span> <span class="o">=></span> <span class="s1">'https://github.com/nshipster/NSHipsterKit.git'</span><span class="p">,</span> <span class="ss">:tag</span> <span class="o">=></span> <span class="s1">'1.0.0'</span> <span class="p">}</span>
<span class="n">s</span><span class="p">.</span><span class="nf">source_files</span> <span class="o">=</span> <span class="s1">'NSHipsterKit'</span>
<span class="k">end</span></code></pre></figure></p>
<p>一旦把这个.podspec发布到公共数据库中,任何想使用它的开发者,只需要在Podfile中加入如下声明即可:</p>
<h3>Podfile</h3>
<p><figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">pod</span> <span class="s1">'NSHipsterKit'</span><span class="p">,</span> <span class="s1">'~> 1.0'</span></code></pre></figure></p>
<p>.podspec文件也可以作为管理内部代码的利器:</p>
<p><figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">pod</span> <span class="s1">'Z'</span><span class="p">,</span> <span class="ss">:path</span> <span class="o">=></span> <span class="s1">'path/to/directory/with/podspec'</span></code></pre></figure></p>
<h3>发布CocoaPod</h3>
<p>CocoaPods 0.33中加入了Trunk服务。</p>
<p>虽然一开始使用GitHub Pull Requests来整理所有公共pods效果很好。但是,随着Pod数量的增加,这个工作对于spec维护人员Keith Smiley来说变得十分繁杂。甚至一些没有通过$ pod lint的spec也被提交上来,造成repo无法build。</p>
<p>CocoaPods Trunk服务的引入,解决了很多类似的问题。CocoaPods作为一个集中式的服务,使得分析和统计平台数据变得十分方便。</p>
<p>要想使用Trunk服务,首先你需要注册自己的电脑。这很简单,只要你指明你的邮箱地址(spec文件中的)和名称即可。</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>pod trunk register mattt@nshipster.com <span class="s2">"Mattt Thompson"</span></code></pre></figure></p>
<p>至此,你就可以通过以下命令来方便地发布和升级你的Pod!</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>pod trunk push NAME.podspec</code></pre></figure></p>
<p>已经发布Pod的作者可以通过几个简单的步骤来声明所有权。</p>
<h2>展望</h2>
<hr />
<p>CocoaPods例证了一个社区的凝聚力。在短短的几年内,Objective-C社区让我们所有人都引以为傲。</p>
<p>CocoaPods仅仅是众多Objective-C基础设施的一部分,还有诸如Travis CI, CocoaDocs和Nomad这些非常好的生产力工具。</p>
<p>虽然整个社区的未来不会一帆风顺,不管怎样,让我们怀着信念,尽可能的提供建设性的意见。我们更应该互相帮助,乐于分享,共同努力推动整个社区的进步!</p>
<p>CocoaPods已经是Objective-C不可或缺的一部分,它只会越来越强大!</p>
项目第十七天 Associative机制使用场景2015-07-22T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/22/gpxj-17-day<h3>原文:<a href="http://blog.sina.com.cn/s/blog_60342e330101tcz1.html">Associative机制使用场景</a></h3>
<hr />
<h3>1. 概念</h3>
<hr />
<p>objective-c有两个扩展机制:category和associative。我们可以通过category来扩展方法,但是它有个很大的局限性,不能扩展属性。于是,就有了专门用来扩展属性的机制:associative。</p>
<h3>2. 使用方法</h3>
<hr />
<p>在iOS开发过程中,category比较常见,而associative就用的比较少。associative的主要原理,就是把两个对象相互关联起来,使得其中的一个对象作为另外一个对象的一部分。</p>
<p>使用associative,我们可以不用修改类的定义而为其对象增加存储空间。这在我们无法访问到类的源码的时候或者是考虑到二进制兼容性的时候是非常有用。</p>
<p>associative是基于关键字的。因此,我们可以为任何对象增加任意多的associative,每个都使用不同的关键字即可。associative是可以保证被关联的对象在关联对象的整个生命周期都是可用的。</p>
<p>associative机制提供了三个方法:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)</p>
<p>OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)</p>
<p>OBJC_EXPORT void objc_removeAssociatedObjects(id object)</code></pre></figure></p>
<h4>2.1.创建associative</h4>
<p>创建associative使用的是:objc_setAssociatedObject。它把一个对象与另外一个对象进行关联。该函数需要四个参数:源对象,关键字,关联的对象、关联策略。</p>
<p>关键字是一个void类型的指针。每一个关联的关键字必须是唯一的。通常都是会采用静态变量来作为关键字。</p>
<p>关联策略表明了相关的对象是通过赋值,保留引用还是复制的方式进行关联的;还有这种关联是原子的还是非原子的。这里的关联策略和声明属性时的很类似。这种关联策略是通过使用预先定义好的常量来表示的。</p>
<p>比如,我们想对一个UIView,添加一个NSString类型的tag。可以这么做:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)setTagString:(NSString *)value {
objc_setAssociatedObject(self, KEY_TAGSTRING, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}</code></pre></figure></p>
<h4>2.2.获取associative对象</h4>
<p>获取相关联的是函数objc_getAssociatedObject。</p>
<p>继续上面的例子,从一个UIView的实例中,获取一个NSString类型的tag</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (NSString <em>)tagString {
NSObject </em>obj = objc_getAssociatedObject(self, KEY_TAGSTRING);
if (obj && [obj isKindOfClass:[NSString class]]) {
return (NSString *)obj;
}</p>
<pre><code>return nil;
</code></pre>
<p>}</code></pre></figure></p>
<h4>2.3.断开associative</h4>
<p>断开associative是使用objc_setAssociatedObject函数,传入nil值即可。</p>
<p>objc_setAssociatedObject(self, KEY_TAGSTRING, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);</p>
<p>使用函数objc_removeAssociatedObjects可以断开所有associative。通常情况下不建议这么做,因为他会断开所有关联。</p>
<h3>3. 应用场景</h3>
<hr />
<h4>3.1.TagString</h4>
<p>上面的例子提到,在UIView中添加NSString类型的标记,就是一个非常实用的例子。全部的代码如下:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@interface UIView(BDTag)</p>
<p>@property (nonatomic, retain) NSString *tagString;</p>
<ul>
<li>(UIView <em>)viewWithTagString:(NSString </em>)value;</li>
</ul>
<p>@end</code></pre></figure></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import "UIView+BDTag.h"</p>
<h1>undef KEY_TAGSTRING</h1>
<h1>define KEY_TAGSTRING "UIView.tagString"</h1>
<p>@implementation UIView(BDTag)</p>
<p>@dynamic tagString;</p>
<ul>
<li><p>(NSString <em>)tagString {
NSObject </em>obj = objc_getAssociatedObject(self, KEY_TAGSTRING);
if (obj && [obj isKindOfClass:[NSString class]]) {
return (NSString *)obj;
}</p>
<p> return nil;
}</p></li>
<li><p>(void)setTagString:(NSString *)value {
objc_setAssociatedObject(self, KEY_TAGSTRING, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}</p></li>
<li><p>(UIView <em>)viewWithTagString:(NSString </em>)value {
if (nil == value) {
return nil;
}</p>
<p> for (UIView <em>subview in self.subviews) {
NSString </em>tag = subview.tagString;
if ([tag isEqualToString:value])
{
return subview;
}
}</p>
<p> return nil;
}</p></li>
</ul>
<p>@end</code></pre></figure></p>
<p>苹果虽然有提供NSInteger类型的tag属性,用于标记相应的ui。但是在处理比较复杂的逻辑的时候,往往NSInteger类型的标记不能满足需求。为其添加了NSString类型的标记后。就能使用字符串,快速的标记ui,并且使用viewWithTagString方法,快速找到你所需要的ui。</p>
<h4>3.2.为NSObject子类添加任何信息</h4>
<p>这是一个方便,强大,并且简单的类。利用associative机制,为任何Object,添加你所需要的信息。比如用户登录,向服务端发送用户名/密码时,可以将这些信息绑定在请求的项之中。等请求完成后,再取出你所需要的信息,进行逻辑处理。而不需要另外设置成员,保存这些数据。</p>
<p>具体的实现如下:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@interface NSObject (BDAssociation)</p>
<ul>
<li>(id)associatedObjectForKey:(NSString*)key;</li>
<li>(void)setAssociatedObject:(id)object forKey:(NSString*)key;</li>
</ul>
<p>@end</code></pre></figure></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import "NSObject+BDAssociation.h"</p>
<p>@implementation NSObject (BDAssociation)</p>
<p>static char associatedObjectsKey;</p>
<ul>
<li><p>(id)associatedObjectForKey:(NSString<em>)key {
NSMutableDictionary </em>dict = objc_getAssociatedObject(self, &associatedObjectsKey);
return [dict objectForKey:key];
}</p></li>
<li><p>(void)setAssociatedObject:(id)object forKey:(NSString<em>)key {
NSMutableDictionary </em>dict = objc_getAssociatedObject(self, &associatedObjectsKey);
if (!dict) {
dict = [[NSMutableDictionary alloc] init];
objc_setAssociatedObject(self, &associatedObjectsKey, dict, OBJC_ASSOCIATION_RETAIN);
}
[dict setObject:object forKey:key];
}</p></li>
</ul>
<p>@end</code></pre></figure></p>
<h4>3.3.内存回收检测</h4>
<p>记得在我刚开始学iOS开发的时候,经常出现内存泄露的问题,于是就在各个view controller的dealloc中打Log。这种方法虽然有效,但比较挫,不好管理。
这里贴出一种漂亮的解决方案,利用associative机制。让object在回收时,自动输出回收信息。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@interface NSObject (BDLogDealloc)</p>
<ul>
<li>(void)logOnDealloc;</li>
</ul>
<p>@end</code></pre></figure></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import "NSObject+BDLogDealloc.h"</p>
<p>static char <strong>logDeallocAssociatedKey</strong>;</p>
<p>@interface LogDealloc : NSObject</p>
<p>@property (nonatomic, copy) NSString* message;</p>
<p>@end</code></pre></figure></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@implementation NSObject (LogDealloc)</p>
<ul>
<li>(void)logOnDealloc {
if(objc_getAssociatedObject(self, &<strong>logDeallocAssociatedKey</strong>) == nil) {
LogDealloc* log = [[[LogDealloc alloc] init] autorelease];
log.message = NSStringFromClass(self.class);
objc_setAssociatedObject(self, &<strong>logDeallocAssociatedKey</strong>, log, OBJC_ASSOCIATION_RETAIN);
}
}</li>
</ul>
<p>@end</code></pre></figure></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@implementation LogDealloc</p>
<ul>
<li>(void)dealloc {
NSLog(@"dealloc: %@", self.message);
[_message release];
[super dealloc];
}</li>
</ul>
<p>@end</code></pre></figure></p>
<h3>4. 总结</h3>
<p>以上便是几种associative机制的使用例子。这只是强大的associative功能中,小小的几个缩影。有了associative,就能用简单的几行代码,解决曾经困扰我们许久的问题。</p>
项目第十六天 autolayout之后获取uiview的frame2015-07-21T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/21/gpxj-16-day<h3>原文: <a href="http://www.nikest.com/web/jswd/2015/0225/122190.html">autolayout之后获取uiview的frame</a></h3>
<hr />
<p>这个只要一行代码就搞定了。详细请看:</p>
<p>In order to get the right frame/bounds of your UIImageView after resizing, you need first ask auto-layout to update that layout using [yourImageView layoutIfNeeded]. that will solve your constraints and update your yourImage.bounds.</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">[myImageView layoutIfNeeded];
NSLog(@"w: %f, h: %f", myImageView.bounds.size.width, myImageView.bounds.size.height);</code></pre></figure></p>
<p>就一行代码啊!</p>
<p>或者在这个方法中获取frame:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)viewDidLayoutSubviews {
// VC just laid off its views
}</code></pre></figure></p>
项目第十五天 图片选择2015-07-20T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/20/gpxj-15-day<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#pragma mark - UIActionSheetDelegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
NSUInteger sourceType = 0;
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
switch (buttonIndex) {
case 0:
sourceType = UIImagePickerControllerSourceTypeCamera;
break;
case 1:
sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
break;
case 2:
return;
}
}
else {
if (buttonIndex == 1) {
return;
}
else {
sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
}
}</p>
<pre><code>UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.delegate = self;
imagePickerController.allowsEditing = YES;
imagePickerController.sourceType = sourceType;
[self showDetailViewController:imagePickerController sender:nil];
</code></pre>
<p>}</p>
<h1>pragma mark - UIImagePickerControllerDelegate</h1>
<ul>
<li><p>(void)imagePickerController:(UIImagePickerController <em>)picker didFinishPickingMediaWithInfo:(NSDictionary </em>)info {
[picker dismissViewControllerAnimated:YES completion:nil];
UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
[self.avatarButton setImage:image forState:UIControlStateNormal];
}</p></li>
<li><p>(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[self dismissViewControllerAnimated:YES completion:nil];
}</code></pre></figure></p></li>
</ul>
项目第十四天 iOS objc_msgSend 报错解决方案2015-07-19T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/19/gpxj-14-day<h2>原文: <a href="http://www.ruanman.net/swift/learn/10443.html">iOS objc_msgSend 报错解决方案</a></h2>
<hr />
<h3>问题描述:</h3>
<hr />
<p>objc_msgSend(...)</p>
<p>Too many arguments to function call, expected 0, have 3</p>
<h3>解决方法:</h3>
<hr />
<p>选中项目 - Project - Build Settings - ENABLE_STRICT_OBJC_MSGSEND 将其设置为 NO 即可</p>
<p><img src="/assets/img/ios/gpxj/14/1/1.jpg" alt="1.jpg" /></p>
项目第十三天 XCTest/XCTest.h not found on old projects built in Xcode 2015-07-18T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/18/gpxj-13-day<h2>原文: <a href="http://blog.sina.com.cn/s/blog_5df876f30102v9rd.html">Test/XCTest.h not found on old projects built in Xcode</a></h2>
<hr />
<h3>问题描述:</h3>
<hr />
<p>fatal error: 'XCTest/XCTest.h' file not found</p>
<h3>解决方法:</h3>
<hr />
<p>在报错的Target中的Building settings中FRAMEWORK_SEARCH_PATHS添加$(PLATFORM_DIR)/Developer/Library/Frameworks</p>
<p><img src="/assets/img/ios/gpxj/13/1/1.jpeg" alt="1.jpeg" /></p>
项目第十二天 单元测试教程:模拟对象2015-07-17T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/17/gpxj-12-day<h2>原文: <a href="http://www.raywenderlich.com/101306/unit-testing-tutorial-mocking-objects">Unit Testing Tutorial: Mocking Objects</a></h2>
<hr />
<p>Who needs unit tests? Not you — your code is perfect. Umm…so you’re just reading this tutorial for your “friend” who needs to learn more about writing unit tests in Swift, right? Right. :]</p>
<p>谁需要单元测试?不是你——你的代码是完美的。嗯…所以你只是阅读本教程对你的“朋友”谁需要了解更多关于迅速编写单元测试,对吗?当然是这样(绝对不是)。:]</p>
<p>Unit tests are a great way to write better code; tests help you find most of the bugs early on in the process, but more importantly, writing code in a test-based development mindset helps you write modular code that’s easy to maintain. As a rule of thumb: if your code isn’t easy to test, it’s not going to be easy to maintain or debug.</p>
<p>单元测试是一个伟大的方式编写更好的代码,测试帮助你在早期就能找到大多数bug,但更重要的是,在基于开发的心态能帮助你编写代码编写模块代码更易于维护。作为一个经验法则:如果你的代码不容易测试,那么同样不容易维护或调试。</p>
<p>Unit tests deal with isolated “micro features”. Often you need to mock classes — that is, provide fake yet functional implementations — to isolate a specific micro feature so it can be tested. In Objective-C there are several third-party frameworks that help with mocking and stubbing. But those rely on introspection, which isn’t yet available on Swift. Someday, hopefully! :]</p>
<p>单元测试处理孤立的“微(小)功能”。你经常需要模拟类——也就是说,提供假的但功能实现——隔离一个具体的微观特性,所以可以进行测试。在objective-c中有几个第三方框架,帮助模拟和桩。但那些依赖于内省,尚未在.swift。有一天,希望!:]</p>
<p><strong>Mock</strong></p>
<blockquote><p>mock除了保证stub的功能之外,还可深入的模拟对象之间的交互方式,如:调用了几次、在某种情况下是否会抛出异常。</p></blockquote>
<p><strong>Stub</strong></p>
<blockquote><p>stub存在的意图是为了让测试对象可以正常的执行,其实现一般会硬编码一些输入和输出。</p></blockquote>
<p>In this tutorial you’ll learn how to write your own mocks, fakes and stubs to test a simple app that helps you remember your friends birthdays.</p>
<p> 在本教程中,您将了解如何编写自己的模拟,假货和桩来测试一个简单的应用程序,可以帮助你记住你的朋友的生日。</p>
<h3>Getting Started</h3>
<hr />
<p> 准备开始</p>
<p><a href="http://cdn4.raywenderlich.com/wp-content/uploads/2015/04/Birthdays_Starter1.zip">Download the starter project here</a>; this is a basic contacts app that can be hooked up to a web backend. You won’t work on the core app functionality; rather, you’ll write some tests for it to make sure it behaves as expected.</p>
<p><a href="http://cdn4.raywenderlich.com/wp-content/uploads/2015/04/Birthdays_Starter1.zip">在这里下载启动项目</a>,这是一个基本的接触可以连接到一个web应用程序的后端。你不会工作的核心应用程序功能;相反,你会为它编写一些测试以确保它的行为。</p>
<p> Build and run your app to see how it works. Tap the plus sign and add good ol’ John Appleseed to your list:</p>
<p> 构建和运行您的应用程序看到它是如何工作的。点击加号和添加好ol' John Appleseed 到你的列表:</p>
<p><img src="/assets/img/ios/gpxj/12/1/1.png" alt="1.png" /></p>
<p>The sample app uses Core Data to store your contacts.</p>
<p>示例应用程序使用 <strong>Core Data</strong> 存储你的联系人。</p>
<p><img src="/assets/img/ios/gpxj/12/1/2.png" alt="2.png" /></p>
<p>Don’t panic! :] You don’t need any experience with Core Data for this tutorial; there’s no rocket science involved.</p>
<p>别慌!:你不需要任何经验与本教程的核心数据;没有涉及火箭科学(应该是没有像研究造火箭那么困难吧)。</p>
<blockquote><p>Note: If you do want to become a Core Data master, you can get started by reading this <a href="http://www.raywenderlich.com/85578/first-core-data-app-using-swift">Core Data: Getting Started tutorial</a>.</p>
<p>注意:如果你想成为一个熟练掌握 Core Data 的人,你可以开始通过阅读这个<a href="http://www.raywenderlich.com/85578/first-core-data-app-using-swift">Core Data:入门教程</a>。</p></blockquote>
<h2>Advantages and Disadvantages of Unit Tests 单元测试的优缺点</h2>
<hr />
<p>When it comes to testing, there’s good news, and bad news. The bad news is that there can be disadvantages to unit tests, like the following:</p>
<p> 测试时,有好消息,也有坏消息。坏消息是单元测试有如下缺点:</p>
<ul>
<li>More code: In projects with high test coverage it’s possible to have more test code than functional code.</li>
<li>更多的代码:在项目使用覆盖率高的测试会编写比功能代码要多的测试代码。</li>
<li>More to maintain: When there is more code, there is more to maintain.</li>
<li>更多的维护:当有更多的代码也要更多的维护。</li>
<li>No silver bullet: Unit tests don’t (and can’t) ensure that your code is free of bugs.</li>
<li>没有银弹:单元测试不会(也不能)确保您的代码是没有缺陷的。</li>
<li>Takes longer: Writing tests takes time — time you could spend learning new exciting stuff on <a href="http://www.raywenderlich.com">raywenderlich.com</a>!</li>
<li>需要更长的时间:编写测试需要时间——你可以花时间学习新的令人兴奋的东西在raywenderlich.com上!</li>
</ul>
<p>Although there is no silver bullet, there is a silver lining — testing has the following advantages:</p>
<p>虽然没有银弹,但有一线希望——测试具有以下优点:</p>
<ul>
<li>Confidence: You can demonstrate that your code works.</li>
<li>信心:你可以证明你的代码能够工作。</li>
<li>Quick feedback: You can use unit tests to quickly validate code that is buried many layers deep in your app navigation — things that are cumbersome to test manually.</li>
<li>快速反馈:您可以使用单元测试快速验证多层深埋在你的应用程序的代码导航——事情繁琐的手动测试。</li>
<li>Modularity: Unit tests help keep you focused on writing more modular code.</li>
<li>模块化:单元测试帮助你保持专注于编写更模块化的代码。</li>
<li>Focus: Writing tests for micro features keep you focused on the small details.</li>
<li>专注:编写测试微特性让你只关注小细节。</li>
<li>Regression: Be sure that the bugs you fixed stay fixed — and aren’t broken by subsequent fixes.</li>
<li>回归:确保你固定保持固定的缺陷,不打破的后续补丁。</li>
<li>Refactoring: Until Xcode gets smart enough to refactor your code on its own, you’ll need unit tests to validate your refactoring.</li>
<li>重构:直到Xcode变得足够聪明能无错误的重构你的代码,否则你还是需要单元测试来验证你的重构。</li>
<li>Documentation: Unit tests describe what you think the code should do; they serve as another way to document your code.</li>
<li>单元测试文档:描述你认为的代码应该做什么;他们作为另一种方式来记录您的代码。</li>
</ul>
<p>The Basic App Structure</p>
<p>基本的应用程序结构</p>
<p>A lot of the code in the sample app is based on the Master-Detail Application template with Core Data enabled. But there are some significant improvements over the template code. Open the sample project in Xcode and have a look at the project navigator:</p>
<p>大量的示例应用程序中的代码是基于主从复合结构应用程序启用了核心数据的模板。但也有一些重大改进的模板代码。在Xcode中打开示例项目,看看项目导航器:</p>
<p><img src="/assets/img/ios/gpxj/12/1/3.png" alt="3.png" /></p>
<p>Take note of the following details:</p>
<p>注意以下细节:</p>
<p>There is a Person.swift and a PersonInfo.swift file. The Person class is an NSManagedObject that contains some basic information about each person. The PersonInfo struct contains the same information but can instanced from the address book.</p>
<p>这里有一个 Person.swift 和 PersonInfo.swift 的文件。Person类是一个NSManagedObject,包含关于每个人的一些基本信息。PersonInfo结构包含相同的信息,但可以从地址簿得到实例。</p>
<p>The folder PeopleList has three files: A view controller, a data provider and a data provider protocol.</p>
<p>文件夹PeopleList有三个文件:一个视图控制器,数据提供者和数据供应商协议。</p>
<p>The file collection in PeopleList is an attempt to avoid massive view controllers. It’s good practice to avoid massive view controllers by moving some responsibilities into other classes that communicate with the view controllers via a simple protocol. You can learn more about massive view controllers and how to avoid them by reading this interesting albeit older article.</p>
<p>文件收集PeopleList试图避免大规模视图控制器。这是好的做法,以避免大规模视图控制器通过移动一些责任到其他类与视图控制器通过一个简单的通信协议。您可以了解更多关于大规模视图控制器和如何避免它们通过阅读这个有趣但过时的文章。</p>
<p>In this case, the protocol is defined in PeopleListDataProviderProtocol.swift; open it and have a look. A class conforming to this protocol must have the properties managedObjectContext and tableView and must define the methods addPerson(_:) and fetch(). In addition, it must conform to the UITableViewDataSource protocol.</p>
<p>在这种情况下,协议是PeopleListDataProviderProtocol.swift中定义。打开它看看。类符合该协议必须属性managedObjectContext tableView和方法必须定义addPerson(_:)和fetch()。此外,它必须符合需要的协议。</p>
<p>The view controller PeopleListViewController has a property dataProvider, which conforms to PeopleListDataProviderProtocol. This property is set to an instance of PeopleListDataProvider in AppDelegate.swift.</p>
<p>视图控制器PeopleListViewController 包含一个 dataProvider属性,符合PeopleListDataProviderProtocol。这个属性设置为在AppDelegate.swift 中的一个PeopleListDataProvider的实例。</p>
<p>You add people to the list using ABPeoplePickerNavigationController. This class lets you, the developer, access the user’s contacts without requiring explicit permission.</p>
<p>你使用ABPeoplePickerNavigationController添加人到列表。开发人员,这个类允许您访问用户的联系人而不需要明确的许可。</p>
<p>PeopleListDataProvider is responsible for filling the table view and for talking to the Core Data persistent store.</p>
<p>PeopleListDataProvider负责填充表视图和与核心数据持久存储。</p>
<p>Note: Several classes and methods in the starter project are declared as public; this is so the test target can access those classes and methods. The test target is outside of the app module. If you don’t add any access modifier the classes and methods are defined as internal. This means they are only accessible within the same module. To access them from outside the module (for example from the test target) you need to add the public access modifier.</p>
<p>注意:起动器的几个类和方法声明为公共项目;这是所以测试目标可以访问这些类和方法。测试目标之外的应用程序模块。如果你不添加任何访问修饰符的类和方法被定义为内部。这意味着他们只能在同一个模块中进行访问。从外部访问它们模块(例如从测试目标)您需要添加公共访问修饰符。</p>
<h3>That’s enough overview — time to start writing some tests!</h3>
<p>概述了解够了——开始编写一些测试!</p>
<p>Writing Mocks</p>
<p>编写模拟</p>
<p>Mocks let you check if a method call is performed or if a property is set when something happens in your app. For example, in viewDidLoad() of PeopleListViewController, the table view is set to the tableView property of the dataProvider.</p>
<p>模拟让你检查是否执行方法调用或者属性设置当事情发生在你的应用程序。例如,在PeopleListViewController()中的viewDidLoad方法里,表视图设置为tableView的数据提供 dataProvider属性。</p>
<p>You’ll write a test to check that this actually happens.</p>
<p>您将编写一个测试来检查,这到底会不会发生。</p>
<p>Preparing Your App for Testing</p>
<p>准备你的应用程序进行测试</p>
<p>First, you need to prepare the project to make testing possible.</p>
<p>首先,你需要准备项目使测试成为可能。</p>
<p>Select the project in the project navigator, then select Build Settings in the Birthdays target. Search for Defines Module, and change the setting to Yes as shown below:</p>
<p>选择项目在项目导航器,然后选择在生日的目标建立设置。搜索定义模块,改变设置为Yes,如下所示:</p>
<p><img src="/assets/img/ios/gpxj/12/1/4.png" alt="4.png" /></p>
<p>Next, select the BirthdaysTests folder and go to File\New\File…. Select a iOS\Source\Test Case Classtemplate, click Next, name it PeopleListViewControllerTests, ensure you’re creating a Swift file, click Next, then finally click Create.</p>
<p>接下来,选择BirthdaysTests文件夹, 文件\新建\新文件…。选择一个iOS\源码\测试用例Classtemplate,单击Next,名字改为PeopleListViewControllerTests,确保你创建一个.swift文件,单击Next,然后单击Create。</p>
<p>If Xcode prompts you to create a bridging header, select No. This is a bug in Xcode that occurs when there is no file in the target and you add a Swift file.</p>
<p>如果Xcode提示您创建一个连接头,选择No。这是一个Bug。在Xcode,发生在没有文件在目标和你swift 添加一个文件。</p>
<p>Open the newly created PeopleListViewControllerTests.swift. Import the module you just enabled by adding the import Birthdays statement right after the other import statements as shown below:</p>
<p>打开新创建的PeopleListViewControllerTests.swift。您刚启用导入模块通过添加导入生日声明之后其他导入语句如下所示:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">import</span> <span class="kt">UIKit</span>
<span class="kd">import</span> <span class="kt">XCTest</span>
<span class="kd">import</span> <span class="kt">Birthdays</span></code></pre></figure></p>
<p>Remove the following two template test methods:</p>
<p>删除以下两个模板测试方法:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">testExample</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// This is an example of a functional test case.</span>
<span class="kt">XCTAssert</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span> <span class="s">"Pass"</span><span class="p">)</span>
<span class="p">}</span></p>
<p><span class="kd">func</span> <span class="nf">testPerformanceExample</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// This is an example of a performance test case.</span>
<span class="k">self</span><span class="o">.</span><span class="nf">measureBlock</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Put the code you want to measure the time of here.</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure></p>
<p>You now need an instance of PeopleListViewController so you can use it in your tests.</p>
<p>你现在需要一个实例PeopleListViewController才可以使用它在你的测试。</p>
<p>Add the following line to the beginning of PeopleListViewControllerTests:</p>
<p>添加以下行PeopleListViewControllerTests的开始:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">var</span> <span class="nv">viewController</span><span class="p">:</span> <span class="kt">PeopleListViewController</span><span class="o">!</span></code></pre></figure></p>
<p>Replace the setUp() method with the following code:</p>
<p>用以下代码替换setUp()方法:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">override</span> <span class="kd">func</span> <span class="nf">setUp</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="o">.</span><span class="nf">setUp</span><span class="p">()</span></p>
<pre><code><span class="n">viewController</span> <span class="o">=</span> <span class="kt">UIStoryboard</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"Main"</span><span class="p">,</span> <span class="nv">bundle</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span><span class="o">.</span><span class="nf">instantiateViewControllerWithIdentifier</span><span class="p">(</span><span class="s">"PeopleListViewController"</span><span class="p">)</span> <span class="k">as!</span> <span class="kt">PeopleListViewController</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<p>This uses the main storyboard to create an instance of PeopleListViewController and assigns it to viewController.</p>
<p>它使用的主要故事板创建viewController PeopleListViewController并赋予它的一个实例。</p>
<p>Select Product\Test; Xcode builds the project and runs any existing tests. Although you don’t have any tests yet, this is a good way to ensure everything is set up correctly. After a few seconds, Xcode should report that all tests succeeded.</p>
<p>选择产品\测试;Xcode项目构建和运行任何现有的测试。虽然你还没有任何测试,这是一个很好的方式,以确保一切都正确设置。几秒钟后,Xcode应该报告,所有测试成功了。</p>
<p>You’re now ready to create your first mock.</p>
<p>你现在准备好创建您的第一个模拟。</p>
<p>Writing Your First Mock</p>
<p>编写你的第一个模拟</p>
<p>Since you’re going to be working with Core Data, add the following import to the top of PeopleListViewControllerTests.swift, right below import Birthdays:</p>
<p>因为你将要使用核心数据,添加以下入口在PeopleListViewControllerTests.swift 的顶部。</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">import</span> <span class="kt">CoreData</span></code></pre></figure></p>
<p>Next, add the following code within the class definition of PeopleListViewControllerTests:</p>
<p>接下来,添加下面的代码在PeopleListViewControllerTests的类定义:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">MockDataProvider</span><span class="p">:</span> <span class="kt">NSObject</span><span class="p">,</span> <span class="kt">PeopleListDataProviderProtocol</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">managedObjectContext</span><span class="p">:</span> <span class="kt">NSManagedObjectContext</span><span class="p">?</span>
<span class="k">weak</span> <span class="k">var</span> <span class="nv">tableView</span><span class="p">:</span> <span class="kt">UITableView</span><span class="o">!</span></p>
<pre><code><span class="kd">func</span> <span class="nf">addPerson</span><span class="p">(</span><span class="nv">personInfo</span><span class="p">:</span> <span class="kt">PersonInfo</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
<span class="kd">func</span> <span class="nf">fetch</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
<span class="kd">func</span> <span class="nf">tableView</span><span class="p">(</span><span class="nv">tableView</span><span class="p">:</span> <span class="kt">UITableView</span><span class="p">,</span> <span class="n">numberOfRowsInSection</span> <span class="nv">section</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Int</span> <span class="p">{</span> <span class="k">return</span> <span class="mi">1</span> <span class="p">}</span>
<span class="kd">func</span> <span class="nf">tableView</span><span class="p">(</span><span class="nv">tableView</span><span class="p">:</span> <span class="kt">UITableView</span><span class="p">,</span> <span class="n">cellForRowAtIndexPath</span> <span class="nv">indexPath</span><span class="p">:</span> <span class="kt">NSIndexPath</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">UITableViewCell</span> <span class="p">{</span>
<span class="k">return</span> <span class="kt">UITableViewCell</span><span class="p">()</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<p>This looks like a quite complicated mock class. However, this is just the bare minimum required, as you’re going to set an instance of this mock class to the dataProvider property of PeopleListViewController. Your mock class also has to conform to the PeopleListDataProviderProtocol as well as the UITableViewDataSource protocol.</p>
<p>这看起来像一个相当复杂的模拟类。然而,这仅仅是所需的最低限度,当你去设置这个模拟类的一个实例PeopleListViewController的dataProvider属性。模拟类还必须符合PeopleListDataProviderProtocol以及需要显示协议。</p>
<p>Select Product\Test; your project will compile again and your zero tests will run with zero failures. Sorry — that doesn’t count at a 100% pass rate. :] But now you have everything set up for the first unit test using a mock.</p>
<p>选择产品\测试;要再次编译您的项目和你零测试将运行零故障。对不起,那不算通过率为100%。:]但现在你一切设置第一单元测试使用模拟。</p>
<p>It’s good practice to separate the unit tests in three parts called given, when and then. ‘Given’, sets up the environment; ‘when’ executes the code you want to test; and ‘then’ checks for the expected result.</p>
<p>是个好习惯独立的单元测试在三个部分,然后。“给予”,设置环境;“当”执行您想要测试的代码,为预期的结果和随后的检查。</p>
<p>Your test will check that the tableView property of the data provider is set after viewDidLoad() has been executed.</p>
<p>您的测试将检查数据提供者的tableView属性设置后viewDidLoad()被执行。</p>
<p>Add the following test to PeopleListViewControllerTests:</p>
<p>PeopleListViewControllerTests添加以下测试:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">testDataProviderHasTableViewPropertySetAfterLoading</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// given</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">mockDataProvider</span> <span class="o">=</span> <span class="kt">MockDataProvider</span><span class="p">()</span></p>
<pre><code><span class="n">viewController</span><span class="o">.</span><span class="n">dataProvider</span> <span class="o">=</span> <span class="n">mockDataProvider</span>
<span class="c1">// when</span>
<span class="c1">// 2</span>
<span class="kt">XCTAssertNil</span><span class="p">(</span><span class="n">mockDataProvider</span><span class="o">.</span><span class="n">tableView</span><span class="p">,</span> <span class="s">"Before loading the table view should be nil"</span><span class="p">)</span>
<span class="c1">// 3</span>
<span class="k">let</span> <span class="nv">_</span> <span class="o">=</span> <span class="n">viewController</span><span class="o">.</span><span class="n">view</span>
<span class="c1">// then </span>
<span class="c1">// 4</span>
<span class="kt">XCTAssertTrue</span><span class="p">(</span><span class="n">mockDataProvider</span><span class="o">.</span><span class="n">tableView</span> <span class="o">!=</span> <span class="kc">nil</span><span class="p">,</span> <span class="s">"The table view should be set"</span><span class="p">)</span>
<span class="kt">XCTAssert</span><span class="p">(</span><span class="n">mockDataProvider</span><span class="o">.</span><span class="n">tableView</span> <span class="o">===</span> <span class="n">viewController</span><span class="o">.</span><span class="n">tableView</span><span class="p">,</span>
<span class="s">"The table view should be set to the table view of the data source"</span><span class="p">)</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<p>Here is what the above test is doing:</p>
<p>这是上面的测试在做什么:</p>
<p>Creates an instance of MockDataProvider and sets it to the dataProvider property of the view controller.</p>
<p>MockDataProvider创建一个实例,并设置它的dataProvider属性视图控制器。</p>
<p>Asserts that the tableView property is nil before the test.</p>
<p>断言tableView财产零前测试。</p>
<p>Accesses the view to trigger viewDidLoad().</p>
<p>访问视图来触发viewDidLoad()。</p>
<p>Asserts that the test class’ tableView property is not nil and that it is set to the tableView of the view controller.</p>
<p>断言测试类的tableView属性不为空,它是tableView视图控制器。</p>
<p>Select Product\Test again; once the tests have finished, open the test navigator (Cmd+5 is a handy shortcut). You should see something like the following:</p>
<p>选择产品\再次测试,一旦测试完成后,打开Test navigator(Cmd + 5是一个方便的快捷方式)。您应当会看到类似如下:</p>
<p><img src="/assets/img/ios/gpxj/12/1/5.png" alt="5.png" /></p>
<p>Your first test with a mock passed with flying colors! :]</p>
<p>你的第一个测试模拟以优异的成绩通过了!:]</p>
<p>Testing addPerson(_:)</p>
<p>测试addPerson(_:)</p>
<p>The next test is to ensure selecting a contact from the list calls addPerson(_:) of the data provider.</p>
<p>下一个测试是为了确保从列表中选择一个联系人电话addPerson(_:)的数据提供者。</p>
<p>Add the following property to the MockDataProvider class:</p>
<p>MockDataProvider类添加以下属性:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">var</span> <span class="nv">addPersonGotCalled</span> <span class="o">=</span> <span class="kc">false</span></code></pre></figure></p>
<p>Next, change addPerson(_:) to the following:</p>
<p>接下来,改变addPerson(_:)如下:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">addPerson</span><span class="p">(</span><span class="nv">personInfo</span><span class="p">:</span> <span class="kt">PersonInfo</span><span class="p">)</span> <span class="p">{</span> <span class="n">addPersonGotCalled</span> <span class="o">=</span> <span class="kc">true</span> <span class="p">}</span></code></pre></figure></p>
<p>Now when you call addPerson(_:), you’ll register this in an instance of MockDataProvider by setting addPersonGotCalled to true.</p>
<p>现在当你调用addPerson(_:),您将在一个实例注册这个addPersonGotCalled MockDataProvider的设置为true。</p>
<p>You’ll have to import the AddressBookUI framework before you can add a method to test this behavior.</p>
<p>你必须导入AddressBookUI框架之前,您可以添加一个方法来测试这种行为。</p>
<p>Add the following import right below the other imports in PeopleListViewControllerTests.swift:</p>
<p>添加以下导入正确的其他进口PeopleListViewControllerTests.swift以下:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">import</span> <span class="kt">AddressBookUI</span></code></pre></figure></p>
<p>Now add the following test method with the rest of the test cases:</p>
<p>现在添加以下测试方法与测试用例:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">testCallsAddPersonOfThePeopleDataSourceAfterAddingAPersion</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// given</span>
<span class="k">let</span> <span class="nv">mockDataSource</span> <span class="o">=</span> <span class="kt">MockDataProvider</span><span class="p">()</span></p>
<pre><code><span class="c1">// 1</span>
<span class="n">viewController</span><span class="o">.</span><span class="n">dataProvider</span> <span class="o">=</span> <span class="n">mockDataSource</span>
<span class="c1">// when</span>
<span class="c1">// 2</span>
<span class="k">let</span> <span class="nv">record</span><span class="p">:</span> <span class="kt">ABRecord</span> <span class="o">=</span> <span class="kt">ABPersonCreate</span><span class="p">()</span><span class="o">.</span><span class="nf">takeRetainedValue</span><span class="p">()</span>
<span class="kt">ABRecordSetValue</span><span class="p">(</span><span class="n">record</span><span class="p">,</span> <span class="n">kABPersonFirstNameProperty</span><span class="p">,</span> <span class="s">"TestFirstname"</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
<span class="kt">ABRecordSetValue</span><span class="p">(</span><span class="n">record</span><span class="p">,</span> <span class="n">kABPersonLastNameProperty</span><span class="p">,</span> <span class="s">"TestLastname"</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
<span class="kt">ABRecordSetValue</span><span class="p">(</span><span class="n">record</span><span class="p">,</span> <span class="n">kABPersonBirthdayProperty</span><span class="p">,</span> <span class="kt">NSDate</span><span class="p">(),</span> <span class="kc">nil</span><span class="p">)</span>
<span class="c1">// 3</span>
<span class="n">viewController</span><span class="o">.</span><span class="nf">peoplePickerNavigationController</span><span class="p">(</span><span class="kt">ABPeoplePickerNavigationController</span><span class="p">(),</span>
<span class="nv">didSelectPerson</span><span class="p">:</span> <span class="n">record</span><span class="p">)</span>
<span class="c1">// then</span>
<span class="c1">// 4</span>
<span class="kt">XCTAssert</span><span class="p">(</span><span class="n">mockDataSource</span><span class="o">.</span><span class="n">addPersonGotCalled</span><span class="p">,</span> <span class="s">"addPerson should have been called"</span><span class="p">)</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<p>So what’s going on here?</p>
<p>这里发生了什么?</p>
<p>First you set the data provider of the view controller to an instance of your mock data provider.</p>
<p>首先设置数据提供者视图控制器的模拟数据提供者的一个实例。</p>
<p>Then you create a contact by using ABPersonCreate().</p>
<p>然后您创建一个接触通过ABPersonCreate()。</p>
<p>Here you manually call the delegate method peoplePickerNavigationController(_:didSelectPerson:). Normally, calling delegate methods manually is a code smell, but it’s fine for testing purposes.</p>
<p>在这里你手动调用委托方法peoplePickerNavigationController(_:didSelectPerson:)。正常情况下,手动调用委托方法代码味道,但它很好用于测试目的。</p>
<p>Finally you assert that addPerson(_:) was called by checking that addPersonGotCalled of the data provider mock is true.</p>
<p>最后你断言addPerson(_:)被称为通过检查addPersonGotCalled数据提供者的模拟是正确的。</p>
<p>Select Product\Test to run the tests — they should all pass. Hey, this testing thing is pretty easy!</p>
<p>选择产品\测试运行测试,他们都应该通过。嗨,这个测试是非常容易!</p>
<p>But wait! How do you know that the tests actually test what you think they’re testing?</p>
<p>但是等等!你怎么知道测试实际测试你认为他们正在测试什么?</p>
<p><img src="/assets/img/ios/gpxj/12/1/6.png" alt="6.png" /></p>
<h2>Testing Your Tests 测试您的测试</h2>
<hr />
<p>A quick way to check that a test is actually validating something is to remove the entity that the test validates.</p>
<p>一个快速的方法来检查一个测试是验证是删除实体的东西,测试验证。</p>
<p>Open PeopleListViewController.swift and comment out the following line</p>
<p>打开PeopleListViewController.swift并注释掉以下行</p>
<p>in peoplePickerNavigationController(_:didSelectPerson:):</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="n">dataProvider</span><span class="p">?</span><span class="o">.</span><span class="nf">addPerson</span><span class="p">(</span><span class="n">person</span><span class="p">)</span></code></pre></figure></p>
<p>Run the tests again; the last test you wrote should now fail. Cool — you now know that your test is actually testing something. It’s good practice to test your tests; at the very least you should test your most complicated tests to be sure they</p>
<p>再次运行测试,最后测试你写现在应该失败。很酷,你现在知道你的测试实际上是测试的东西。良好的实践测试测试,至少你应该测试你最复杂的测试,以确保他们的工作。</p>
<p><img src="/assets/img/ios/gpxj/12/1/7.png" alt="7.png" /></p>
<p>Un-comment the line to get the code back to a working state; run the tests again to make sure everything is working.</p>
<p>取消一行的注释的代码恢复到工作状态,再次运行测试,确保一切工作。</p>
<p>Mocking Apple Framework Classes</p>
<p>模拟苹果框架类</p>
<p>You may have used singletons such as NSNotificationCenter.defaultCenter() and NSUserDefaults.standardUserDefaults() — but how would you test that a notification is actually sent or that a default is set? Apple doesn’t allow you to inspect the state of these classes.</p>
<p>你可以使用单例如NSNotificationCenter.defaultCenter()和NSUserDefaults.standardUserDefaults(),但是你怎么测试,实际发送或通知默认设置吗?苹果不允许你检查这些类的状态。</p>
<p>You could add the test class as an observer for the expected notifications. But this might cause your tests to become slow and unreliable since they depend on the implementation of those classes. Or the notification could be sent from another part of your code, and you wouldn’t be testing an isolated behavior.</p>
<p>您可以添加的测试类作为观察员预计通知。但这可能导致您的测试变得缓慢和不可靠的,因为他们依赖于这些类的实现。或通知可以发送另一个代码的一部分,和你不会测试一个孤立的行为。</p>
<p>To get around these limitations, you can use mocks in place of these singletons.</p>
<p>为了解决这些限制,您可以使用模拟的单例。</p>
<blockquote><p>Note: When you replace Apple’s classes with mocks, it’s very important to only test the interaction with that class, not the behavior of that class, as implementation details could change at any point.</p>
<p>注意:当你用模拟取代苹果的类,这是非常重要的只有测试交互的类,类的行为,实现细节可以改变在任何时候。</p></blockquote>
<p>Build and run your app; add John Appleseed and David Taylor to the list of people and toggle the sorting between ‘Last Name’ and ‘First Name’. You’ll see that the order of the contacts in the list depends on the sort order of the table view.</p>
<p>构建和运行应用程序;约翰苹果子和大卫泰勒添加到列表的人之间切换排序“姓氏”和“名字”。你会发现联系人列表的顺序取决于表视图的排序顺序。</p>
<p>The code that’s responsible for sorting lives in changeSort() in PeopleListViewController.swift:</p>
<p>代码负责排序住在changeSort PeopleListViewController.swift():</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">@IBAction</span> <span class="kd">func</span> <span class="nf">changeSorting</span><span class="p">(</span><span class="nv">sender</span><span class="p">:</span> <span class="kt">UISegmentedControl</span><span class="p">)</span> <span class="p">{</span>
<span class="n">userDefaults</span><span class="o">.</span><span class="nf">setInteger</span><span class="p">(</span><span class="n">sender</span><span class="o">.</span><span class="n">selectedSegmentIndex</span><span class="p">,</span> <span class="nv">forKey</span><span class="p">:</span> <span class="s">"sort"</span><span class="p">)</span>
<span class="n">dataProvider</span><span class="p">?</span><span class="o">.</span><span class="nf">fetch</span><span class="p">()</span>
<span class="p">}</span></code></pre></figure></p>
<p>This adds the selected segment index for the key sort to the user defaults and calls fetch() on the data provider. fetch() should read this new sort order from the user defaults and update the contact list, as demonstrated in PeopleListDataProvider:</p>
<p>这种添加选中的部分指数关键用户默认值并调用获取数据提供者()。fetch()应该阅读这个新的排序顺序从用户违约和更新联系人列表,在PeopleListDataProvider:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">public</span> <span class="kd">func</span> <span class="nf">fetch</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="nv">sortKey</span> <span class="o">=</span> <span class="kt">NSUserDefaults</span><span class="o">.</span><span class="nf">standardUserDefaults</span><span class="p">()</span><span class="o">.</span><span class="nf">integerForKey</span><span class="p">(</span><span class="s">"sort"</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">?</span> <span class="s">"lastName"</span> <span class="p">:</span> <span class="s">"firstName"</span></p>
<pre><code><span class="k">let</span> <span class="nv">sortDescriptor</span> <span class="o">=</span> <span class="kt">NSSortDescriptor</span><span class="p">(</span><span class="nv">key</span><span class="p">:</span> <span class="n">sortKey</span><span class="p">,</span> <span class="k">as</span><span class="nv">cending</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">sortDescriptors</span> <span class="o">=</span> <span class="p">[</span><span class="n">sortDescriptor</span><span class="p">]</span>
<span class="n">fetchedResultsController</span><span class="o">.</span><span class="n">fetchRequest</span><span class="o">.</span><span class="n">sortDescriptors</span> <span class="o">=</span> <span class="n">sortDescriptors</span>
<span class="k">var</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">NSError</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="k">if</span> <span class="o">!</span><span class="n">fetchedResultsController</span><span class="o">.</span><span class="nf">performFetch</span><span class="p">(</span><span class="o">&amp;</span><span class="n">error</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">println</span><span class="p">(</span><span class="s">"error: </span><span class="se">\(</span><span class="n">error</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">tableView</span><span class="o">.</span><span class="nf">reloadData</span><span class="p">()</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<p>PeopleListDataProvider uses an NSFetchedResultsController to fetch data from the Core Data persistent store. To change the sorting of the list, fetch() creates an array with sort descriptors and sets it to the fetch request of the fetched results controller. Then it performs a fetch to update the list and call reloadData() on the table view.</p>
<p>PeopleListDataProvider使用NSFetchedResultsController获取核心数据持久存储的数据。改变列表的排序,取()创建了一个数组类型描述符和获取结果集的获取请求的控制器。然后执行获取更新列表和电话reloadData()在表视图。</p>
<p>You’ll now add a test to ensure the user’s preferred sort order is correctly set in NSUserDefaults.</p>
<p>现在,您将添加一个测试,以确保用户的首选NSUserDefaults排序顺序是正确的组。</p>
<p>Open PeopleListViewControllerTests.swift and add the following class definition right below the class definition of MockDataProvider:</p>
<p>PeopleListViewControllerTests开放。.swift和添加以下类定义正确的低于MockDataProvider的类定义:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">class</span> <span class="kt">MockUserDefaults</span><span class="p">:</span> <span class="kt">NSUserDefaults</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">sortWasChanged</span> <span class="o">=</span> <span class="kc">false</span>
<span class="k">override</span> <span class="kd">func</span> <span class="nf">setInteger</span><span class="p">(</span><span class="nv">value</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="n">forKey</span> <span class="n">defaultName</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">defaultName</span> <span class="o">==</span> <span class="s">"sort"</span> <span class="p">{</span>
<span class="n">sortWasChanged</span> <span class="o">=</span> <span class="kc">true</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure></p>
<p>MockUserDefaults is a subclass of NSUserDefaults; it has a boolean property sortWasChanged with a default value of false. It also overrides the method setInteger(_:forKey:) that changes the value of sortWasChanged to true.</p>
<p>MockUserDefaults NSUserDefaults的子类,它有一个布尔属性sortWasChanged默认值为false。还覆盖方法setInteger(_:forKey:),改变sortWasChanged的值为true。</p>
<p>Add the following test below the last test in your test class:</p>
<p>下面添加以下测试最后测试在测试类:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">testSortingCanBeChanged</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// given</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">mockUserDefaults</span> <span class="o">=</span> <span class="kt">MockUserDefaults</span><span class="p">(</span><span class="nv">suiteName</span><span class="p">:</span> <span class="s">"testing"</span><span class="p">)</span><span class="o">!</span>
<span class="n">viewController</span><span class="o">.</span><span class="n">userDefaults</span> <span class="o">=</span> <span class="n">mockUserDefaults</span></p>
<pre><code><span class="c1">// when</span>
<span class="c1">// 2</span>
<span class="k">let</span> <span class="nv">segmentedControl</span> <span class="o">=</span> <span class="kt">UISegmentedControl</span><span class="p">()</span>
<span class="n">segmentedControl</span><span class="o">.</span><span class="n">selectedSegmentIndex</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">segmentedControl</span><span class="o">.</span><span class="nf">addTarget</span><span class="p">(</span><span class="n">viewController</span><span class="p">,</span> <span class="nv">action</span><span class="p">:</span> <span class="s">"changeSorting:"</span><span class="p">,</span> <span class="nv">forControlEvents</span><span class="p">:</span> <span class="o">.</span><span class="kt">ValueChanged</span><span class="p">)</span>
<span class="n">segmentedControl</span><span class="o">.</span><span class="nf">sendActionsForControlEvents</span><span class="p">(</span><span class="o">.</span><span class="kt">ValueChanged</span><span class="p">)</span>
<span class="c1">// then</span>
<span class="c1">// 3</span>
<span class="kt">XCTAssertTrue</span><span class="p">(</span><span class="n">mockUserDefaults</span><span class="o">.</span><span class="n">sortWasChanged</span><span class="p">,</span> <span class="s">"Sort value in user defaults should be altered"</span><span class="p">)</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<p>Here’s the play-by-play of this test:</p>
<p>这个测试的比赛详情如下:</p>
<p>You first assign an instance of MockUserDefaults to userDefaults of the view controller; this technique is known as dependency injection).</p>
<p>你第一次分配的实例MockUserDefaults userDefaults视图控制器;这种技术被称为依赖注入)。</p>
<p>You then create an instance of UISegmentedControl, add the view controller as the target for the .ValueChanged control event and send the event.</p>
<p>然后UISegmentedControl创建一个实例,添加的视图控制器作为目标。ValueChanged控制事件和发送事件。</p>
<p>Finally, you assert that setInteger(_:forKey:) of the mock user defaults was called. Note that you don’t check if the value was actually stored in NSUserDefaults, since that’s an implementation detail.</p>
<p>最后,你断言setInteger(_:forKey)被称为模拟用户的默认值。注意,你不检查如果值实际上是存储在NSUserDefaults,因为这是一个实现细节。</p>
<p>Run your suite of tests — they should all succeed.</p>
<p>运行您的测试套件,他们都应该成功。</p>
<p>What about the case when you have a really complicated API or framework underneath your app, but all you really want to do is test a small feature without delving deep into the framework?</p>
<p>这种情况下,当你有一个非常复杂的API或框架下应用程序,但你真正想做的是测试一个小功能,没有深入深入框架?</p>
<p>That’s when you “fake” it ’till you make it! :]</p>
<p>你可以先“伪造”它,直到你让实现它!:]</p>
<p>Writing Fakes</p>
<p>伪造</p>
<p>Fakes behave like a full implementation of the classes they are faking. You use them as stand-ins for classes or structures that are too complicated to deal with for the purposes of your test.</p>
<p>假货的像一个完整的实现类那是假的。您使用它们作为替身类或结构太复杂,处理您的测试的目的。</p>
<p>In the case of the sample app, you don’t want to add records to and fetch them from the real Core Data persistent store in your tests. So instead, you’ll fake the Core Data persistent store.</p>
<p>在示例应用程序的情况下,你不想添加记录,获取他们的真正核心数据持久存储在你的测试。所以,你假的核心数据持久存储。</p>
<p>Select the BirthdaysTests folder and go to File\New\File…. Choose an iOS\Source\Test Case Class template and click Next. Name your class it PeopleListDataProviderTests, click Next and then Create.</p>
<p>选择BirthdaysTests文件夹和文件\ \新文件…。选择一个iOS源\ \测试用例类模板,然后单击Next。你的类名PeopleListDataProviderTests,单击Next,然后创建。</p>
<p>Again remove the following demo tests in the created test class:</p>
<p>演示测试后再删除创建的测试类:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">testExample</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="p">}</span></p>
<p><span class="kd">func</span> <span class="nf">testPerformanceExample</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="p">}</span></code></pre></figure></p>
<p>Add the following two imports to your class:</p>
<p>添加以下两个进口类:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">import</span> <span class="kt">Birthdays</span>
<span class="kd">import</span> <span class="kt">CoreData</span></code></pre></figure></p>
<p>Now add the following properties:</p>
<p>现在添加以下属性:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">var</span> <span class="nv">storeCoordinator</span><span class="p">:</span> <span class="kt">NSPersistentStoreCoordinator</span><span class="o">!</span>
<span class="k">var</span> <span class="nv">managedObjectContext</span><span class="p">:</span> <span class="kt">NSManagedObjectContext</span><span class="o">!</span>
<span class="k">var</span> <span class="nv">managedObjectModel</span><span class="p">:</span> <span class="kt">NSManagedObjectModel</span><span class="o">!</span>
<span class="k">var</span> <span class="nv">store</span><span class="p">:</span> <span class="kt">NSPersistentStore</span><span class="o">!</span></p>
<p><span class="k">var</span> <span class="nv">dataProvider</span><span class="p">:</span> <span class="kt">PeopleListDataProvider</span><span class="o">!</span></code></pre></figure></p>
<p>Those properties contain the major components that are used in the Core Data stack. To get started with Core Data, check out our tutorial, Core Data Tutorial: Getting Started</p>
<p>这些属性包含的主要组件中使用核心数据堆栈。开始使用核心数据,看看我们的教程,教程:核心数据开始</p>
<p>Add the following code to setUp():</p>
<p>添加以下代码来设置():</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">// 1</span>
<span class="n">managedObjectModel</span> <span class="o">=</span> <span class="kt">NSManagedObjectModel</span><span class="o">.</span><span class="nf">mergedModelFromBundles</span><span class="p">(</span><span class="kc">nil</span><span class="p">)</span>
<span class="n">storeCoordinator</span> <span class="o">=</span> <span class="kt">NSPersistentStoreCoordinator</span><span class="p">(</span><span class="nv">managedObjectModel</span><span class="p">:</span> <span class="n">managedObjectModel</span><span class="p">)</span>
<span class="n">store</span> <span class="o">=</span> <span class="n">storeCoordinator</span><span class="o">.</span><span class="nf">addPersistentStoreWithType</span><span class="p">(</span><span class="kt">NSInMemoryStoreType</span><span class="p">,</span>
<span class="nv">configuration</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="kt">URL</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="kc">nil</span><span class="p">,</span> <span class="nv">error</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span></p>
<p><span class="n">managedObjectContext</span> <span class="o">=</span> <span class="kt">NSManagedObjectContext</span><span class="p">()</span>
<span class="n">managedObjectContext</span><span class="o">.</span><span class="n">persistentStoreCoordinator</span> <span class="o">=</span> <span class="n">storeCoordinator</span></p>
<p><span class="c1">// 2</span>
<span class="n">dataProvider</span> <span class="o">=</span> <span class="kt">PeopleListDataProvider</span><span class="p">()</span>
<span class="n">dataProvider</span><span class="o">.</span><span class="n">managedObjectContext</span> <span class="o">=</span> <span class="n">managedObjectContext</span></code></pre></figure></p>
<p>Here’s what’s going on in the code above:</p>
<p>这是发生了什么在上面的代码:</p>
<p>setUp() creates a managed object context with an in-memory store. Normally the persistent store of Core Data is a file in the file system of the device. For these tests, you are creating a ‘persistent’ store in the memory of the device.</p>
<p>setUp()创建一个管理对象上下文与内存中的存储。通常核心数据的持久化存储设备的文件系统中的一个文件。对于这些测试,您创建一个“持续”存储在内存的设备。</p>
<p>Then you create an instance of PeopleListDataProvider and the managed object context with the in-memory store is set as its managedObjectContext. This means your new data provider will work like the real one, but without adding or removing objects to the persistent store of the app.</p>
<p>然后你创建一个实例PeopleListDataProvider和管理对象上下文内存中的存储设置managedObjectContext。这意味着你的新数据提供者将像真正的工作,但没有添加或删除对象的持久性存储应用程序。</p>
<p>Add the following two properties to PeopleListDataProviderTests:</p>
<p>PeopleListDataProviderTests添加以下两个属性:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">var</span> <span class="nv">tableView</span><span class="p">:</span> <span class="kt">UITableView</span><span class="o">!</span>
<span class="k">var</span> <span class="nv">testRecord</span><span class="p">:</span> <span class="kt">PersonInfo</span><span class="o">!</span></code></pre></figure></p>
<p>Now add the following code to the end of setUp():</p>
<p>现在添加以下代码的设置():</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">let</span> <span class="nv">viewController</span> <span class="o">=</span> <span class="kt">UIStoryboard</span><span class="p">(</span><span class="nv">name</span><span class="p">:</span> <span class="s">"Main"</span><span class="p">,</span> <span class="nv">bundle</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span><span class="o">.</span><span class="nf">instantiateViewControllerWithIdentifier</span><span class="p">(</span><span class="s">"PeopleListViewController"</span><span class="p">)</span> <span class="k">as!</span> <span class="kt">PeopleListViewController</span>
<span class="n">viewController</span><span class="o">.</span><span class="n">dataProvider</span> <span class="o">=</span> <span class="n">dataProvider</span></p>
<p><span class="n">tableView</span> <span class="o">=</span> <span class="n">viewController</span><span class="o">.</span><span class="n">tableView</span></p>
<p><span class="n">testRecord</span> <span class="o">=</span> <span class="kt">PersonInfo</span><span class="p">(</span><span class="nv">firstName</span><span class="p">:</span> <span class="s">"TestFirstName"</span><span class="p">,</span> <span class="nv">lastName</span><span class="p">:</span> <span class="s">"TestLastName"</span><span class="p">,</span> <span class="nv">birthday</span><span class="p">:</span> <span class="kt">NSDate</span><span class="p">())</span></code></pre></figure></p>
<p>This sets up the table view by instantiating the view controller from the storyboard and creates an instance of PersonInfo that will be used in the tests.</p>
<p>这个设置表视图的实例化视图控制器PersonInfo的故事板和创建一个实例,将用于测试。</p>
<p>When the test is done, you’ll need to discard the managed object context.</p>
<p>当测试完成后,你需要丢弃管理对象上下文。</p>
<p>Replace tearDown() with the following code:</p>
<p>用以下代码替换tearDown():</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="k">override</span> <span class="kd">func</span> <span class="nf">tearDown</span><span class="p">()</span> <span class="p">{</span>
<span class="n">managedObjectContext</span> <span class="o">=</span> <span class="kc">nil</span></p>
<pre><code><span class="k">var</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">NSError</span><span class="p">?</span> <span class="o">=</span> <span class="kc">nil</span>
<span class="kt">XCTAssert</span><span class="p">(</span><span class="n">storeCoordinator</span><span class="o">.</span><span class="nf">removePersistentStore</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="nv">error</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">error</span><span class="p">),</span>
<span class="s">"couldn't remove persistent store: </span><span class="se">\(</span><span class="n">error</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
<span class="k">super</span><span class="o">.</span><span class="nf">tearDown</span><span class="p">()</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<p>This code sets the managedObjectContext to nil to free up memory and removes the persistent store from the store coordinator. This is just basic housekeeping. You want to start each test with a fresh test store.</p>
<p>这段代码集managedObjectContext零释放内存的持久性存储和删除存储协调员。这只是基本的家务。你想开始用新的测试存储每个测试。</p>
<p>Now — you can write the actual test! Add the following test to your test class:</p>
<p>现在,你可以写实际的测试!以下测试添加到您的测试类:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">testThatStoreIsSetUp</span><span class="p">()</span> <span class="p">{</span>
<span class="kt">XCTAssertNotNil</span><span class="p">(</span><span class="n">store</span><span class="p">,</span> <span class="s">"no persistent store"</span><span class="p">)</span>
<span class="p">}</span></code></pre></figure></p>
<p>This tests checks that the store is not nil. It’s a good idea to have this check here to fail early in case the store could not be set up.</p>
<p>这个测试检查store不为空。这里是一个好主意这张支票早失败,以防store不能成立。</p>
<p>Run your tests — everything should pass.</p>
<p>运行您的测试,一切都应该通过。</p>
<p>The next test will check whether the data source provides the expected number of rows.</p>
<p>下一个测试将检查是否数据源提供了预期的行数。</p>
<p>Add the following test to the test class:</p>
<p>以下测试添加到测试类:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">testOnePersonInThePersistantStoreResultsInOneRow</span><span class="p">()</span> <span class="p">{</span>
<span class="n">dataProvider</span><span class="o">.</span><span class="nf">addPerson</span><span class="p">(</span><span class="n">testRecord</span><span class="p">)</span></p>
<pre><code><span class="kt">XCTAssertEqual</span><span class="p">(</span><span class="n">tableView</span><span class="o">.</span><span class="n">dataSource</span><span class="o">!.</span><span class="nf">tableView</span><span class="p">(</span><span class="n">tableView</span><span class="p">,</span> <span class="nv">numberOfRowsInSection</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="s">"After adding one person number of rows is not 1"</span><span class="p">)</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<p>First, you add a contact to the test store, then you assert that the number of rows is equal to 1.</p>
<p>首先,你将联系人添加到测试store,然后你断言的行数等于1。</p>
<p>Run the tests — they should all succeed.</p>
<p>运行测试,他们都应该成功。</p>
<p>By creating a fake “persistent” store that never writes to disk, you can keep your tests fast and your disk clean, while maintaining the confidence that when you actually run your app, everything will work as expected.</p>
<p>通过创建一个假的“持久性”store,没有写入磁盘,你可以保持你的测试速度和磁盘清洁,同时保持信心,当你实际运行应用程序,一切都会正常工作。</p>
<p>In a real test suite you could also test the number of sections and rows after adding two or more test contacts; this all depends on the level of confidence you’re attempting to reach in your project.</p>
<p>在实际测试套件还可以测试部分的数量和行添加两个或两个以上的测试接触之后,这一切都取决于水平的信心你试图达到您的项目。</p>
<p>If you’ve ever worked with several teams at once on a project, you know that not all parts of the project are ready at the same time — but you still need to test your code. But how can you test a part of your code against something that may not exist, such as a web service or other back-end provider?</p>
<p>如果你曾经使用过几个团队项目,你知道,并不是所有地区的项目准备好了在同一时间,但你仍然需要测试你的代码。但你怎么能测试你的代码的一部分对可能不存在的东西,比如web服务或其他后端提供者?</p>
<p>Stubs to the rescue! :]</p>
<p>桩救援!:]</p>
<p>Writing Stubs</p>
<p>编写桩</p>
<p>Stubs fake a response to method calls of an object. You’ll use stubs to test your code against a web service that isn’t yet finished.</p>
<p>桩假响应对象的方法调用。您将使用桩来测试您的代码与一个web服务,它还没有完成。</p>
<p>The web team for your project has been tasked with building a website with the same functionality of the app. The user creates an account on the website and can then synchronize the data between the app and the website. But the web team hasn’t even started – and you’re nearly done. Looks like you’ll have to write a stub to stand-in for the web backend.</p>
<p>web团队为您的项目一直肩负着建设一个网站相同的功能的应用程序。用户在网站上创建一个帐户,然后应用程序和网站之间的数据同步。但网络团队甚至还没有开始,你差不多了。看来你得写一个桩替身web端。</p>
<p>In this section you will focus on two test methods: one for fetching contacts added to the website, and one to post contacts from your app to the website. In a real-world scenario you’d also need some kind of login mechanism and error handling, but that’s beyond the scope of this tutorial.</p>
<p>在本节中,您将重点关注两个测试方法:一个用于获取联系人添加到网站,和一个,联系人从应用程序发布到网站上去。在真实的场景中你也需要某种类型的登录和错误处理机制,但这超出了本教程的范围。</p>
<p>Open APICommunicatorProtocol.swift; this protocol declares the two methods for getting contacts from the web service and for posting contacts to the web service.</p>
<p>APICommunicatorProtocol开放。.swift;这协议声明了两个方法从web服务获取联系和接触到web服务。</p>
<p>You could pass around Person instances, but this would require you to use another managed object context. Using a struct is simpler in this case.</p>
<p>你可以通过在Person实例,但这需要你使用另一个管理对象上下文。在这种情况下使用一个结构比较简单。</p>
<p>Now open APICommunicator.swift. APICommunicator conforms to APICommunicatorProtocol, but right now there’s just enough implementation to keep the compiler happy.</p>
<p>现在APICommunicator.swift开放。APICommunicator符合APICommunicatorProtocol,但是现在只是有足够让编译器实现幸福。</p>
<p>You’ll now create stubs to support the interaction of the view controller with an instance of APICommunicator.</p>
<p>现在您将创建桩支持交互视图控制器的APICommunicator的实例。</p>
<p>Open PeopleListViewControllerTests.swift and add the following class definition within the PeopleListViewControllerTests class:</p>
<p>PeopleListViewControllerTests开放。.swift和添加以下PeopleListViewControllerTests类中的类定义:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">// 1</span>
<span class="kd">class</span> <span class="kt">MockAPICommunicator</span><span class="p">:</span> <span class="kt">APICommunicatorProtocol</span> <span class="p">{</span>
<span class="k">var</span> <span class="nv">allPersonInfo</span> <span class="o">=</span> <span class="p"><a href=""></span><span class="kt">PersonInfo</span><span class="p"></a></span>
<span class="k">var</span> <span class="nv">postPersonGotCalled</span> <span class="o">=</span> <span class="kc">false</span></p>
<pre><code><span class="c1">// 2</span>
<span class="kd">func</span> <span class="nf">getPeople</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="kt">NSError</span><span class="p">?,</span> <span class="p">[</span><span class="kt">PersonInfo</span><span class="p">]?)</span> <span class="p">{</span>
<span class="nf">return</span> <span class="p">(</span><span class="kc">nil</span><span class="p">,</span> <span class="n">allPersonInfo</span><span class="p">)</span>
<span class="p">}</span>
<span class="c1">// 3</span>
<span class="kd">func</span> <span class="nf">postPerson</span><span class="p">(</span><span class="nv">personInfo</span><span class="p">:</span> <span class="kt">PersonInfo</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">NSError</span><span class="p">?</span> <span class="p">{</span>
<span class="n">postPersonGotCalled</span> <span class="o">=</span> <span class="kc">true</span>
<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<p>There are few things to note here:</p>
<p>这里有几点需要注意:</p>
<p>Even though APICommunicator is a struct, the mock implementation is a class. It’s more convenient to use a class in this case because your tests require you to mutate data. This is a little easier to do in a class than in a struct.</p>
<p>尽管APICommunicator结构,模拟实现一个类。它更方便地使用类在这种情况下,因为您的测试需要变异的数据。这是一个小更容易在一个类结构。</p>
<p>getPeople() returns what is stored in allPersonInfo. Instead of going out on the web and having to download or parse data, you just store contact information in a simple array.</p>
<p>getPeople()返回存储在allPersonInfo什么。在网络上,而不是出去,必须下载或解析数据,你只联系信息存储在一个简单的数组。</p>
<p>postPerson(_:) sets postPersonGotCalled to true.</p>
<p>postPerson(_)集postPersonGotCalled为true。</p>
<p>You’ve just created your “web API” in under 20 lines of code! :]</p>
<p>你刚刚创建的web API在20行代码!:]</p>
<p>Now it’s time to test your stub API by ensuring all contacts that come back from the API are added to the persistent store on the device when you call addPerson().</p>
<p>现在是时候测试你的桩API通过确保所有联系人回来从API添加到持久存储在设备上,当你调用addPerson()。</p>
<p>Add the following test method to PeopleListViewControllerTests:</p>
<p>添加以下PeopleListViewControllerTests测试方法:</p>
<p><figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">testFetchingPeopleFromAPICallsAddPeople</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// given</span>
<span class="c1">// 1</span>
<span class="k">let</span> <span class="nv">mockDataProvider</span> <span class="o">=</span> <span class="kt">MockDataProvider</span><span class="p">()</span>
<span class="n">viewController</span><span class="o">.</span><span class="n">dataProvider</span> <span class="o">=</span> <span class="n">mockDataProvider</span></p>
<pre><code><span class="c1">// 2</span>
<span class="k">let</span> <span class="nv">mockCommunicator</span> <span class="o">=</span> <span class="kt">MockAPICommunicator</span><span class="p">()</span>
<span class="n">mockCommunicator</span><span class="o">.</span><span class="n">allPersonInfo</span> <span class="o">=</span> <span class="p">[</span><span class="kt">PersonInfo</span><span class="p">(</span><span class="nv">firstName</span><span class="p">:</span> <span class="s">"firstname"</span><span class="p">,</span> <span class="nv">lastName</span><span class="p">:</span> <span class="s">"lastname"</span><span class="p">,</span>
<span class="nv">birthday</span><span class="p">:</span> <span class="kt">NSDate</span><span class="p">())]</span>
<span class="n">viewController</span><span class="o">.</span><span class="n">communicator</span> <span class="o">=</span> <span class="n">mockCommunicator</span>
<span class="c1">// when</span>
<span class="n">viewController</span><span class="o">.</span><span class="nf">fetchPeopleFromAPI</span><span class="p">()</span>
<span class="c1">// then</span>
<span class="c1">// 3</span>
<span class="kt">XCTAssert</span><span class="p">(</span><span class="n">mockDataProvider</span><span class="o">.</span><span class="n">addPersonGotCalled</span><span class="p">,</span> <span class="s">"addPerson should have been called"</span><span class="p">)</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<p>Here’s what going on in the above code:</p>
<p>以下是在上面的代码:</p>
<p>First you set up the mock objects mockDataProvider and mockCommunicator you’ll use in the test.</p>
<p>首先建立了模拟对象mockDataProvider mockCommunicator你将使用在测试。</p>
<p>Then you set up some fake contacts and call fetchPeopleFromAPI() to make a fake network call.</p>
<p>然后你设置一些假的接触和调用fetchPeopleFromAPI()做一个假的网络电话。</p>
<p>Finally you test that addPerson(_:) was called.</p>
<p>最后测试,addPerson(_:)。</p>
<p>Build and run your tests — all should pass.</p>
<p>构建和运行您的测试,所有应通过。</p>
<h2>Where to Go From Here? 接下来去哪?</h2>
<hr />
<p>Download the final project here; this version also includes some extra tests that didn’t make it into the tutorial.</p>
<p>下载最后的项目在这里,这个版本还包括一些额外的测试,没有进入教程。</p>
<p>You’ve learned how to write mocks, fakes and stubs to test micro features in your app, along with getting a sense how XCTest works in Swift.</p>
<p>您已经了解了如何编写模拟,假货和桩来测试微特性在您的应用程序,以及在迅速感觉XCTest是如何工作的。</p>
<p>The tests in this tutorial are only a starter; I’m sure you already have ideas for tests in your own projects.</p>
<p>本教程中的测试只是一个起动器;我相信你已经测试的想法在自己的项目中。</p>
<p>For more on unit testing, check out Test Driven Development (TDD) and Behavior Driven Development (BDD). Both are development methodologies (and, frankly, a whole new mindset) where you write the tests before you write the code.</p>
<p>更多关于单元测试、检查测试驱动开发(TDD)和行为驱动开发(BDD)。都是开发方法(坦率地说,一个全新的心态),您编写测试,然后再编写代码。</p>
<p>You can listen to tutorial team member Ellen Shapiro discuss unit testing in the official Ray Wenderlich podcast.</p>
<p>你可以听听教程团队成员艾伦·夏皮罗在官方讨论单元测试雷Wenderlich播客。</p>
<p>Unit Tests are only one part of a complete test suite; integration tests are the next logical step. An easy way to start working with integration tests is UIAutomation. It’s well worth the read if you’re serious about testing your apps — and you should be! :]</p>
<p>单元测试是唯一一个完整的测试套件的一部分,集成测试是下一个合乎逻辑的步骤。一个简单的方法来集成测试是UIAutomation开始工作。是值得的如果你真的了解测试你的应用程序,您应该!:]</p>
<p>If you have any comments or questions about this tutorial, feel free to join the discussion in the forum below!</p>
<p>如果你有任何意见或疑问本教程,请加入下面的讨论论坛!</p>
项目第十一天 CocoaLumberjack2015-07-16T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/16/gpxj-11-day<h2>开源库</h2>
<hr />
<p>CocoaLumberjack:<a href="https://github.com/CocoaLumberjack/CocoaLumberjack">https://github.com/CocoaLumberjack/CocoaLumberjack</a></p>
<h1>CocoaLumberjack</h1>
<p><a href="https://travis-ci.org/CocoaLumberjack/CocoaLumberjack"><img src="https://travis-ci.org/CocoaLumberjack/CocoaLumberjack.svg" alt="Build Status" /></a>
<a href="http://cocoadocs.org/docsets/CocoaLumberjack/"><img src="http://img.shields.io/cocoapods/v/CocoaLumberjack.svg?style=flat" alt="Pod Version" /></a>
<a href="https://github.com/Carthage/Carthage"><img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat" alt="Carthage compatible" /></a>
<a href="http://cocoadocs.org/docsets/CocoaLumberjack/"><img src="http://img.shields.io/cocoapods/p/CocoaLumberjack.svg?style=flat" alt="Pod Platform" /></a>
<a href="http://opensource.org/licenses/BSD-3-Clause"><img src="http://img.shields.io/cocoapods/l/CocoaLumberjack.svg?style=flat" alt="Pod License" /></a>
<a href="https://www.versioneye.com/objective-c/cocoalumberjack/references"><img src="https://www.versioneye.com/objective-c/cocoalumberjack/reference_badge.svg?style=flat" alt="Reference Status" /></a></p>
<p><strong>CocoaLumberjack</strong> is a fast & simple, yet powerful & flexible logging framework for Mac and iOS.</p>
<h3>How to get started</h3>
<ul>
<li>install via <a href="http://cocoapods.org">CocoaPods</a></li>
</ul>
<p><figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">platform</span> <span class="ss">:ios</span><span class="p">,</span> <span class="s1">'5.0'</span>
<span class="n">pod</span> <span class="s1">'CocoaLumberjack'</span></code></pre></figure></p>
<ul>
<li>or <a href="Documentation/GettingStarted.md#manual-installation">install manually</a></li>
<li>read the <a href="Documentation/GettingStarted.md">Getting started</a> guide, check out the <a href="Documentation/FAQ.md">FAQ</a> section or the other <a href="Documentation/">docs</a></li>
<li>if you find issues or want to suggest improvements, create an issue or a pull request</li>
<li>for all kinds of questions involving CocoaLumberjack, use the <a href="http://groups.google.com/group/cocoalumberjack">Google group</a> or StackOverflow (use <a href="http://stackoverflow.com/questions/tagged/lumberjack">#lumberjack</a>).</li>
</ul>
<h3>CocoaLumberjack 2</h3>
<h4>Migrating to 2.x</h4>
<ul>
<li>Replace <code>DDLog.h</code> imports by <code>#import <CocoaLumberjack/CocoaLumberjack.h></code>.</li>
</ul>
<p>Advanced users, third party libraries:</p>
<ul>
<li>Replace all <code>DDLogC</code> macros for regular <code>DDLog</code> macros.</li>
<li>Replace log level (<code>LOG_LEVEL_*</code>) macros with <code>DDLogLevel</code> enum values</li>
<li>Replace log flag (<code>LOG_FLAG_*</code>) macros with <code>DDLogFlag</code> enum values</li>
<li>Replace <code>DDLogMessage</code> ivars and method calls to the new ivars and methods</li>
<li><code>logMsg</code> with <code>_message</code></li>
<li><code>logLevel</code> with <code>_level</code></li>
<li><code>logFlag</code> with <code>_flag</code></li>
<li><code>logContext</code> with <code>_context</code></li>
<li><code>lineNumber</code> with <code>_line</code> (type changed from <code>int</code> to <code>NSUInteger</code>)</li>
<li><code>file</code> with <code>_file</code> (<code>filename</code> contains just the file name, without the extension and the full path)</li>
<li><code>timestamp</code> with <code>_timestamp</code></li>
<li><code>methodName</code> with <code>function</code></li>
<li>Replace <code>DDAbstractLogger</code> <code>formatter</code> to <code>logFormatter</code></li>
<li><code>YSSingleFileLogger</code> ivars are no longer accesible, use the methods instead</li>
<li>Replace <code>[DDLog addLogger:withLogLevel:]</code> with <code>[DDLog addLogger:withLevel:]</code></li>
</ul>
<h4>Forcing 1.x</h4>
<p>If an included library requires it, you can force CocoaLumberjack 1.x by setting the version before the conflicting library:</p>
<p><figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">pod</span> <span class="s1">'CocoaLumberjack'</span><span class="p">,</span> <span class="s1">'~> 1.9'</span>
<span class="n">pod</span> <span class="s1">'ConflictingLibrary'</span></code></pre></figure></p>
<h3>Features</h3>
<h4>Lumberjack is Fast & Simple, yet Powerful & Flexible.</h4>
<p>It is similar in concept to other popular logging frameworks such as log4j, yet is designed specifically for Objective-C, and takes advantage of features such as multi-threading, grand central dispatch (if available), lockless atomic operations, and the dynamic nature of the Objective-C runtime.</p>
<h4>Lumberjack is Fast</h4>
<p>In most cases it is an order of magnitude faster than NSLog.</p>
<h4>Lumberjack is Simple</h4>
<p>It takes as little as a single line of code to configure lumberjack when your application launches. Then simply replace your NSLog statements with DDLog statements and that's about it. (And the DDLog macros have the exact same format and syntax as NSLog, so it's super easy.)</p>
<h4>Lumberjack is Powerful:</h4>
<p>One log statement can be sent to multiple loggers, meaning you can log to a file and the console simultaneously. Want more? Create your own loggers (it's easy) and send your log statements over the network. Or to a database or distributed file system. The sky is the limit.</p>
<h4>Lumberjack is Flexible:</h4>
<p>Configure your logging however you want. Change log levels per file (perfect for debugging). Change log levels per logger (verbose console, but concise log file). Change log levels per xcode configuration (verbose debug, but concise release). Have your log statements compiled out of the release build. Customize the number of log levels for your application. Add your own fine-grained logging. Dynamically change log levels during runtime. Choose how & when you want your log files to be rolled. Upload your log files to a central server. Compress archived log files to save disk space...</p>
<h3>This framework is for you if:</h3>
<ul>
<li>You're looking for a way to track down that impossible-to-reproduce bug that keeps popping up in the field.</li>
<li>You're frustrated with the super short console log on the iPhone.</li>
<li>You're looking to take your application to the next level in terms of support and stability.</li>
<li>You're looking for an enterprise level logging solution for your application (Mac or iPhone).</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><strong><a href="Documentation/GettingStarted.md">Get started using Lumberjack</a></strong><br/></li>
<li><a href="Documentation/XcodeTricks.md">Different log levels for Debug and Release builds</a><br/></li>
<li><a href="Documentation/PerLoggerLogLevels.md">Different log levels for each logger</a><br/></li>
<li><a href="Documentation/XcodeColors.md">Use colors in the Xcode debugging console</a><br/></li>
<li><a href="Documentation/CustomFormatters.md">Write your own custom formatters</a><br/></li>
<li><a href="Documentation/FAQ.md">FAQ</a><br/></li>
<li><a href="Documentation/Performance.md">Analysis of performance with benchmarks</a><br/></li>
<li><a href="Documentation/ProblemSolution.md">Common issues you may encounter and their solutions</a><br/></li>
<li><a href="Documentation/AppCode-support.md">AppCode support</a></li>
<li><strong><a href="Documentation/">Full Lumberjack documentation</a></strong><br/></li>
</ul>
<h3>Requirements</h3>
<ul>
<li>Xcode 4.4 or later is required</li>
<li>iOS 5 or later</li>
<li>OS X 10.7 or later</li>
<li>for OS X < 10.7 support, use the 1.6.0 version</li>
</ul>
<h3>Author</h3>
<ul>
<li><a href="https://github.com/robbiehanson">Robbie Hanson</a></li>
<li>Love the project? Wanna buy me a coffee? (or a beer :D) <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UZRA26JPJB3DA"><img src="http://www.paypal.com/en_US/i/btn/btn_donate_SM.gif" alt="donation" /></a></li>
</ul>
<h3>Collaborators</h3>
<ul>
<li><a href="https://github.com/rivera-ernesto">Ernesto Rivera</a></li>
<li><a href="https://github.com/dvor">Dmitry Vorobyov</a></li>
<li><a href="https://github.com/bpoplauschi">Bogdan Poplauschi</a></li>
</ul>
<h3>License</h3>
<ul>
<li>CocoaLumberjack is available under the BSD license. See the <a href="https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/LICENSE.txt">LICENSE file</a>.</li>
</ul>
项目第十天 ReactiveCocoa - iOS开发的新框架2015-07-15T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/15/gpxj-10-day<h2>来源</h2>
<hr />
<p>原文: <a href="http://www.infoq.com/cn/articles/reactivecocoa-ios-new-develop-framework">ReactiveCocoa - iOS开发的新框架</a></p>
<p>ReactiveCocoa(其简称为RAC)是由Github 开源的一个应用于iOS和OS X开发的新框架。RAC具有函数式编程和响应式编程的特性。它主要吸取了.Net的 Reactive Extensions的设计和实现。本文将详细介绍该框架试图解决什么问题,以及其用法与特点。</p>
<h3>ReactiveCocoa试图解决什么问题</h3>
<p>经过一段时间的研究,我认为ReactiveCocoa试图解决以下3个问题:</p>
<ol>
<li>传统iOS开发过程中,状态以及状态之间依赖过多的问题</li>
<li>传统MVC架构的问题:Controller比较复杂,可测试性差</li>
<li>提供统一的消息传递机制</li>
</ol>
<h3>传统iOS开发过程中,状态以及状态之间依赖过多的问题</h3>
<p>我们在开发iOS应用时,一个界面元素的状态很可能受多个其它界面元素或后台状态的影响。</p>
<p>例如,在用户帐户的登录界面,通常会有2个输入框(分别输入帐号和密码)和一个登录按钮。如果我们要加入一个限制条件:当用户输入完帐号和密码,并且登录的网络请求还未发出时,确定按钮才可以点击。通常情况下,我们需要监听这两个输入框的状态变化以及登录的网络请求状态,然后修改另一个控件的enabled状态。</p>
<p>传统的写法如下(该示例代码修改自<a href="https://github.com/ReactiveCocoa/ReactiveCocoa">ReactiveCocoa官网</a> ) :</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">static void *ObservationContext = &ObservationContext;
(void)viewDidLoad {
[super viewDidLoad];</p>
<pre><code>[LoginManager.sharedManager addObserver:self
forKeyPath:@"loggingIn"
options:NSKeyValueObservingOptionInitial
context:&amp;ObservationContext];
[self.usernameTextField addTarget:self action:@selector(updateLogInButton)
forControlEvents:UIControlEventEditingChanged];
[self.passwordTextField addTarget:self action:@selector(updateLogInButton)
forControlEvents:UIControlEventEditingChanged];
</code></pre>
<p>}</p>
<ul>
<li><p>(void)updateLogInButton {
BOOL textFieldsNonEmpty = self.usernameTextField.text.length > 0
&& self.passwordTextField.text.length > 0;
BOOL readyToLogIn = !LoginManager.sharedManager.isLoggingIn && !self.loggedIn;
self.logInButton.enabled = textFieldsNonEmpty && readyToLogIn;
}</p></li>
<li><p>(void)observeValueForKeyPath:(NSString <em>)keyPath ofObject:(id)object
change:(NSDictionary </em>)change context:(void *)context {
if (context == ObservationContext) {
[self updateLogInButton];
} else {
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
}</code></pre></figure></p></li>
</ul>
<p>RAC通过引入信号(Signal)的概念,来代替传统iOS开发中对于控件状态变化检查的代理(delegate)模式或target-action模式。因为RAC的信号是可以组合(combine)的,所以可以轻松地构造出另一个新的信号出来,然后将按钮的enabled状态与新的信号绑定。如下所示:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">RAC(self.logInButton, enabled) = [RACSignal
combineLatest:@[
self.usernameTextField.rac_textSignal,
self.passwordTextField.rac_textSignal,
RACObserve(LoginManager.sharedManager, loggingIn),
RACObserve(self, loggedIn)
] reduce:^(NSString <em>username, NSString </em>password, NSNumber *
loggingIn, NSNumber *loggedIn) {
return @(username.length > 0 && password.length > 0 && !
loggingIn.boolValue && !loggedIn.boolValue);
}];</code></pre></figure></p>
<p>可以看到,在引入RAC之后,以前散落在action-target或KVO的回调函数中的判断逻辑被统一到了一起,从而使得登录按钮的enabled状态被更加清晰地表达了出来。</p>
<p>除了组合(combine)之外,RAC的信号还支持链式(chaining)和过滤(filter),以方便将信号进行进一步处理。</p>
<h3>试图解决MVC框架的问题</h3>
<p>对于传统的Model-View-Controller的框架,Controller很容易变得比较庞大和复杂。由于Controller承担了Model和View之间的桥梁作用,所以Controller常常与对应的View和Model的耦合度非常高,这同时也造成对其做单元测试非常不容易,对iOS工程的单元测试大多都只在一些工具类或与界面无关的逻辑类中进行。</p>
<p>RAC的信号机制很容易将某一个Model变量的变化与界面关联,所以非常容易应用Model-View-ViewModel 框架。通过引入ViewModel层,然后用RAC将ViewModel与View关联,View层的变化可以直接响应ViewModel层的变化,这使得Controller变得更加简单,由于View不再与Model绑定,也增加了View的可重用性。</p>
<p>因为引入了ViewModel层,所以单元测试可以在ViewModel层进行,iOS工程的可测试性也大大增强了。InfoQ也曾撰文介绍过MVVM:《MVVM启示录》 。</p>
<h3>统一消息传递机制</h3>
<p>iOS开发中有着各种消息传递机制,包括KVO、Notification、delegation、block以及target-action方式。各种消息传递机制使得开发者在做具体选择时感到困惑,例如在objc.io上就有专门撰文(破船的翻译 ),介绍各种消息传递机制之间的差异性。</p>
<p>RAC将传统的UI控件事件进行了封装,使得以上各种消息传递机制都可以用RAC来完成。示例代码如下:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">// KVO
[RACObserve(self, username) subscribeNext:^(id x) {
NSLog(@"成员变量 username 被修改成了:%@", x);
}];</p>
<p>// target-action
self.button.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
NSLog(@"按钮被点击");
return [RACSignal empty];
}];</p>
<p>// Notification
[[[NSNotificationCenter defaultCenter]
rac_addObserverForName:UIKeyboardDidChangeFrameNotification
object:nil]
subscribeNext:^(id x) {
NSLog(@"键盘Frame改变");
}
];</p>
<p>// Delegate
[[self rac_signalForSelector:@selector(viewWillAppear:)] subscribeNext:^(id x) {
debugLog(@"viewWillAppear方法被调用 %@", x);
}];</code></pre></figure></p>
<p>RAC的RACSignal 类也提供了createSignal方法来让用户创建自定义的信号,如下代码创建了一个下载指定网站内容的信号。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">-(RACSignal <em>)urlResults {
return [RACSignal createSignal:^RACDisposable </em>(id<RACSubscriber> subscriber) {
NSError <em>error;
NSString </em>result = [NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.devtang.com"]
encoding:NSUTF8StringEncoding
error:&error];
NSLog(@"download");
if (!result) {
[subscriber sendError:error];
} else {
[subscriber sendNext:result];
[subscriber sendCompleted];
}
return [RACDisposable disposableWithBlock:^{
NSLog(@"clean up");
}];
}];</p>
<p>}</code></pre></figure></p>
<h2>如何使用ReactiveCocoa</h2>
<hr />
<p>ReactiveCocoa可以在iOS和OS X的应用开发中使用,对于iOS开发者,可以将RAC源码下载编译后,使用编译好的libReactiveCocoa-iOS.a文件。</p>
<p>开发者也可以用CocoaPods来设置目标工程对ReactiveCocoa的依赖,只需要编辑Podfile文件,增加如下内容即可:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">pod <span class="s1">'ReactiveCocoa'</span></code></pre></figure></p>
<h3>ReactiveCocoa的特点</h3>
<p>RAC在应用中大量使用了block,由于Objective-C语言的内存管理是基于引用计数 的,为了避免循环引用问题,在block中如果要引用self,需要使用@weakify(self)和@strongify(self)来避免强引用。另外,在使用时应该注意block的嵌套层数,不恰当的滥用多层嵌套block可能给程序的可维护性带来灾难。</p>
<p>RAC的编程方式和传统的MVC方式差异巨大,所以需要较长的学习时间。并且,业界内对于RAC并没有广泛应用,这造成可供参考的项目和教程比较欠缺。 另外,RAC项目本身也还在快速演进当中,1.x版本和2.x版本API改动了许多,3.0版本也正在快速开发中,对它的使用也需要考虑后期的升级维护问题。</p>
<p>作为一个iOS开发领域的新开源框架,ReactiveCocoa带来了函数式编程和响应式编程的思想,值得大家关注并且学习。</p>
<h3>一些学习资源</h3>
<p>博客&教程</p>
<p><a href="http://spin.atomicobject.com/2014/02/03/objective-c-delegate-pattern/">http://spin.atomicobject.com/2014/02/03/objective-c-delegate-pattern/</a><br/>
<a href="http://blog.bignerdranch.com/4549-data-driven-ios-development-reactivecocoa/">http://blog.bignerdranch.com/4549-data-driven-ios-development-reactivecocoa/</a><br/>
<a href="http://en.wikipedia.org/wiki/Functional_reactive_programming">http://en.wikipedia.org/wiki/Functional_reactive_programming</a><br/>
<a href="http://www.teehanlax.com/blog/reactivecocoa/">http://www.teehanlax.com/blog/reactivecocoa/</a><br/>
<a href="http://www.teehanlax.com/blog/getting-started-with-reactivecocoa/">http://www.teehanlax.com/blog/getting-started-with-reactivecocoa/</a><br/>
<a href="http://nshipster.com/reactivecocoa/">http://nshipster.com/reactivecocoa/</a><br/>
<a href="http://cocoasamurai.blogspot.com/2013/03/basic-mvvm-with-reactivecocoa.html">http://cocoasamurai.blogspot.com/2013/03/basic-mvvm-with-reactivecocoa.html</a><br/>
<a href="http://iiiyu.com/2013/09/11/learning-ios-notes-twenty-eight/">http://iiiyu.com/2013/09/11/learning-ios-notes-twenty-eight/</a><br/>
<a href="https://speakerdeck.com/andrewsardone/reactivecocoa-at-mobidevday-2013">https://speakerdeck.com/andrewsardone/reactivecocoa-at-mobidevday-2013</a><br/>
<a href="http://msdn.microsoft.com/en-us/library/hh848246.aspx">http://msdn.microsoft.com/en-us/library/hh848246.aspx</a><br/>
<a href="http://www.itiger.me/?p=38">http://www.itiger.me/?p=38</a><br/>
<a href="http://blog.leezhong.com/ios/2013/12/27/reactivecocoa-2.html">http://blog.leezhong.com/ios/2013/12/27/reactivecocoa-2.html</a><br/>
<a href="https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/Documentation/FrameworkOverview.md">https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/Documentation/FrameworkOverview.md</a><br/>
<a href="http://www.haskell.org/haskellwiki/Functional_Reactive_Programming">http://www.haskell.org/haskellwiki/Functional_Reactive_Programming</a><br/>
<a href="http://blog.zhaojie.me/2009/09/functional-reactive-programming-for-csharp.html">http://blog.zhaojie.me/2009/09/functional-reactive-programming-for-csharp.html</a></p>
<h3>代码</h3>
<p><a href="https://github.com/Machx/MVVM-IOS-Example">https://github.com/Machx/MVVM-IOS-Example</a>
<a href="https://github.com/ReactiveCocoa/RACiOSDemo">https://github.com/ReactiveCocoa/RACiOSDemo</a></p>
<h3>书籍</h3>
<p><a href="https://leanpub.com/iosfrp">https://leanpub.com/iosfrp</a></p>
<h3>视频</h3>
<p><a href="http://vimeo.com/65637501">http://vimeo.com/65637501</a></p>
项目第九天 避免滥用单例2015-07-14T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/14/gpxj-9-day<h2>来源</h2>
<hr />
<p>原文: <a href="http://www.objc.io/issue-13/singletons.html">Avoiding Singleton Abuse</a></p>
<p>译文:<a href="http://objccn.io/issue-13-2/">避免滥用单例</a></p>
<p>单例是整个 Cocoa 中被广泛使用的核心设计模式之一。事实上,苹果开发者库把单例作为 "Cocoa 核心竞争力" 之一。作为一个iOS开发者,我们经常和单例打交道,比如 UIApplication 和 NSFileManager 等等。我们在开源项目、苹果示例代码和 StackOverflow 中见过了无数使用单例的例子。Xcode 甚至有一个默认的 "Dispatch Once" 代码片段,可以使我们非常简单地在代码中添加一个单例:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">+ (instancetype)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}</code></pre></figure></p>
<p>由于这些原因,单例在 iOS 开发中随处可见。问题是,它们很容易被滥用。</p>
<p>尽管有些人认为单例是 '反模式', '魔鬼' 以及 '病态的说谎者',我不会去完全否认单例所带来的的好处,而是会展示一些使用单例所带来的问题,这样下一次在使用 dispatch_once 代码片段的自动补全功能时,你可以对它的影响进行评估,三思而行。</p>
<h2>全局状态</h2>
<hr />
<p>大多数的开发者都认同使用全局可变的状态是不好的行为。太多状态使得程序难以理解,难以调试。我们这些面向对象的程序员在最小化代码的状态复杂程度的方面,有很多需要向函数式编程学习的地方。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@implementation SPMath {
NSUInteger <em>a;
NSUInteger </em>b;
}</p>
<ul>
<li>(NSUInteger)computeSum
{
return <em>a + </em>b;
}</code></pre></figure></li>
</ul>
<p>在上面这个简单的数学库的实现中,程序员需要在调用 computeSum 前正确的设置实例变量 <em>a 和 </em>b。这样有以下问题:</p>
<ol>
<li><p>computeSum 没有显式地通过使用参数的形式声明它依赖于 <em>a 和 </em>b 的状态。与仅仅通过查看函数声明就可以知道这个函数的输出依赖于哪些变量不同的是,另一个开发者必须查看这个函数的具体实现才能明白这个函数依赖那些变量。隐藏依赖是不好的。</p></li>
<li><p>当为调用 computeSum 做准备而修改 <em>a 和 </em>b 的数值时,程序员需要保证这些修改不会影响任何其他依赖于这两个变量的代码的正确性。而这在多线程的环境中是尤其困难的。</p></li>
</ol>
<p>把下面的代码和上面的例子做对比:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">+ (NSUInteger)computeSumOf:(NSUInteger)a plus:(NSUInteger)b
{
return a + b;
}</code></pre></figure></p>
<p>这里,对变量 a 和 b 的依赖被显式地声明了。我们不需要为了调用这个方法而去改变实例变量的状态。并且我们也不需要担心调用这个函数会留下持久的副作用。我们甚至可以把这个方法声明为类方法,这样就告诉了代码的阅读者这个方法不会修改任何实例的状态。</p>
<p>那么,这个例子和单例又有什么关系呢?用 Miško Hevery 的话来说,"单例就是披着羊皮的全局状态"。一个单例可以被使用在任何地方,而不需要显式地声明依赖。就像变量 <em>a 和 </em>b 在 computeSum 内部被使用了,却没有被显式声明一样,程序的任意模块都可以调用 [SPMySingleton sharedInstance] 并且访问这个单例。这意味着任何和这个单例交互产生的副作用都会影响程序其他地方的任意代码。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@interface SPSingleton : NSObject</p>
<ul>
<li><p>(instancetype)sharedInstance;</p></li>
<li><p>(NSUInteger)badMutableState;</p></li>
<li>(void)setBadMutableState:(NSUInteger)badMutableState;</li>
</ul>
<p>@end</p>
<p>@implementation SPConsumerA</p>
<ul>
<li>(void)someMethod
{
if ([[SPSingleton sharedInstance] badMutableState]) {
// ...
}
}</li>
</ul>
<p>@end</p>
<p>@implementation SPConsumerB</p>
<ul>
<li>(void)someOtherMethod
{
[[SPSingleton sharedInstance] setBadMutableState:0];
}</li>
</ul>
<p>@end</code></pre></figure></p>
<p>在上面的例子中,SPConsumerA 和 SPConsumerB 是两个完全独立的模块。但是 SPConsumerB 可以通过使用单例提供的共享状态来影响 SPConsumerA 的行为。这种情况应该只能发生在 consumer B 显式引用了 A,并表明了两者之间的关系时。这里使用了单例,由于其具有全局和多状态的特性,导致隐式地在两个看起来完全不相关的模块之间建立了耦合。</p>
<p>让我们来看一个更具体的例子,并且暴露一个使用全局可变状态的额外问题。比如我们想要在我们的应用中构建一个网页查看器。为了支持这个查看器,我们构建了一个简单的 URL cache:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@interface SPURLCache</p>
<ul>
<li><p>(SPCache *)sharedURLCache;</p></li>
<li><p>(void)storeCachedResponse:(NSCachedURLResponse <em>)cachedResponse forRequest:(NSURLRequest </em>)request;</p></li>
</ul>
<p>@end </code></pre></figure></p>
<p>这个开发者开始写一些单元测试来保证代码在一些不同的情况下都能达到预期。首先,他写了一个测试用例来保证网页查看器在设备没有连接时能够展示出错误信息。然后他写了一个测试用例来保证网页查看器能够正确的处理服务器错误。最后,他为成功情况时写了一个测试用例,来保证返回的网络内容能够被正确的显示出来。这个开发者运行了所有的测试用例,并且它们都如预期一样正确。赞!</p>
<p>几个月以后,这些测试用例开始出现失败,尽管网页查看器的代码从它写完后就从来没有再改动过!到底发生了什么?</p>
<p>原来,有人改变了测试的顺序。处理成功的那个测试用例首先被运行,然后再运行其他两个。处理错误的那两个测试用例现在竟然成功了,和预期不一样,因为 URL cache 这个单例把不同测试用例之间的 response 缓存起来了。</p>
<p>持久化状态是单元测试的敌人,因为单元测试在各个测试用例相互独立的情况下才有效。如果状态从一个测试用例传递到了另外一个,这样就和测试用例的执行顺序就有关系了。有 bug 的测试用例,尤其是那些本来不应该通过的测试用例,是非常糟糕的事情。</p>
<h2>对象的生命周期</h2>
<hr />
<p>另外一个关键问题就是单例的生命周期。当你在程序中添加一个单例时,很容易会认为 “永远只会有一个实例”。但是在很多我看到过的 iOS 代码中,这种假定都可能被打破。</p>
<p>比如,假设我们正在构建一个应用,在这个应用里用户可以看到他们的好友列表。他们的每个朋友都有一张个人信息的图片,并且我们想使我们的应用能够下载并且在设备上缓存这些图片。 使用 dispatch_once 代码片段,我们可以写一个 SPThumbnailCache 单例:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@interface SPThumbnailCache : NSObject</p>
<ul>
<li><p>(instancetype)sharedThumbnailCache;</p></li>
<li><p>(void)cacheProfileImage:(NSData <em>)imageData forUserId:(NSString </em>)userId;</p></li>
<li>(NSData <em>)cachedProfileImageForUserId:(NSString </em>)userId;</li>
</ul>
<p>@end</code></pre></figure></p>
<p>我们继续构建我们的应用,一切看起来都很正常,直到有一天,我们决定去实现‘注销’功能,这样用户可以在应用中进行账号切换。突然我们发现我们将要面临一个讨厌的问题:用户相关的状态存储在全局单例中。当用户注销后,我们希望能够清理掉所有的硬盘上的持久化状态。否则,我们将会把这些被遗弃的数据残留在用户的设备上,浪费宝贵的硬盘空间。对于用户登出又登录了一个新的账号这种情况,我们也想能够对这个新用户使用一个全新的 SPThumbnailCache 实例。</p>
<p>问题在于按照定义单例被认为是“创建一次,永久有效”的实例。你可以想到一些对于上述问题的解决方案。或许我们可以在用户登出时移除这个单例:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">static SPThumbnailCache *sharedThumbnailCache;</p>
<ul>
<li><p>(instancetype)sharedThumbnailCache
{
if (!sharedThumbnailCache) {
sharedThumbnailCache = [[self alloc] init];
}
return sharedThumbnailCache;
}</p></li>
<li><p>(void)tearDown
{
// The SPThumbnailCache will clean up persistent states when deallocated
sharedThumbnailCache = nil;
}</code></pre></figure></p></li>
</ul>
<p>这是一个明显的对单例模式的滥用,但是它可以工作,对吧?</p>
<p>我们当然可以使用这种方式去解决,但是代价实在是太大了。我们不能使用简单的的 dispatch_once 方案了,而这个方案能够保证线程安全以及所有调用 [SPThumbnailCache sharedThumbnailCache] 的地方都能访问到同一个实例。现在我们需要对使用缩略图 cache 的代码的执行顺序非常小心。假设当用户正在执行登出操作时,有一些后台任务正在执行把图片保存到缓存中的操作:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[SPThumbnailCache sharedThumbnailCache] cacheProfileImage:newImage forUserId:userId];
});</code></pre></figure></p>
<p>我们需要保证在所有的后台任务完成前, tearDown 一定不能被执行。这确保了 newImage 数据可以被正确的清理掉。或者,我们需要保证在缩略图 cache 被移除时,后台缓存任务一定要被取消掉。否则,一个新的缩略图 cache 的实例将会被延迟创建,并且之前用户的数据 (newImage 对象) 会被存储在它里面。</p>
<p>由于对于单例实例来说它没有明确的所有者,(因为单例自己管理自己的生命周期),“关闭”一个单例变得非常的困难。</p>
<p>分析到这里,我希望你能够意识到,“这个缩略图 cache 从来就不应该作为一个单例!”。问题在于一个对象得生命周期可能在项目的最初阶段没有被很好得考虑清楚。举一个具体的例子,Dropbox 的 iOS 客户端曾经只支持一个账号登录。它以这样的状态存在了数年,直到有一天我们希望能够同时支持多个用户账号登录 (同时登陆私人账号和工作账号)。突然之间,我们以前的的假设“只能够同时有一个用户处于登录状态”就不成立了。如果假定了一个对象的生命周期和应用的生命周期一致,那你的代码的灵活扩展就受到了限制,早晚有一天当产品的需求产生变化时,你会为当初的这个假定付出代价的。</p>
<p>这里我们得到的教训是,单例应该只用来保存全局的状态,并且不能和任何作用域绑定。如果这些状态的作用域比一个完整的应用程序的生命周期要短,那么这个状态就不应该使用单例来管理。用一个单例来管理用户绑定的状态,是代码的坏味道,你应该认真的重新评估你的对象图的设计。</p>
<h2>避免使用单例</h2>
<hr />
<p>既然单例对局部作用域的状态有这么多的坏处,那么我们应该怎样避免使用它们呢?</p>
<p>让我们来重温一下上面的例子。既然我们的缩略图 cache 的缓存状态是和具体的用户绑定的,那么让我们来定义一个user对象吧:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@interface SPUser : NSObject</p>
<p>@property (nonatomic, readonly) SPThumbnailCache *thumbnailCache;</p>
<p>@end</p>
<p>@implementation SPUser</p>
<ul>
<li><p>(instancetype)init
{
if ((self = [super init])) {
_thumbnailCache = [[SPThumbnailCache alloc] init];</p>
<pre><code> // Initialize other user-specific state...
</code></pre>
<p> }
return self;
}</p></li>
</ul>
<p>@end</code></pre></figure></p>
<p>我们现在用一个对象来作为一个经过认证的用户会话的模型类,并且我们可以把所有和用户相关的状态存储在这个对象中。现在假设我们有一个view controller来展现好友列表:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@interface SPFriendListViewController : UIViewController</p>
<ul>
<li>(instancetype)initWithUser:(SPUser *)user;</li>
</ul>
<p>@end</code></pre></figure></p>
<p>我们可以显式地把经过认证的 user 对象作为参数传递给这个 view controller。这种把依赖性传递给依赖对象的技术正式的叫法是依赖注入,它有很多优点:</p>
<ol>
<li>对于阅读这个 SPFriendListViewController 头文件的读者来说,可以很清楚的知道它只有在有登录用户的情况下才会被展示。</li>
<li>这个 SPFriendListViewController 只要还在使用中,就可以强引用 user 对象。举例来说,对于前面的例子,我们可以像下面这样在后台任务中保存一个图片到缩略图 cache 中:</li>
</ol>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[_user.thumbnailCache cacheProfileImage:newImage forUserId:userId];
});</code></pre></figure></p>
<p>dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[_user.thumbnailCache cacheProfileImage:newImage forUserId:userId];
});</p>
<p>就算后台任务还没有完成,应用其他地方的代码也可以创建和使用一个全新的 SPUser 对象,而不会在清理第一个实例时阻塞用户交互。</p>
<p>为了更详细的说明一下第二点,让我们画一下在使用依赖注入之前和之后的对象图。</p>
<p>假设我们的 SPFriendListViewController 是当前 window 的 root view controller。使用单例时,我们的对象图看起来如下所示:</p>
<p><img src="/assets/img/ios/gpxj/9/1/1.png" alt="1.png" /></p>
<p>view controller 自己,以及自定义的 image view 的列表,都会和 sharedThumbnailCache 产生交互。当用户登出后,我们想要清理 root view controller 并且退出到登录页面:</p>
<p><img src="/assets/img/ios/gpxj/9/1/2.png" alt="2.png" /></p>
<p>这里的问题在于这个好友列表的 view controller 可能仍然在执行代码 (由于后台操作的原因),并且可能因此仍然有一些没有执行的涉及到 sharedThumbnailCache 的调用。</p>
<p>和使用依赖注入的解决方案对比一下:</p>
<p><img src="/assets/img/ios/gpxj/9/1/3.png" alt="3.png" /></p>
<p>简单起见,假设 SPApplicationDelegate 管理 SPUser 的实例 (在实践中,你可能会把这些用户状态的管理工作交给另外一个对象来做,这样可以使你的 application delegate 简化)。当展现好友列表 view controller 时,会传递进去一个 user 的引用。这个引用也会向下传递给 profile image views。现在,当用户登出时,我们的对象图如下所示:</p>
<p><img src="/assets/img/ios/gpxj/9/1/4.png" alt="4.png" /></p>
<p>这个对象图看起来和使用单例时很像。那么,区别是什么呢?</p>
<p>关键问题是作用域。在单例那种情况中,sharedThumbnailCache 仍然可以被程序的任意模块访问。假如用户快速的登录了一个新的账号。该用户也想看看他的好友列表,这也就意味着需要再一次的和缩略图 cache 产生交互:</p>
<p><img src="/assets/img/ios/gpxj/9/1/5.png" alt="5.png" /></p>
<p>当用户登录一个新账号,我们应该能够构建并且与全新的 SPThumbnailCache 交互,而不需要再在销毁老的缩略图 cache 上花费精力。基于对象管理的典型规则,老的 view controllers 和老的缩略图 cache 应该能够自己在后台延迟被清理掉。简而言之,我们应该隔离用户 A 相关联的状态和用户 B 相关联的状态:</p>
<p><img src="/assets/img/ios/gpxj/9/1/6.png" alt="6.png" /></p>
<h2>结论</h2>
<hr />
<p>希望这篇文章中的内容读起来不像奇幻小说那样难以理解。人们已经对单例的滥用抱怨了很多年了,并且我们也都知道全局状态是很不好的事情。但是在 iOS 开发的世界中,单例的使用是如此的普遍以至于我们有时候忘记了我们多年来在其他面向对象编程中学到的教训。</p>
<p>这一切的关键点是,在面向对象编程中我们想要最小化可变状态的作用域。但是单例却因为使可变的状态可以被程序中的任何地方访问,而站在了对立面。下一次你想使用单例时,我希望你能够好好考虑一下使用依赖注入作为替代方案。</p>
项目第八天 MVVM 介绍2015-07-13T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/13/gpxj-8-day<h2>来源</h2>
<hr />
<p>原文: <a href="http://www.objc.io/issue-13/mvvm.html">Introduction to MVVM</a></p>
<p>译文:<a href="http://objccn.io/issue-13-1/">MVVM 介绍</a></p>
<p>我于 2011 年在 500px 找到自己的第一份 iOS 开发工作。虽然我已经在大学里做了好几年 iOS 外包开发,但这才是我的一个真正的 iOS 开发工作。我被作为唯一的 iOS 开发者被招聘去实现拥有漂亮设计的 iPad 应用。在短短七周里,我们就发布了 1.0 并持续迭代,添加了更多特性,但从本质上,代码库也变得更加复杂了。</p>
<p>有时我感觉就像我不知道在做什么。虽然我知道自己的设计模式——就像任何好的编程人员那样 —— 但我太接近我在做的产品以至于不能客观地衡量我的架构决策的有效性。当队伍中来了另外一位开发者时,我意识到我们陷入困境了。</p>
<p>从没听过 MVC ?有人称之为 Massive View Controller(重量级视图控制器),这就是我们那时候的感觉。我不打算介绍令人汗颜的细节,但说实在的,如果我不得不再次重来一次,我绝对会做出不同的决策。</p>
<p>我会修改一个关键架构,并将其带入我从那时起就在开发的各种应用,即使用一种叫做 Model-View-ViewModel 的架构替换 Model-View-Controller。</p>
<p>所以,MVVM 到底是什么?与其专注于说明 MVVM 的来历,不如让我们看一个典型的 iOS 是如何构建的,并从那里了解 MVVM:</p>
<p><img src="/assets/img/ios/gpxj/8/1/1.png" alt="1.png" /></p>
<p>Typical Model-View-Controller setup</p>
<p>我们看到的是一个典型的 MVC 设置。Model 呈现数据,View 呈现用户界面,而 View Controller 调节它两者之间的交互。Cool!</p>
<p>稍微考虑一下,虽然 View 和 View Controller 是技术上不同的组件,但它们几乎总是手牵手在一起,成对的。你什么时候看到一个 View 能够与不同 View Controller 配对?或者反过来?所以,为什么不正规化它们的连接呢?</p>
<p><img src="/assets/img/ios/gpxj/8/1/2.png" alt="2.png" /></p>
<p>Intermediate</p>
<p>这更准确地描述了你可能已经编写的 MVC 代码。但它并没有做太多事情来解决 iOS 应用中日益增长的重量级视图控制器的问题。在典型的 MVC 应用里,许多逻辑被放在 View Controller 里。它们中的一些确实属于 View Controller,但更多的是所谓的“表示逻辑(presentation logic)”,以 MVVM 属术语来说,就是那些将 Model 数据转换为 View 可以呈现的东西的事情,例如将一个 NSDate 转换为一个格式化过的 NSString。</p>
<p>我们的图解里缺少某些东西,那些使我们可以把所有表示逻辑放进去的东西。我们打算将其称为 “View Model” —— 它位于 View/Controller 与 Model 之间:</p>
<p><img src="/assets/img/ios/gpxj/8/1/3.png" alt="3.png" /></p>
<p>Model-View-ViewModel</p>
<p>看起好多了!这个图解准确地描述了什么是 MVVM:一个 MVC 的增强版,我们正式连接了视图和控制器,并将表示逻辑从 Controller 移出放到一个新的对象里,即 View Model。MVVM 听起来很复杂,但它本质上就是一个精心优化的 MVC 架构,而 MVC 你早已熟悉。</p>
<p>现在我们知道了什么是 MVVM,但为什么我们会想要去使用它呢?在 iOS 上使用 MVVM 的动机,对我来说,无论如何,就是它能减少 View Controller 的复杂性并使得表示逻辑更易于测试。通过一些例子,我们将看到它如何达到这些目标。</p>
<p>此处有三个重点是我希望你看完本文能带走的:</p>
<ul>
<li>MVVM 可以兼容你当下使用的 MVC 架构。</li>
<li>MVVM 增加你的应用的可测试性。</li>
<li>MVVM 配合一个绑定机制效果最好。</li>
</ul>
<p>如我们之前所见,MVVM 基本上就是 MVC 的改进版,所以很容易就能看到它如何被整合到现有使用典型 MVC 架构的应用中。让我们看一个简单的 Person Model 以及相应的 View Controller:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@interface Person : NSObject</p>
<ul>
<li>(instancetype)initwithSalutation:(NSString <em>)salutation firstName:(NSString </em>)firstName lastName:(NSString <em>)lastName birthdate:(NSDate </em>)birthdate;</li>
</ul>
<p>@property (nonatomic, readonly) NSString <em>salutation;
@property (nonatomic, readonly) NSString </em>firstName;
@property (nonatomic, readonly) NSString <em>lastName;
@property (nonatomic, readonly) NSDate </em>birthdate;</p>
<p>@end</code></pre></figure></p>
<p>Cool!现在我们假设我们有一个 PersonViewController ,在 viewDidLoad 里,只需要基于它的 model 属性设置一些 Label 即可。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)viewDidLoad {
[super viewDidLoad];</p>
<pre><code>if (self.model.salutation.length &gt; 0) {
self.nameLabel.text = [NSString stringWithFormat:@"%@ %@ %@", self.model.salutation, self.model.firstName, self.model.lastName];
} else {
self.nameLabel.text = [NSString stringWithFormat:@"%@ %@", self.model.firstName, self.model.lastName];
}
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"EEEE MMMM d, yyyy"];
self.birthdateLabel.text = [dateFormatter stringFromDate:model.birthdate];
</code></pre>
<p>}</code></pre></figure></p>
<p>这全都直截了当,标准的 MVC。现在来看看我们如何用一个 View Model 来增强它。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@interface PersonViewModel : NSObject</p>
<ul>
<li>(instancetype)initWithPerson:(Person *)person;</li>
</ul>
<p>@property (nonatomic, readonly) Person *person;</p>
<p>@property (nonatomic, readonly) NSString <em>nameText;
@property (nonatomic, readonly) NSString </em>birthdateText;</p>
<p>@end</code></pre></figure></p>
<p>我们的 View Model 的实现大概如下:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@implementation PersonViewModel</p>
<ul>
<li><p>(instancetype)initWithPerson:(Person *)person {
self = [super init];
if (!self) return nil;</p>
<p> <em>person = person;
if (person.salutation.length > 0) {
</em>nameText = [NSString stringWithFormat:@"%@ %@ %@", self.person.salutation, self.person.firstName, self.person.lastName];
} else {
_nameText = [NSString stringWithFormat:@"%@ %@", self.person.firstName, self.person.lastName];
}</p>
<p> NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"EEEE MMMM d, yyyy"];
_birthdateText = [dateFormatter stringFromDate:person.birthdate];</p>
<p> return self;
}</p></li>
</ul>
<p>@end</code></pre></figure></p>
<p>Cool!我们已经将 viewDidLoad 中的表示逻辑放入我们的 View Model 里了。此时,我们新的 viewDidLoad 就会非常轻量:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)viewDidLoad {
[super viewDidLoad];</p>
<pre><code>self.nameLabel.text = self.viewModel.nameText;
self.birthdateLabel.text = self.viewModel.birthdateText;
</code></pre>
<p>}</code></pre></figure></p>
<p>所以,如你所见,并没有对我们的 MVC 架构做太多改变。还是同样的代码,只不过移动了位置。它与 MVC 兼容,带来更轻量的 View Controllers。</p>
<p>可测试,嗯?是怎样?好吧,View Controller 是出了名的难以测试,因为它们做了太多事情。在 MVVM 里,我们试着尽可能多的将代码移入 View Model 里。测试 View Controller 就变得容易多了,因为它们不再做一大堆事情,并且 View Model 也非常易于测试。让我们来看看:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">SpecBegin(Person)
NSString <em>salutation = @"Dr.";
NSString </em>firstName = @"first";
NSString <em>lastName = @"last";
NSDate </em>birthdate = [NSDate dateWithTimeIntervalSince1970:0];</p>
<pre><code>it (@"should use the salutation available. ", ^{
Person *person = [[Person alloc] initWithSalutation:salutation firstName:firstName lastName:lastName birthdate:birthdate];
PersonViewModel *viewModel = [[PersonViewModel alloc] initWithPerson:person];
expect(viewModel.nameText).to.equal(@"Dr. first last");
});
</code></pre>
<p>it (@"should not use an unavailable salutation. ", ^{
Person <em>person = [[Person alloc] initWithSalutation:nil firstName:firstName lastName:lastName birthdate:birthdate];
PersonViewModel </em>viewModel = [[PersonViewModel alloc] initWithPerson:person];
expect(viewModel.nameText).to.equal(@"first last");
});</p>
<p>it (@"should use the correct date format. ", ^{
Person <em>person = [[Person alloc] initWithSalutation:nil firstName:firstName lastName:lastName birthdate:birthdate];
PersonViewModel </em>viewModel = [[PersonViewModel alloc] initWithPerson:person];
expect(viewModel.birthdateText).to.equal(@"Thursday January 1, 1970");
});
SpecEnd</code></pre></figure></p>
<p>如果我们没有将这个逻辑移入 View Model,我们将不得不实例化一个完整的 View Controller 以及伴随的 View,然后去比较我们 View 中 Lable 的值。这样做不只是会变成一个麻烦的间接层,而且它只代表了一个十分脆弱的测试。现在,我们可以按意愿自由地修改视图层级而不必担心破坏我们的单元测试。使用 MVVM 带来的对于测试的好处非常清晰,甚至从这个简单的例子来看也可见一斑,而在有更复杂的表示逻辑的情况下,这个好处会更加明显。</p>
<p>注意到在这个简单的例子中, Model 是不可变的,所以我们可以只在初始化的时候指定我们 View Model 的属性。对于可变 Model,我们还需要使用一些绑定机制,这样 View Model 就能在背后的 Model 改变时更新自身的属性。此外,一旦 View Model 上的 Model 发生改变,那 View 的属性也需要更新。Model 的改变应该级联向下通过 View Model 进入 View。</p>
<p>在 OS X 上,我们可以使用 Cocoa 绑定,但在 iOS 上我们并没有这样好的配置可用。我们想到了 KVO(Key-Value Observation),而且它确实做了很伟大的工作。然而,对于一个简单的绑定都需要很大的样板代码,更不用说有许多属性需要绑定了。作为替代,我个人喜欢使用 ReactiveCocoa,但 MVVM 并未强制我们使用 ReactiveCocoa。MVVM 是一个伟大的典范,它自身独立,只是在有一个良好的绑定框架时做得更好。</p>
<p>我们覆盖了不少内容:从普通的 MVC 派生出 MVVM,看它们是如何相兼容的范式,从一个可测试的例子观察 MVVM,并看到 MVVM 在有一个配对的绑定机制时工作得更好。如果你有兴趣学习更多关于 MVVM 的知识,你可以看看这篇博客,它用更多细节解释了 MVVM 的好处,或者这一篇关于我们如何在最近的项目里使用 MVVM 获得巨大的成功的文章。我同样还有一个经过完整测试,基于 MVVM 的应用,叫做 C-41 ,它是开源的。去看看吧,如果你有任何疑问,请告诉我。</p>
项目第七天 git ssh key2015-07-12T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/12/gpxj-7-day<h2>git key</h2>
<hr />
<p>生成 git ssh 密钥</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>ssh-keygen -t rsa -C <span class="s1">'xxxxxxx@gmail.com'</span></code></pre></figure></p>
<p>一路回车</p>
项目第六天 iOS 常量2015-07-11T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/11/gpxj-6-day<h2>Constants in Objective-C</h2>
<hr />
<p>原文: <a href="http://stackoverflow.com/questions/538996/constants-in-objective-c">http://stackoverflow.com/questions/538996/constants-in-objective-c</a></p>
<p>You should create a header file like</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">// Constants.h
FOUNDATION_EXPORT NSString <em>const MyFirstConstant;
FOUNDATION_EXPORT NSString </em>const MySecondConstant;
//etc.</code></pre></figure></p>
<blockquote><p>(you can use <strong>extern</strong> instead of <strong>FOUNDATION_EXPORT</strong> if your code will not be used in mixed C/C++ environments or on other platforms)</p></blockquote>
<p>You can include this file in each file that uses the constants or in the pre-compiled header for the project.</p>
<p>You define these constants in a .m file like</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">// Constants.m
NSString <em>const MyFirstConstant = @"FirstConstant";
NSString </em>const MySecondConstant = @"SecondConstant";</code></pre></figure></p>
<p>Constants.m should be added to your application/framework's target so that it is linked in to the final product.</p>
<p>The advantage of using string constants instead of #define'd constants is that you can test for equality using pointer comparison (stringInstance == MyFirstConstant) which is much faster than string comparison ([stringInstance isEqualToString:MyFirstConstant]) (and easier to read, IMO).</p>
项目第五天 iOS 绘图2015-07-10T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/10/gpxj-5-day<h2>开源库 ios-charts</h2>
<hr />
<p><a href="https://github.com/danielgindi/ios-charts">https://github.com/danielgindi/ios-charts</a>
<a href="https://github.com/PhilJay/MPAndroidChart">https://github.com/PhilJay/MPAndroidChart</a></p>
<h2>使用</h2>
<hr />
<p>iOS 8 以上可直接用pods安装。</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>pod search charts</p>
<p><span class="gp">-> </span>CSChartsView <span class="o">(</span>0.1.0<span class="o">)</span>
A light weight line graph drawing framework
pod <span class="s1">'CSChartsView'</span>, <span class="s1">'~> 0.1.0'</span>
- Homepage: https://github.com/sk344208651/CSChartsView
- Source: https://github.com/sk344208651/CSChartsView.git
- Versions: 0.1.0 <span class="o">[</span>master repo]</p>
<p><span class="gp">-> </span>Charts <span class="o">(</span>2.1.0<span class="o">)</span>
ios-charts is a powerful & easy to use chart library <span class="k">for </span>iOS
pod <span class="s1">'Charts'</span>, <span class="s1">'~> 2.1.0'</span>
- Homepage: https://github.com/danielgindi/ios-charts
- Source: https://github.com/danielgindi/ios-charts.git
- Versions: 2.1.0, 2.0.9 <span class="o">[</span>master repo]</code></pre></figure></p>
<p>iOS 7 拖动所有<strong>.swift</strong>到xcode工程中。</p>
<p>If you want to compile for iOS 7:</p>
<ol>
<li>Drag the code itself (.swift files) to your project. As sadly, Swift currently does not support compiling Frameworks for iOS 7.</li>
<li>Make sure that the files are added to the Target membership.</li>
<li>When using Swift in an ObjC project:
<ol type="a">
<li>You need to import your Bridging Header. Usually it is "YourProject-Swift.h", so in ChartsDemo it's "ChartsDemo-Swift.h". Do not try to actually include "ChartsDemo-Swift.h" in your project :-)</li>
<li>Under "Build Options", mark "Embedded Content Contains Swift Code"</li>
</ol>
</li>
</ol>
<h2>Features 特征</h2>
<hr />
<h3>Core features:</h3>
<p>8 different chart types</p>
<ul>
<li>Scaling on both axes (with touch-gesture, axes separately or pinch-zoom)</li>
<li>Dragging / Panning (with touch-gesture)</li>
<li>Combined-Charts (line-, bar-, scatter-, candle-stick-, bubble-)</li>
<li>Dual (separate) Y-Axis</li>
<li>Finger drawing (draw values into the chart with touch-gesture)</li>
<li>Highlighting values (with customizeable popup-views)</li>
<li>Multiple / Separate Axes</li>
<li>Save chart to camera-roll / export to PNG/JPEG</li>
<li>Predefined color templates</li>
<li>Legends (generated automatically, customizeable)</li>
<li>Customizeable Axes (both x- and y-axis)</li>
<li>Animations (build up animations, on both x- and y-axis)</li>
<li>Limit lines (providing additional information, maximums, ...)</li>
<li>Fully customizeable (paints, typefaces, legends, colors, background, gestures, dashed lines, ...)</li>
</ul>
<h3>Screenshots</h3>
<hr />
<p>creenshots are currently taken from the original repository, as they render exactly the same :-)</p>
<ul>
<li>LineChart (with legend, simple design)</li>
</ul>
<p><img src="/assets/img/ios/gpxj/5/1/1.png" alt="1.png" /></p>
<ul>
<li>LineChart (with legend, simple design)</li>
</ul>
<p><img src="/assets/img/ios/gpxj/5/1/2.png" alt="2.png" /></p>
<ul>
<li>LineChart (cubic lines)</li>
</ul>
<p><img src="/assets/img/ios/gpxj/5/1/3.png" alt="3.png" /></p>
<ul>
<li>LineChart (single DataSet)</li>
</ul>
<p><img src="/assets/img/ios/gpxj/5/1/4.png" alt="4.png" /></p>
<ul>
<li>Combined-Chart (bar- and linechart in this case)</li>
</ul>
<p><img src="/assets/img/ios/gpxj/5/1/5.png" alt="5.png" /></p>
<ul>
<li>BarChart (with legend, simple design)</li>
</ul>
<p><img src="/assets/img/ios/gpxj/5/1/6.png" alt="6.png" /></p>
<ul>
<li>BarChart (grouped DataSets)</li>
</ul>
<p><img src="/assets/img/ios/gpxj/5/1/7.png" alt="7.png" /></p>
<ul>
<li>Horizontal-BarChart</li>
</ul>
<p><img src="/assets/img/ios/gpxj/5/1/8.png" alt="8.png" /></p>
<ul>
<li>PieChart (with selection, ...)</li>
</ul>
<p><img src="/assets/img/ios/gpxj/5/1/9.png" alt="9.png" /></p>
<ul>
<li>ScatterChart (with squares, triangles, circles, ... and more)</li>
</ul>
<p><img src="/assets/img/ios/gpxj/5/1/10.png" alt="10.png" /></p>
<ul>
<li>CandleStickChart (for financial data)</li>
</ul>
<p><img src="/assets/img/ios/gpxj/5/1/11.png" alt="11.png" /></p>
<ul>
<li>BubbleChart (area covered by bubbles indicates the value)</li>
</ul>
<p><img src="/assets/img/ios/gpxj/5/1/12.png" alt="12.png" /></p>
<ul>
<li>RadarChart (spider web chart)</li>
</ul>
<p><img src="/assets/img/ios/gpxj/5/1/13.png" alt="13.png" /></p>
<h2>Documentation</h2>
<hr />
<p>Currently there's no need for documentation for the iOS version, as the API is 95% the same as on Android.</p>
<p>You can read the official <a href="https://github.com/PhilJay/MPAndroidChart">MPAndroidChart</a> documentation here: <a href="https://github.com/PhilJay/MPAndroidChart/wiki">Wiki</a></p>
<p>Or you can see the <a href="https://github.com/danielgindi/ios-charts/tree/master/ChartsDemo">ChartsDemo</a> project and learn the how-tos from it.</p>
项目第四天 java转Objective-C工具 J2ObjC2015-07-09T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/09/gpxj-4-day<h2>开源库 J2ObjC</h2>
<hr />
<p><a href="https://github.com/google/j2objc">https://github.com/google/j2objc</a></p>
<h2>使用</h2>
<hr />
<p>Getting Started</p>
<p>First, either:</p>
<p>Download the current distribution from the Releases section and unzip it, or
Get the source and build it.
To translate a Java source file (Hello.java, for example):</p>
<p><figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Hello</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="n">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</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="s">"hello, world"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure></p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>j2objc Hello.java
translating Hello.java
Translated 1 file: 0 errors, 0 warnings</code></pre></figure></p>
<p>To compile the translated file:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>j2objcc -c Hello.m</code></pre></figure></p>
<p>j2objcc is a wrapper script that invokes your C compiler (normally clang, aka LLVM, Apple's C/C++/Objective-C compiler). To build the executable:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>j2objcc -o hello Hello.o
<span class="gp">$ </span>./hello Hello
hello, world</code></pre></figure></p>
<p>j2objcc forwards whatever options you specify for the Objective-C compiler. For example, to translate and build with debugging symbols, use the -g flag:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>j2objcc -g -o hello Hello.m</code></pre></figure></p>
项目第四天 UINavigationController导航栏的隐藏和显示2015-07-09T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/09/gpxj-4-1-day<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)viewWillAppear:(BOOL)animated {
[super.navigationController setNavigationBarHidden:YES animated:TRUE];</p>
<p>}</p>
<ul>
<li>(void)viewWillDisappear:(BOOL)animated {
[super.navigationController setNavigationBarHidden:NO animated:TRUE];</li>
</ul>
<p>}</code></pre></figure></p>
项目第三天 左侧滑动抽屉菜单2015-07-08T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/08/gpxj-3-day<h2>开源库 REFrostedViewController</h2>
<hr />
<p><a href="https://github.com/romaonthego/REFrostedViewController">https://github.com/romaonthego/REFrostedViewController</a></p>
<h2>Storyboards Example</h2>
<ol>
<li>Create a subclass of REFrostedViewController. In this example we call it DEMORootViewController.</li>
<li>In the Storyboard designate the root view's owner as DEMORootViewController.</li>
<li>Make sure to #import "REFrostedViewController.h" in DEMORootViewController.h.</li>
<li>Add more view controllers to your Storyboard, and give them identifiers "menuController" and "contentController". Note that in the new XCode the identifier is called "Storyboard ID" and can be found in the Identity inspector.</li>
<li>Add a method awakeFromNib to DEMORootViewController.m with the following code:</li>
</ol>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)awakeFromNib
{
self.contentViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"contentController"];
self.menuViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"menuController"];
}</code></pre></figure></p>
项目第三天 抽屉菜单2015-07-08T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/08/gpxj-3-2-day<h2>开源库 RESideMenu</h2>
<hr />
<p><a href="https://github.com/romaonthego/RESideMenu">https://github.com/romaonthego/RESideMenu</a></p>
<h2>使用</h2>
<hr />
<h3>Code</h3>
<p>In your AppDelegate's - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions create the view controller and assign content and menu view controllers.</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">// Create content and menu controllers
//
DEMONavigationController <em>navigationController = [[DEMONavigationController alloc] initWithRootViewController:[[DEMOHomeViewController alloc] init]];
DEMOLeftMenuViewController </em>leftMenuViewController = [[DEMOLeftMenuViewController alloc] init];
DEMORightMenuViewController *rightMenuViewController = [[DEMORightMenuViewController alloc] init];</p>
<p>// Create side menu controller
//
RESideMenu *sideMenuViewController = [[RESideMenu alloc] initWithContentViewController:navigationController
leftMenuViewController:leftMenuViewController
rightMenuViewController:rightMenuViewController];
sideMenuViewController.backgroundImage = [UIImage imageNamed:@"Stars"];</p>
<p>// Make it a root controller
//
self.window.rootViewController = sideMenuViewController;</code></pre></figure></p>
<p>Present the menu view controller:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">[self.sideMenuViewController presentLeftMenuViewController];</code></pre></figure></p>
<p>or</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">[self.sideMenuViewController presentRightMenuViewController];</code></pre></figure></p>
<p>Switch content view controllers:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import <RESideMenu/RESideMenu.h></p>
<p>....</p>
<p>[self.sideMenuViewController setContentViewController:viewController animated:YES];
[self.sideMenuViewController hideMenuViewController];</code></pre></figure></p>
<h3>Storyboards Example</h3>
<p>Create a subclass of RESideMenu. In this example we call it DEMORootViewController.
In the Storyboard designate the root view's owner as DEMORootViewController.
Make sure to #import "RESideMenu.h" in DEMORootViewController.h.
Add more view controllers to your Storyboard, and give them identifiers "leftMenuViewController", "rightMenuViewController" and "contentViewController". Note that in the new XCode the identifier is called "Storyboard ID" and can be found in the Identity inspector.
Add a method awakeFromNib to DEMORootViewController.m with the following code:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)awakeFromNib
{
self.contentViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"contentViewController"];
self.leftMenuViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"leftMenuViewController"];
self.rightMenuViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"rightMenuViewController"];</p>
<p>}</code></pre></figure></p>
项目第三天 解决在 iOS 8 中用Storyboard modal UINavigationController不能dismiss的问题2015-07-08T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/08/gpxj-3-1-day<h2>原文:</h2>
<hr />
<p><a href="http://www.cocoachina.com/bbs/read.php?tid=230841">http://www.cocoachina.com/bbs/read.php?tid=230841</a></p>
<p>自己自定义一个Dismiss的UIStoryboardSegue类</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import "Dismiss.h"</p>
<p>@implementation Dismiss
- (void)perform
{
UIViewController *sourceViewController = self.sourceViewController;
[sourceViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
@end</code></pre></figure></p>
<p>然后,链接的时候直接选择dismiss</p>
<p><img src="/assets/img/ios/gpxj/3/1/1.png" alt="1.png" /></p>
项目第二天 国际化2015-07-07T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/07/gpxj-2-day<h2>文本国际化</h2>
<hr />
<h3>一、获取系统所支持的国际化信息</h3>
<hr />
<p>在国际化之前,你可以在iphone中的<strong>设置->通用->多语言环境->语言</strong>中来查看你的iphone支持哪些语言,当然也可以写一段代码测试一下你的iphone都支持哪些语言.测试代码如下:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">NSUserDefaults <em>defaults = [NSUserDefaults standardUserDefaults];
NSArray </em>languages = [defaults objectForKey:@"AppleLanguages"];
NSLog(@"%@", languages);</code></pre></figure></p>
<blockquote><p>注:NSUserDefaults类用来取得用户人默认信息.</p></blockquote>
<h3>二、在Xcode中建立多语言文档</h3>
<hr />
<p>打开 File>New>File,选择Resource中Strings Fils,如图:</p>
<p><img src="/assets/img/ios/gpxj/2/1/1.png" alt="1.png" /></p>
<blockquote><p> 注意:Localizable.strings是iOS用来本地化文本默认的文件名称。请抑制以其他内容给它命名的冲动,否则以后你每次引用本地化字符串的时候要一次次输入.strings 文件名。</p></blockquote>
<p>现在,你已经创建了Localizable.strings文件,你需要添加所有的文本--当前硬编码在app中的文本。你需要遵从一个特定但简单的格式:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">"KEY" = "CONTENT"; </code></pre></figure></p>
<h3>三、在源代码中使用NSLocalizedString来引用国际化文件</h3>
<hr />
<ul>
<li>括号里第一个参数是要显示的内容,与各Localizable.strings中的id对应</li>
<li>第二个是对第一个参数的注释,一般可以为空串</li>
</ul>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">NSString *str = NSLocalizedString(@"KEY",@"CONTENT");</code></pre></figure></p>
<h3>四、使用Terminal的genstrings命令进行生成资源文件</h3>
<hr />
<p>打开Terminal,然后cd到工程所在的目录,然后使用genstrings来生成自动从源代码中生成资源文件.</p>
<p>例如,项目的目录为:/user/project/test01,则命令如下:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">genstrings -o English.lproj ./classes/<span class="k"><em></span>.m genstrings -o zh.lproj ./classes/<span class="k"></em></span>.m</code></pre></figure></p>
<h3>五、编辑各Localizable.strings文件</h3>
<hr />
<p>从第四步中得到了与代码对应的资源文件,最后我们需要对这些资源文件翻译成对应的语言就可以了.如在Localizable.strings(zh)中,把等号后的文字进行编译成中文.</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">"KEY" = "CONTENT"</code></pre></figure></p>
<h2>不同语言显示不同名称</h2>
<hr />
<h3>一、 创建InfoPlist.strings文件,并本地化它</h3>
<hr />
<p>在项目目录下新建一个<strong>InfoPlist.strings</strong>文件,同样按着以上的步骤进行本地化处理。保存。</p>
<h3>二、编辑InfoPlist.strings文件内容</h3>
<hr />
<p>English下面,添加一行:CFBundleDisplayName = "英文名称"</p>
<p>zh-CN对应:CFBundleDisplayName = "中文名称"</p>
<blockquote><p>Error: Copy .strings file Error</p>
<p>Read failed: The data couldn't be read because it isn't in the correct format.</p>
<blockquote><p>没有正确格式化,可能是忘记以;结尾。</p></blockquote></blockquote>
<h3>三、 模拟器中显示</h3>
<hr />
<p><img src="/assets/img/ios/gpxj/2/1/2.png" alt="2.png" /></p>
<p>Cmd + R 运行</p>
<p>Cmd + Shift + Home 回去主界面</p>
<p>可以看到显示名称已经改为中文。</p>
<h2>界面国际化</h2>
<hr />
<h3>一、 项目中添加多语言</h3>
<hr />
<p><img src="/assets/img/ios/gpxj/2/1/3.png" alt="3.png" /></p>
<h3>二、选择要多语言的文件</h3>
项目第二天 结队开发之多storyboard2015-07-07T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/07/gpxj-2-4-day<h2>开源库</h2>
<hr />
<p><a href="https://github.com/rob-brown/RBStoryboardLink">https://github.com/rob-brown/RBStoryboardLink</a></p>
<h2>原文 <a href="http://www.ifun.cc/blog/2014/02/23/jie-dui-kai-fa-zhi-duo-storyboard/">http://www.ifun.cc/blog/2014/02/23/jie-dui-kai-fa-zhi-duo-storyboard/</a></h2>
<hr />
<p>Storyboard的出现,让开发变得像讲故事一样,UI间的关系流程也一目了然。它其实是xib的升级版本,将多个xib统一管理了。任何事都有双面性,Storyboard也有它的缺点。笔者就说说自己的经验,版本管理中,多人修改很容易严生冲突。storyboard中UIViewController太多,找到想要的比较困难(特别是在MBA上)。 这些缺点在结队开发中就会遇见。 如果我们用xib文件,这样将UI最小化分隔开,将后用code将这些小单元连接起来,就可以解决这个问题。如果只用xib就展现不出storyboard的故事情节能力。</p>
<p>本人提供两种解决方案:</p>
<ol>
<li>Xib link Xib</li>
<li>Storyboard link Storyboard</li>
</ol>
<p>其实它们之间可以两两组合,这样就可以延伸出另外两种方法:</p>
<ol>
<li>Xib link Storyboard</li>
<li>Storyboard link Xib</li>
</ol>
<h2>Storyboard link Storyboard</h2>
<p>这种方法与上面方法原理是一样的,利用加载storyboard时,实例化对应自定义UIViewController类,然后手动addSubview到对link view上,以达到链接的目录。 用言语表达是有点抽象,还是用代码表达吧,容易理解。</p>
<p>在这儿用到了Github上一个人写的link代码:RBStoryboardLink。它的用法也在README中说得很清楚了。</p>
<p>大家将源码下载下来,里面有一个sample:LinkedTabs. 由于原作者用到了cocoapods,所以需要到源码LinkedTabs目录下, 运行一个命令行:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">pod update</code></pre></figure></p>
<p>我还是简单说一下sample的结构。</p>
<p>里面有三个storyboard,分别是MainStoryboard,FirstTabStoryboard, SecondTabStoryboard, 现在需要将MainStoryboard中的两个UIViewController分别与FirstTabStoryboard,SecondTabStoryboard建立连接,以达到程序运行后,这三个storyboard像是在一起的目的。</p>
<p>打开MainStoryboard,定位到Tab1的UIViewController</p>
<p><img src="/assets/img/ios/gpxj/2/4/1.jpg" alt="1.jpg" /></p>
<blockquote><p>注意: 图中两个箭头,上面一个一定要填这个自定义类 <a href="https://github.com/rob-brown/RBStoryboardLink">RBStoryboardLink</a>, 就是个类完成了连接的任务。</p></blockquote>
<p>下面一个箭头处,是传递的属性与对应的值,在实例化RBStoryboardLink的时候传送递给实例化对象。storyboardName这个属性表示要link到哪儿去,这儿我们要link到FirstTabStoryboard,所以值为FirstTabStoryboard。还有一个可选属性sceneIdentifier, 表示要link到Storyboard中的哪一个UIViewController. 如果不传这个属性,那就link到FirstTabStoryboard中的initial View Controller.</p>
<p>如果我们想link到FirstTabStoryboard中的第二个UIViewController那如何办呢,很简单:</p>
<p><img src="/assets/img/ios/gpxj/2/4/2.jpg" alt="2.jpg" /></p>
<p>如上图,我们先找到这个被linke的View Controller,然后在Storyboard ID那输入该View Controller的标识,如在此输入:NavController.</p>
<p>接着在需要link的地方加入sceneIdentifier:NavController, 见下图箭头处。</p>
<p><img src="/assets/img/ios/gpxj/2/4/3.jpg" alt="3.jpg" /></p>
<p>这样就link成功了。</p>
<p>方法3与方法4只是组合出来的方法,也不在此多述了。</p>
项目第二天 自动更新2015-07-07T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/07/gpxj-2-3-day<h2>开源库</h2>
<hr />
<p><a href="https://github.com/nicklockwood/iVersion">https://github.com/nicklockwood/iVersion</a></p>
<h3>添加Configurations</h3>
<p><img src="/assets/img/ios/gpxj/2/3/1.png" alt="1.png" /></p>
<h3>添加 宏定义</h3>
<p><img src="/assets/img/ios/gpxj/2/3/2.png" alt="2.png" /></p>
<h3>编写自动更新代码</h3>
<p>编辑<strong>AppDelegate.m</strong></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#pragma mark - iVersion</p>
<h1>ifdef GITHUB</h1>
<ul>
<li><p>(void)initialize
{
//configure iVersion. These paths are optional - if you don't set
//them, iVersion will just get the release notes from iTunes directly (if your app is on the store)</p>
<p> [iVersion sharedInstance].remoteVersionsPlistURL = @"https://gpxj.github.io/versions.plist";
[iVersion sharedInstance].localVersionsPlistPath = @"versions.plist";
NSString *updateUrl = @"itms-services:///?action=download-manifest&url=https://gpxj.github.io/gpxj.plist";</p>
<p> [iVersion sharedInstance].updateURL = [NSURL URLWithString:updateUrl];
}</p>
<h1>endif</code></pre></figure></h1></li>
</ul>
<h3>添加 version.plist</h3>
<p><figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="cp"><?xml version="1.0" encoding="UTF-8"?></span>
<span class="cp"><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"></span>
<span class="nt"><plist</span> <span class="na">version=</span><span class="s">"1.0"</span><span class="nt">></span>
<span class="nt"><dict></span>
<span class="nt"><key></span>0.1.0<span class="nt"></key></span>
<span class="nt"><array></span>
<span class="nt"><string></span>First Version.<span class="nt"></string></span>
<span class="nt"></array></span>
<span class="nt"></dict></span>
<span class="nt"></plist></span></code></pre></figure></p>
项目第二天 iOS版本号2015-07-07T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/07/gpxj-2-2-day<h2>原文:<a href="http://segmentfault.com/a/1190000002423661">http://segmentfault.com/a/1190000002423661</a></h2>
<hr />
<p>FIR.im 一直在尽量兼容不同使用习惯的版本号形式, 但是在使用中我们发现好多开发者对怎么更好的用版本号来标示应用很陌生. 这是篇基础文章, 简单介绍 iOS 的版本号.</p>
<p><img src="/assets/img/ios/gpxj/2/2/1.png" alt="1.png" /></p>
<h2>名词解释</h2>
<hr />
<ul>
<li>Version, 通常说的版本号, 是应用向用户宣传说明时候用到的标识. 一般有2段或者3段式, 如:2.1,8.1.2</li>
</ul>
<p>Version 一般由产品部门确定, 完全迥异的更新需要改变主版本号, 比如 QQ 4.0 的变化非常大, 主版本的变化会更加吸引用户的眼球,所以有的应用会频繁的更新主版本号, 比如 FireFox 20.0 . 两段式的副版本号既包含小功能更新也会包含 bug 修复等,三段式副版本基本都是新功能添加和大问题修复,第三段则表示稳定版本基本都是修复 bug</p>
<ul>
<li>Build , 编译号指一次唯一编译标识, 通常是一个递增整数(安卓强制为数字, iOS 可以是字符串)</li>
</ul>
<p>Build 都是给内部使用, 用来确定一个唯一版本. 与前面提到的 Version 不会有太大联系.</p>
<p><strong>iOS 开发中,这个2个号码都可以任意字符串或数字.</strong></p>
<p>我们目前遇到的情况有:</p>
<ul>
<li>忽略了 Version 或 Build. 这两个号中的一个常年的不会发生变化.</li>
<li>颠倒了 Version 和 Build.</li>
</ul>
<p><img src="/assets/img/ios/gpxj/2/2/2.png" alt="2.png" /></p>
<p>获取方法也很简单:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">NSDictionary *info= [[NSBundle mainBundle] infoDictionary];</p>
<p>info[@"CFBundleShortVersionString"]; //Version
info[@"CFBundleVersion"]; // Build</code></pre></figure></p>
<h2>为什么使用版本号</h2>
<hr />
<h3>1. 方便标示和沟通</h3>
<p>前面提到 版本号更新会给推广产生一定的积极作用. 所以版本号不要太长, 如果像这样 "我们隆重推出了 某某某 1.7.14.19257 !", 这个会让用户感觉很乏味很像电视购物,而且也不利于传播. 如果是 "某某 3.0, 大有不同 !"可能就会产生更好的沟通效果.</p>
<h3>2. 方便追踪 Bug</h3>
<p>一个应用有 Bug 是肯定的, 但是很快的定位解决问题却体现出团队和程序员的能力. 我们经常遇到有开发者说我提交一个版本, 但是下载下来有还是旧的. 我们帮他解决问题的时候,他自己都搞不清哪个是哪个了, 如果能在"关于"之类的地方显示当前的版本, 就会容易找到问题.</p>
<p>或者是测试团队的同事, 可能手里同时有几个不同分支的版本在测试, 他们需要精确的描述一个测试版本.</p>
<h2>自动改变 Build 号</h2>
<hr />
<p>前面提到, Version 是不需要自动变化的, 根据产品或者市场部门的需求,适时的手动改一下就好.</p>
<h3>1. agvtool (Apple-generic versioning tool)</h3>
<p>agvtool, 是苹果的命令行工具, 也是集成在 Xcode 中最方便的工具. 我们在自动编译 SDK 的脚本中用的就是这个方法. 其实就用了一行(其他的高级用法可以参考前面的链接):</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">agvtool next-version</code></pre></figure></p>
<p>使用前需要在 Xcode 里简单配置一下, 如图:</p>
<p><img src="/assets/img/ios/gpxj/2/2/3.png" alt="3.png" /></p>
<h3>2. 基于SCM的版本控制号</h3>
<p>SCM 现在常用的有 Git 和 SVN, 还有一些相对小众的比如 hg 这里就不多做介绍了.
如果用 Git/SVN 来管理代码(相信已经没有人不用了) 我们可以用代码的提交次数来代替Build号.</p>
<ul>
<li>Git</li>
</ul>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">REV</span><span class="o">=</span><span class="sb"><code></span>git rev-list HEAD | wc -l | awk <span class="s1">'{print $1}'</span><span class="sb"></code></span></code></pre></figure></p>
<p>其中 HEAD是分支名, 代表当前分支, 可以直接替换成其他分支名, 比如master,dev.
这个脚本放到</p>
<ul>
<li>SVN</li>
</ul>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">REV</span><span class="o">=</span><span class="sb"><code></span>svnversion -nc | sed -e <span class="s1">'s/^[^:]*://;s/[A-Za-z]//'</span><span class="sb"></code></span></code></pre></figure></p>
<p>后面都是一样的:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">/usr/libexec/PlistBuddy -c <span class="s2">"Set :CFBundleVersion </span><span class="k">${</span><span class="nv">REV</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">TARGET_BUILD_DIR</span><span class="k">}</span><span class="s2">"</span>/<span class="k">${</span><span class="nv">INFOPLIST_PATH</span><span class="k">}</span></code></pre></figure></p>
<p>这样每次编译app的时候就自动把版本号加到Info.plist的CFBundleVersion键值下</p>
<p>把上面2行代码 加到 "Build Phase > Run Script"就可以了:</p>
<p><img src="/assets/img/ios/gpxj/2/2/4.png" alt="4.png" /></p>
<ol>
<li>基于日期时间</li>
</ol>
<p>用发布日期作为版本好也是许多应用常用的方式, 因为好记好理解. 这里直接附上代码:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">REV</span><span class="o">=</span><span class="sb"><code></span>date +%y%m%d<span class="sb"></code></span> <span class="c">#输出格式141120的六位日期格式,可以根据自己喜欢改变格式</span></code></pre></figure></p>
<p>后面都是一样的:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">/usr/libexec/PlistBuddy -c <span class="s2">"Set :CFBundleVersion </span><span class="k">${</span><span class="nv">REV</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">TARGET_BUILD_DIR</span><span class="k">}</span><span class="s2">"</span>/<span class="k">${</span><span class="nv">INFOPLIST_PATH</span><span class="k">}</span></code></pre></figure></p>
<p>使用方法同上.</p>
<h2>怎么使用</h2>
<p>只要配置好了版本号, 其他的事情就不需要人工干预了, 这里介绍2种使用场景.</p>
<ol>
<li>Crash 收集</li>
</ol>
<p>收集 Crash 是应用开发必要的环节, 通过分析和修复 Crash 信息可以大大提高应用的稳定性而不会让更多的用户失望甚至删除应用.
目前有很多收集工具, 比如 FIR.im 旗下的BugHD, Crashlytics等.</p>
<p><img src="/assets/img/ios/gpxj/2/2/5.png" alt="5.png" /></p>
<ol>
<li>用户反馈</li>
</ol>
<p>能主动反馈问题的用户都是极品用户, 不管要求是不是合理我们都要认真对待.
不管是用各种 SDK 还是用 Email 都要尽量的带上版本号, 系统信息, 方便确认用户需求.最不济也要在"关于"里面能让用户找到相关的版本信息以便描述问题.</p>
项目第二天 引导画面2015-07-07T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/07/gpxj-2-1-day<h2>开源库 OnBoard</h2>
<hr />
<p><a href="https://github.com/mamaral/Onboard">https://github.com/mamaral/Onboard</a></p>
<ul>
<li>创建新文件<strong>OnBoardViewController.{hm}</strong>。</li>
<li>根据官方Demo中的MyOnboardingViewController.{hm}完成<strong>OnBoardViewController</strong>。</li>
<li><p>将<strong>AppDelegate.m</strong>中的实现改为<strong>StoryBoard</strong>形式。</p></li>
<li><p>编辑 - (void)setupNormalRootViewControllerAnimated:(BOOL)animated;</p></li>
<li>将</li>
</ul>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">ViewController *mainVC = [UIViewController new];</code></pre></figure></p>
<p>改为</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">UIViewController *mainVC = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateInitialViewController];</code></pre></figure></p>
项目第一天 cocoapods2015-07-06T00:00:00+00:00http://jiaxianhua.github.io/gpxj/2015/07/06/gpxj-1-day<h2>CocoaPods - Official Site</h2>
<p><a href="https://cocoapods.org">https://cocoapods.org</a></p>
<h2>cocoapods install and update</h2>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>sudo gem update --system // 先更新gem,国内需要切换源
<span class="gp">$ </span>gem sources --remove https://rubygems.org/
<span class="gp">$ </span>gem sources -a http://ruby.taobao.org/
<span class="gp">$ </span>gem sources -l
<span class="se">***</span> CURRENT SOURCES <span class="se">***</span>
http://ruby.taobao.org/
<span class="gp">$ </span>sudo gem install cocoapods // 安装升级cocoapods
<span class="gp">$ </span>pod setup</p>
<p><span class="gp">$ </span>pod --version
0.37.2</code></pre></figure></p>
<h2>Error</h2>
<hr />
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>pod update</p>
<p><span class="o">[</span>!] Invalid <span class="sb"><code></span>Podfile<span class="sb"></code></span> file: syntax error, unexpected tIDENTIFIER, expecting end-of-input
pod <span class="s1">'iVersion'</span>, <span class="s1">'~> 1.11.4'</span>
^. Updating CocoaPods might fix the issue.</p>
<p> <span class="c"># from /Users/jiaxianhua/Code/Bitbucket/gpxj/gpxj/Podfile:12</span>
<span class="c"># -------------------------------------------</span>
<span class="c"># pod 'Onboard', '~> 2.1.1</span>
> pod <span class="s1">'iVersion'</span>, <span class="s1">'~> 1.11.4'</span>
> <span class="c"># -------------------------------------------</span></code></pre></figure></p>
<h2>Fix</h2>
<hr />
<ul>
<li><p>Podfile文件书写问题,上一行结尾没有加<strong>单引号</strong></p></li>
<li><p>这是编码的问题</p></li>
</ul>
<p>查看编码,终端中输入:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>locale
<span class="nv">LANG</span><span class="o">=</span><span class="s2">"zh_CN.UTF-8"</span>
<span class="nv">LC_COLLATE</span><span class="o">=</span><span class="s2">"zh_CN.UTF-8"</span>
<span class="nv">LC_CTYPE</span><span class="o">=</span><span class="s2">"zh_CN.UTF-8"</span>
<span class="nv">LC_MESSAGES</span><span class="o">=</span><span class="s2">"zh_CN.UTF-8"</span>
<span class="nv">LC_MONETARY</span><span class="o">=</span><span class="s2">"zh_CN.UTF-8"</span>
<span class="nv">LC_NUMERIC</span><span class="o">=</span><span class="s2">"zh_CN.UTF-8"</span>
<span class="nv">LC_TIME</span><span class="o">=</span><span class="s2">"zh_CN.UTF-8"</span>
<span class="nv">LC_ALL</span><span class="o">=</span></code></pre></figure></p>
<p>发现都是zh,如要转换一下:</p>
<p>终端中输入:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span><span class="nb">export </span><span class="nv">LC_ALL</span><span class="o">=</span>en_US.UTF-8
<span class="gp">$ </span><span class="nb">export </span><span class="nv">LANG</span><span class="o">=</span>en_US.UTF-8</code></pre></figure></p>
<p>这个时候你可以继续执行了。</p>
<p>慢着,这时你最好先cd到你的项目的根目录下</p>
<p>打开前面你创建的Podfile文件,终端中输入:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>vim Podfile</code></pre></figure></p>
<p>然后将文件中的内容清空,重新编辑一遍,保存退出。</p>
<p>参考:<a href="http://www.cnblogs.com/mgbert/p/3945273.html">http://www.cnblogs.com/mgbert/p/3945273.html</a></p>
那些在学习iOS开发前就应该知道的事(part 2)2015-07-05T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/07/05/things-i-wish-i-had-known-before-starting-ios-developmentpart-2<h2>英文原文:<a href="https://medium.com/ios-os-x-development/things-i-wish-i-had-known-before-starting-ios-development-part-2-d696eec65866">Things I wish I had known before starting iOS development—Part 2</a></h2>
<h2>翻译原文:<a href="http://www.cocoachina.com/ios/20150611/12111.html">那些在学习iOS开发前就应该知道的事(part 2)</a></h2>
<hr />
<p>如果你还没读这篇文章的第一部分,请先读完了再来看第二部分。</p>
<p><a href="http://www.cocoachina.com/ios/20150608/12052.html">那些在学习iOS开发前就应该知道的事(part 1)</a>:设计师设计出来了一个不错的引导界面,然而当我看到设计稿的时候,我们的app也没几天就要上线了……</p>
<p><img src="/assets/img/ios/start/1.gif" alt="1.gif" /></p>
<p>在第一部分中,我讨论了学习iOS开发的一些基本问题。第二部分将更多地讨论一些实际问题,这些问题都是你在开发iOS应用时会遇到的。</p>
<h2>Debugging</h2>
<hr />
<p>毋庸置疑,你在开发应用的过程中一定会遇到很多错误和异常。比如,你会遇到NSInvalidArgumentException,遇到NSInternalInconsistencyException,还会遇到“0xfaded322”这种错误代码。有时候你可以在Stack Overflow或Quora上找到答案,但大部分时候你得自力更生。</p>
<p>Xcode具有断点、视图调试和日志的功能,是debug的不二之选。但毕竟孤掌难鸣,你仍需为它找些得力的帮手。</p>
<h3>Pony Debugger</h3>
<p><img src="/assets/img/ios/start/21.png" alt="21.png" /></p>
<p><a href="https://github.com/square/PonyDebugger">square/PonyDebugger</a>:PonyDebugger - 使用Chrome Developer Tools,为你的native iOS应用进行远程网络和数据调试。</p>
<p>square开发了这个强大的开源工具。它是一个远程调试工具,类似于客户端库和网关服务器的结合体。它在web浏览器上使用Chrome Developer Tools,对应用的网络流量和数据存储进行调试。作为一个强大的网络调试器,PonyDebugger允许用户实时查看应用的网络请求。它还有另一个酷酷的功能,就是可以远程调试iOS应用的核心数据栈。</p>
<h3>Cocoa Lumberjack</h3>
<p><img src="/assets/img/ios/start/22.png" alt="22.png" /></p>
<p><a href="https://github.com/CocoaLumberjack/CocoaLumberjack">CocoaLumberjack</a>:CocoaLumberjack - 快速、简单,强大、灵活。它就是CocoaLumberjack, 一款好用的Mac和iOS日志框架。</p>
<p>CocoaLumberjack,它快速、简单,强大、灵活,是一款好用的Mac和iOS日志框架。如果你想找一款强大的日志工具,希望它具有自定义格式化这样的炫酷功能,那CocoaLumberjack就是你的菜。</p>
<h3>Reveal App</h3>
<p><img src="/assets/img/ios/start/23.png" alt="23.png" /></p>
<p><a href="http://revealapp.com/">Reveal</a>:Reveal - iOS应用的runtime视图调试。</p>
<p>虽然视图调试已经被引入iOS,但Reveal无疑在调试的细节方面更加强大。它是收费应用,但绝对物有所值。它有一些非常好用的功能,如Auto Layout Inspection。</p>
<h3>OHHTTPStubs</h3>
<p><img src="/assets/img/ios/start/24.png" alt="24.png" /></p>
<p><a href="https://github.com/AliSoftware/OHHTTPStubs">AliSoftware/OHHTTPStubs</a>:OHHTTPStubs - 轻松stub你的网络请求。以虚拟网络数据测试你的应用,可自定义响应时间……</p>
<p>OHHTTPStubs库可以轻松stub你的网络请求。它可以帮助你:</p>
<p>使用伪造的网络数据(stubbed from file)测试你的应用,并模拟慢速网络以检查应用在网络不良情况下的表现。
从设备中获得伪造的网络数据,用于写单元测试。</p>
<p><img src="/assets/img/ios/start/25.png" alt="25.png" /></p>
<h2>数据存储</h2>
<hr />
<p>可能大多数应用都需要将各种任务的数据存储在本地。数据存储是一个复杂的话题。它有很多选择,每个选择都对应一种情况。但我很喜欢Stack Overflow上的一种法则,用它来选择数据存储方式就很不错。</p>
<ul>
<li>若数据完全匹配内存且相对非结构化,则使用plist</li>
<li>若数据完全匹配内存且具有树状结构,则使用XML</li>
<li>若数据与内存不匹配且具有图形结构,同时应用不需要额外的查询能力,则使用Core Data</li>
<li>如果数据与内存不匹配且具有复杂结构,或应用需要关系数据库提供的强查询能力,则使用sqlite</li>
<li>如果数据必须保密(例如密码),则使用<a href="https://developer.apple.com/library/ios/#documentation/security/Conceptual/keychainServConcepts/iPhoneTasks/iPhoneTasks.html">keychain</a>。</li>
</ul>
<p>下面列了一些数据存储方面的库,或许对大家有帮助。</p>
<h3>FMDB</h3>
<p><img src="/assets/img/ios/start/26.png" alt="26.png" /></p>
<p><a href="http://cc.cocimg.com/api/uploads/20150610/1433927562572960.png">ccgus/fmdb</a>:fmdb - 围绕SQLite建立的Cocoa / Objective-C的wrapper</p>
<p>如果你在项目中使用SQLite,此wrapper库会使你的工作变简单。</p>
<h3>SSFKeychain</h3>
<p><img src="/assets/img/ios/start/27.png" alt="27.png" /></p>
<p><a href="https://github.com/soffes/sskeychain">soffes/sskeychain</a>:sskeychain - 简单的Objective-C wrapper,Mac和iOS上的keychain可以使用</p>
<p>要在应用中存储敏感数据,你必须时刻使用keychain。这个库可以简化使用keychain数据存储的过程。</p>
<h3>Magical Record</h3>
<p><img src="/assets/img/ios/start/28.png" alt="28.png" /></p>
<p><a href="https://github.com/magicalpanda/MagicalRecord">MagicalRecord</a> - 轻松管理Core Data</p>
<p>Core data非常难于管理。而这个库可以让你舒爽地管理Core Data。</p>
<h2>网络</h2>
<hr />
<p>要想让应用更有趣,你免不了要接入一些API。虽然iOS在对网络的支持方面表现良好,譬如它拥有NSURLSession、NSURLConnection和NSJSONSerialization,但我还是推荐你使用下面的库。</p>
<h3>AFNetworking</h3>
<p><img src="/assets/img/ios/start/29.png" alt="29.png" /></p>
<p><a href="https://github.com/AFNetworking/AFNetworking">AFNetworking</a>:AFNetworking - 令人拍案叫绝的iOS和OS X网络框架。</p>
<p>我认为这是有史以来最好的iOS库之一,它的功能何止炫酷二字。但这些功能中最重要的或许是开发者社区,他们每天都在使用这个库,并为AFNetworking做出贡献。一些iPhone、iPad和Mac上最火的应用都是由AFNetworking提供的支持。</p>
<h3>Restkit</h3>
<p><img src="/assets/img/ios/start/30.png" alt="30.png" /></p>
<p><a href="https://github.com/RestKit/RestKit">RestKit/RestKit</a>:RestKit是在iOS和OS X上使用和构建RESTful web资源的框架。</p>
<p>Restkit具有精心设计的API,访问和构建RESTful资源的过程如沐春风。如果你用core data进行数据存储、用rest service进行数据读取,那你的最佳选择就是它——与Core Data完美集成的Restkit。</p>
<h3>Alamofire</h3>
<p><img src="/assets/img/ios/start/31.png" alt="31.png" /></p>
<p><a href="https://github.com/Alamofire/Alamofire">Alamofire/Alamofire</a>:Alamofire - Swift下精致的HTTP Networking。</p>
<p>哟哟切克闹,Swift粉们不要闹,煎饼果子来一套。下面就是为你们准备的东西了。Alamofire是一个精致的网络库,它具有一些Swift专享的强大功能。</p>
<p>你也可以在这里找到很多其他的库。</p>
<h3>vsouza/awesome-ios</h3>
<p><img src="/assets/img/ios/start/32.png" alt="32.png" /></p>
<p>awesome-ios——精选的优质iOS生态系统,包括Objective-C和Swift项目。</p>
<h2>依赖管理</h2>
<hr />
<p>我在前面的文章中提到过依赖管理,但重要的问题要说两遍!在这里我还要给它细细讲一发。在项目中,你主要有三种管理依赖的方式。</p>
<h3>CocoaPods</h3>
<p><a href="https://cocoapods.org/">CocoaPods.org</a>:iOS和Mac项目的依赖管理器。</p>
<p>CocoaPods是Swift和Objective-C Cocoa项目的依赖管理器。它拥有将近一万个库,可以帮助你轻松扩大项目规模。要想管理Ruby Gems这种依赖,实践中最有效的方法就是它了。</p>
<p>谷歌开发者做了一个YouTube视频,解释了为什么要在你的项目中使用CocoaPods。小心笑尿。</p>
<p>点此观看视频:Route 85: <a href="https://youtu.be/iEAjvNRdZa0?list=PLOU2XLYxmsIKGQekfmV0Qk52qLG5LU2jO">An Introduction to CocoaPods</a></p>
<h3>Github Submodules</h3>
<p>你还可以使用git submodules,在项目中以sub repos形式管理依赖。子模块相对于Cocoapods的优势在于子模块也是sub-repos——这不仅是指git和git GUIs逐渐认可并更加支持它们,也意味着你的依赖可以将git repos和广阔的世界连接起来,而CocoaPods却不能。</p>
<p>但git submodules也有自己的问题:项目中没有那些你所依赖的代码的来源。它只是指向了子模块库。而大多数时间你根本不会去管这个库。</p>
<h3>Carthage</h3>
<p><img src="/assets/img/ios/start/33.png" alt="33.png" /></p>
<p><a href="https://github.com/Carthage/Carthage">Carthage/Carthage</a>:Carthage - 简单、去中心化的Cocoa依赖管理器。</p>
<p>Carthage旨在为Cocoa应用提供最简单的框架添加方式。Carthage使用xcodebuild建立framework binaries,把整合工作留给了用户。CocoaPods的方法更简单易用,而Carthage的方法更灵活温和。</p>
<p>不幸的是,Carthage也有一个巨大缺陷——仅支持iOS 8及以上版本。</p>
<p><img src="/assets/img/ios/start/34.png" alt="34.png" /></p>
<h2>测试</h2>
<hr />
<p>大多数人一提到应用测试就会呵欠连连。但如果没有测试的话,说不巧哪天你的应用就突然崩溃了。发布应用时,你一定得先进行深度测试,保证用户得到最佳体验。</p>
<p>这里有很多测试框架,它们可以简化测试工作。</p>
<h3>XCTest</h3>
<p>XCTest是一个单元测试框架,包含在Xcode中。它支持把单元测试作为项目编译过程的一部分。XCTest与XCode高度整合,因此提供了持续整合支持和覆盖率测试这样的功能。</p>
<h3>KIF</h3>
<p><img src="/assets/img/ios/start/35.png" alt="35.png" /></p>
<p><a href="https://github.com/kif-framework/KIF">kif-framework/KIF</a>:KIF - Keep It Functional - iOS功能测试框架</p>
<p>KIF是Keep It Functional的缩写。它由Square开源,是一款iOS一体化测试框架。它利用其可访问性——即系统让访问不能显示的应用成为可能,轻松实现了iOS应用的自动化。</p>
<h3>Kiwi</h3>
<p><img src="/assets/img/ios/start/36.png" alt="36.png" /></p>
<p><a href="https://github.com/kiwi-bdd/Kiwi">kiwi-bdd/Kiwi</a>:Kiwi - BDD for iOS</p>
<p>Kiwi是iOS开发的Behavior Driven Development库。其目标是提供一个安装和使用都非常简单的BDD库。</p>
<h3>Quick</h3>
<p><img src="/assets/img/ios/start/37.png" alt="37.png" /></p>
<p><a href="https://github.com/Quick/Quick.git">Quick/Quick</a>:Quick - Swift (以及Objective-C)的测试框架。</p>
<p>Quick是Swift和Objective-C的一款行为驱动的开发框架。它由RSpec、Specta和Ginkgo开发。与Quick并肩战斗的是Nimble——一款为测试服务的匹配框架。</p>
<p>我尝试在本文中讲尽量多的技术问题。跳入iOS开发的大坑之后,你就能用得到上我说的这些东西了。关于iOS开发需要说的实在太多,所以在这儿我没有谈到营销之类的话题。如果你想看的话,那就等我的下一篇文章吧!</p>
那些在学习iOS开发前就应该知道的事(part 1)2015-07-04T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/07/04/things-i-wish-i-had-known-before-starting-ios-developmentpart-1<h2>英文原文:<a href="https://medium.com/ios-os-x-development/things-i-wish-i-had-known-before-starting-ios-development-part-1-421a05e8447e">Things I wish I had known before starting iOS development—Part 1</a></h2>
<h2>翻译原文:<a href="http://www.cocoachina.com/ios/20150608/12052.html">那些在学习iOS开发前就应该知道的事(part 1)</a></h2>
<hr />
<p>设计师设计出来了一个不错的引导界面,然而当我看到设计稿的时候,我们的app也没几天就要上线了。这个界面模仿了Evernote iOS app的风格。</p>
<p><img src="/assets/img/ios/start/1.gif" alt="1.gif" /></p>
<p>我以迅雷不及掩耳盗铃之势开始在Xcode上编程,用了page view controller和scroll view。在Stack Overflow和Google的帮助下,我用了2天把它完成了。当我把产品给一个同样搞iOS开发的朋友看时,他跟我说,如果我用了<a href="https://github.com/mamaral/Onboard.git">这个开源项目的话</a>,一个小时就可以搞定一切。</p>
<p>过去这一年我经历了不少类似的事情,这些事情让我成长,让我能够成为一个更好的iOS开发工程师。我想跟大家分享一下我的经验,希望你们可以不再犯我这些错误,一路平坦走向成功。</p>
<h2>重视基础</h2>
<hr />
<p>刚开始学iOS开发的时候,我直接去学习了斯坦福大学的这门课程,非常有用。然而,虽然我从课程中学到了很多,但它并没有教给我多少iOS开发语言的基础知识——当时的iOS开发语言主要是Objective-C。开始写自己的app以后,我发现自己在基础知识方面欠缺很多,这导致我老是弄出来一些bug。</p>
<p>如果你在面向对象编程的语言方面没啥经验的话,我建议你在投身进行iOS开发之前先读一本这方面的经典书籍。我最喜欢的两本是<a href="https://www.bignerdranch.com/we-write/objective-c-programming/">Big Nerd Ranch Guide for Objective-C</a>和Swift的<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/">The Apple's Guide</a>。</p>
<p><img src="/assets/img/ios/start/2.jpeg" alt="2.jpeg" /></p>
<p>没错,<a href="http://www.raywenderlich.com/">Ray Wenderlich</a>上的教程和<a href="http://teamtreehouse.com/">Team Treehouse</a>上的视频课程能教给你很多,但缺少基础知识的程序员就像无源之水、无本之木,总是长久不了的。</p>
<h2>良师益友Github</h2>
<hr />
<p>我对这个iOS开源社区绝对是真爱。Github上面有无数的优质项目,如AFNetworking, Restkit、JSQMessages,甚是碉堡。你必须学会在这个社区里乘前人栽树之凉。</p>
<p>遇到问题时,先别一根筋急着苦思冥想或创建自己的库,不如先去GitHub或Google找一下有没有相似问题的解决方法。很有可能某个开发者已经写好了一个符合你需求的开源项目。</p>
<p>通过Facebook Groups或Slack chat来开始社区之旅吧!他们很乐意回答你的一切问题。你可以浏览那些好的开源项目,看看大牛们是如何组织代码的,自己也学着点。</p>
<p>这里是GitHub上一些最优质的iOS资源。</p>
<p><a href="https://github.com/vsouza/awesome-ios0">vsouza/awesome-ios</a>:awesome-ios——精选的优质iOS生态系统,包括Objective-C和Swift项目。</p>
<p><img src="/assets/img/ios/start/3.png" alt="3.png" /></p>
<p><a href="https://github.com/matteocrippa/awesome-swift">matteocrippa/awesome-swift</a>:awesome-swift——收集了很多优质的swift资源。你也可以来贡献自己的力量!</p>
<p><img src="/assets/img/ios/start/4.png" alt="4.png" /></p>
<p><a href="https://github.com/cjwirth/awesome-ios-ui">cjwirth/awesome-ios-ui</a>:awesome-ios-ui——优质iOS UI/UX库精选。</p>
<p><img src="/assets/img/ios/start/5.png" alt="5.png" /></p>
<p>如果你想找一些iOS的最佳实践以供自己模仿学习,那请看下面这些。</p>
<p>futurice/ios-good-practices:ios-good-practices——为iOS开发者提供灵感,作者是Futurice的开发者们。</p>
<p><img src="/assets/img/ios/start/6.png" alt="6.png" /></p>
<h2>了解你的工具</h2>
<hr />
<p>多数iOS开发者将Xcode作为开发的首选工具。Xcode有很多强大的特性,如Storyboards、Auto Layout,如果学会用这些的话,相信你的开发效率肯定能上一个台阶。出于某些限制,很多开发者会尽力避免使用Storyboards,但我个人认为,Storyboards是快速布局的有力工具。</p>
<p>学习使用Xcode中的快捷键。虽然看上去使用快捷键没节省几秒钟,但“不积小流,无以成江海“,久而久之省下的时间就多了。以上这些都是我亲测有效的手段,它们在提高开发效率方面给了我很大帮助。</p>
<ul>
<li>使用<a href="https://cocoapods.org/">Cocoapods</a>来进行依赖管理。你的团队会因此轻松很多。</li>
<li>在项目早期就学会使用<a href="https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/xcode_guide-continuous_integration/">持续整合(continuos integration)</a>,这样就可以避免未来可能出现的冗余工作。</li>
<li>使用<a href="https://developer.apple.com/testflight/">Testflight</a>来分发测试版本。在被苹果收购之后,Testflight变得更加简单易用了,每个人都可以通过它来使用iTunes账号进行测试版本的分发。</li>
<li>在app中整合<a href="https://try.crashlytics.com/">Crashlytics</a>,这样当app崩溃的时候你就可以获取Crash报告了。</li>
<li>如果你不想自己弄后端服务器的话,<a href="https://parse.com/">Parse</a>为我们提供了很好的服务。</li>
</ul>
<h2>读一些优质博客和资讯</h2>
<hr />
<p>前文已经介绍了一些很棒的iOS开源社区。很多优质的博客是由经验丰富的iOS开发者撰写的,每周都会有一些精彩的内容呈现。我最喜欢的一些包括:</p>
<p><a href="http://cocoawithlove.com/">Cocoa with Love</a>:在本博客中,我将专注于使用和管理用户界面中字符串的最佳实践。这是一个相当...</p>
<p>可能是最好的iOS博客。作者是Matt Galaghar。Matt做事情的方式是大师级的。</p>
<p><a href="http://iosdevweekly.com/">iOS Dev Weekly</a>:订阅本博客,轻松获得每周最棒的iOS开发网页精选。由Dave Verwer精选并发布...</p>
<p><img src="/assets/img/ios/start/7.png" alt="7.png" /></p>
<p>严格来说这不算个博客,但它每周都有超级超级棒的内容更新。作者是Dave Verwer。</p>
<p><a href="http://nshipster.com/">NSHipster</a>:Playgrounds并不是Swift语言本身的特性,它们其实是很棒的展示……</p>
<p><img src="/assets/img/ios/start/8.png" alt="8.png" /></p>
<p>NSHipster对Objective-C和Cocoa进行拾遗的杂志。它由Mattt Thompson每周更新。</p>
<p><a href="http://www.raywenderlich.com/">Ray wenderlich</a>:高质量的编程教程:iOS、Android、Mac,还有更多!</p>
<p><img src="/assets/img/ios/start/9.png" alt="9.png" /></p>
<p>Ray Wenderlich的博客(对初学者超有用)</p>
<p><a href="http://cocoacontrols.com/">Custom Controls for iOS and OS X - Cocoa Controls</a>:暂无描述</p>
<p><img src="/assets/img/ios/start/10.png" alt="10.png" /></p>
<p><a href="http://petersteinberger.com/">Peter Steinberger</a>:苹果在Xcode 6中增加了对NS_DESIGNATED_INITIALIZER 标志的支持,同时也将其添加到了各种各样的框架中……</p>
<p><img src="/assets/img/ios/start/11.png" alt="11.png" /></p>
<p><a href="http://mattgemmell.com/">Matt Gemmell</a>:基于我的写作项目进行简要更新。它或许会引起一些人的兴趣……</p>
<p><img src="/assets/img/ios/start/12.png" alt="12.png" /></p>
<p><a href="http://natashatherobot.com/">Natasha The Robot</a>:仅仅是又一个WordPress站点</p>
<p>在这些博客中挑选干货,认真阅读,相信你会变成一个更好的iOS开发者。</p>
<h2>设计也能变轻松</h2>
<hr />
<p>很多开发者对于iOS的设计方面谈虎色变。我们总是对设计敬而远之,一股脑儿都扔给设计师去做。但其实,只要稍稍努力,你也可以学会设计自己的app。</p>
<p>现在,设计师和开发者之间的界限日益模糊,因为好多成功的iOS独立开发者包揽了所有app设计、开发和营销工作。我将在下一部分谈到营销方面的事。如果你想设计自己的iOS app的话,不妨学一下Sketch这个工具。Sketch专为应用设计和网页设计而生,简单易上手。</p>
<p><a href="http://bohemiancoding.com/sketch/">Bohemian Coding - Sketch 3</a>:Sketch是简单易用的轻量级软件,它强大、灵活而快速。最后……</p>
<p><img src="/assets/img/ios/start/13.png" alt="13.png" /></p>
<p>你可以在网上找到大量的Sketch资源和插件,它们能让你有趣而简单地工作。一旦完成了设计工作,你可以立刻用这个神器将它们整合在一起。</p>
<p><a href="https://marvelapp.com/">Free mobile & web prototyping for designers - Marvel</a>:将草图和设计转化为可交互的Web、iPhone、iPad、Android和Apple Watch原型和模板……</p>
<p><img src="/assets/img/ios/start/14.png" alt="14.png" /></p>
<p>下一部分,我将谈一谈在开发自己的app时的必要方法,并且讲一些iOS应用的营销技巧。</p>
Linux的五个查找命令:find,locate,whereis,which,type2015-07-04T00:00:00+00:00http://jiaxianhua.github.io/linux/2015/07/04/5-find-command-of-linux<h2>原文: <a href="http://www.ruanyifeng.com/blog/2009/10/5_ways_to_search_for_files_using_the_terminal.html">http://www.ruanyifeng.com/blog/2009/10/5_ways_to_search_for_files_using_the_terminal.html</a></h2>
<hr />
<p>最近,我在学习Linux,下面是一些笔记。</p>
<p>使用电脑的时候,经常需要查找文件。</p>
<p>在Linux中,有很多方法可以做到这一点。国外网站LinuxHaxor总结了五条命令,你可以看看自己知道几条。大多数程序员,可能经常使用其中的2到3条,对这5条命令都很熟悉的人应该是不多的。</p>
<p><img src="/assets/img/linux/find-1.png" alt="find-1" /></p>
<h2>find</h2>
<hr />
<p>find是最常见和最强大的查找命令,你可以用它找到任何你想找的文件。</p>
<p>find的使用格式如下:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp"> $ </span>find <指定目录> <指定条件> <指定动作></p>
<p> - <指定目录>: 所要搜索的目录及其所有子目录。默认为当前目录。</p>
<p> - <指定条件>: 所要搜索的文件的特征。</p>
<p> - <指定动作>: 对搜索结果进行特定的处理。</code></pre></figure></p>
<p>如果什么参数也不加,find默认搜索当前目录及其子目录,并且不过滤任何结果(也就是返回所有文件),将它们全都显示在屏幕上。</p>
<p>find的使用实例:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp"> $ </span>find . -name <span class="s2">"my*"</span></code></pre></figure></p>
<p>搜索当前目录(含子目录,以下同)中,所有文件名以my开头的文件。</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp"> $ </span>find . -name <span class="s2">"my*"</span> -ls</code></pre></figure></p>
<p>搜索当前目录中,所有文件名以my开头的文件,并显示它们的详细信息。</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp"> $ </span>find . -type f -mmin -10</code></pre></figure></p>
<p>搜索当前目录中,所有过去10分钟中更新过的普通文件。如果不加-type f参数,则搜索普通文件+特殊文件+目录。</p>
<p><img src="/assets/img/linux/find-2.png" alt="find-2" /></p>
<h2>locate</h2>
<hr />
<p>locate命令其实是“find -name”的另一种写法,但是要比后者快得多,原因在于它不搜索具体目录,而是搜索一个数据库(/var/lib/locatedb),这个数据库中含有本地所有文件信息。Linux系统自动创建这个数据库,并且每天自动更新一次,所以使用locate命令查不到最新变动过的文件。为了避免这种情况,可以在使用locate之前,先使用updatedb命令,手动更新数据库。</p>
<p>locate命令的使用实例:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp"> $ </span>locate /etc/sh</code></pre></figure></p>
<p>搜索etc目录下所有以sh开头的文件。</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp"> $ </span>locate ~/m</code></pre></figure></p>
<p>搜索用户主目录下,所有以m开头的文件。</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp"> $ </span>locate -i ~/m</code></pre></figure></p>
<p>搜索用户主目录下,所有以m开头的文件,并且忽略大小写。</p>
<p><img src="/assets/img/linux/find-3.png" alt="find-3" /></p>
<h2>whereis</h2>
<hr />
<p>whereis命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b)、man说明文件(参数-m)和源代码文件(参数-s)。如果省略参数,则返回所有信息。</p>
<p>whereis命令的使用实例:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp"> $ </span>whereis grep</code></pre></figure></p>
<p><img src="/assets/img/linux/find-4.png" alt="find-4" /></p>
<h2>which</h2>
<hr />
<p>which命令的作用是,在PATH变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果。也就是说,使用which命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令。</p>
<p>which命令的使用实例:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp"> $ </span>which grep</code></pre></figure></p>
<p><img src="/assets/img/linux/find-5.png" alt="find-5" /></p>
<h2>type</h2>
<hr />
<p>type命令其实不能算查找命令,它是用来区分某个命令到底是由shell自带的,还是由shell外部的独立二进制文件提供的。如果一个命令是外部命令,那么使用-p参数,会显示该命令的路径,相当于which命令。</p>
<p>type命令的使用实例:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp"> $ </span><span class="nb">type cd</span></code></pre></figure></p>
<p>系统会提示,cd是shell的自带命令(build-in)。</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp"> $ </span><span class="nb">type </span>grep</code></pre></figure></p>
<p>系统会提示,grep是一个外部命令,并显示该命令的路径。</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp"> $ </span><span class="nb">type</span> -p grep</code></pre></figure></p>
<p>加上-p参数后,就相当于which命令。</p>
<p><img src="/assets/img/linux/find-6.png" alt="find-6" /></p>
mac vimrc2015-07-03T00:00:00+00:00http://jiaxianhua.github.io/vim/2015/07/03/mac-vimrc<h2>参考:<a href="http://www.zhihu.com/question/19989337">http://www.zhihu.com/question/19989337</a></h2>
<hr />
<p>像我这类懒人一直致力于寻找终极配置.</p>
<p>robbyrussell/oh-my-zsh · GitHub 终结了我的 Shell 配置;</p>
<p><a href="https://github.com/robbyrussell/oh-my-zsh">https://github.com/robbyrussell/oh-my-zsh</a></p>
<p>spf13/spf13-vim 路 GitHub 终结了我的 Vim 配置.</p>
<p><a href="https://github.com/spf13/spf13-vim">https://github.com/spf13/spf13-vim</a></p>
<h2>出错信息</h2>
<hr />
<p>E484: Can't open file /usr/local/share/vim/syntax/syntax.vim</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">export </span><span class="nv">VIMRUNTIME</span><span class="o">=</span>/usr/local/Cellar/vim/7.4.488/share/vim/vim74
<span class="c">#let $VIMRUNTIME=/usr/local/Cellar/vim/7.4.488/share/vim/vim74</span>
<span class="c">#set runtimepath=/usr/local/Cellar/vim/7.4.488/share/vim/vim74</span></p>
<p><span class="gp">$ </span>brew link vim</code></pre></figure></p>
<h2>vim syntax</h2>
<hr />
<p>neocomplete does not work with this version of Vim.</p>
<p>It requires Vim 7.3.885 or later with Lua support ("+lua”).</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>vim --version | grep lua
+listcmds +localmap -lua +menu +mksession +modify_fname +mouse -mouseshape</p>
<p><span class="gp">$ </span>brew install vim --with-lua
<span class="gp">$ </span>sudo chmod -R g+w /usr/local/
<span class="gp">$ </span>brew link lua</code></pre></figure></p>
JMeter Demo2015-07-02T00:00:00+00:00http://jiaxianhua.github.io/json/2015/07/02/jmeter-demo
<iframe height=714 width=1144 src="/assets/movie/JMeter.mov" frameborder=0 allowfullscreen></iframe>
What should Xcode 6 gitignore file include?2015-07-01T00:00:00+00:00http://jiaxianhua.github.io/xcode/2015/07/01/what-should-xcode-6-gitignore-file-include<p><a href="http://stackoverflow.com/questions/18939421/what-should-xcode-6-gitignore-file-include#">http://stackoverflow.com/questions/18939421/what-should-xcode-6-gitignore-file-include#</a></p>
<h2>new</h2>
<p>Another answer is that there's a website called <a href="https://www.gitignore.io">gitignore.io</a> , which generates the files based on the .gitignore templates from <a href="https://github.com/github/gitignore">https://github.com/github/gitignore</a>.</p>
<h2>old</h2>
<p>The easiest answer is that mine looks like this:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c"># Xcode</span>
.DS_Store
build/
<span class="k"><em></span>.pbxuser
!default.pbxuser
<span class="k"></em></span>.mode1v3
!default.mode1v3
<span class="k"><em></span>.mode2v3
!default.mode2v3
<span class="k"></em></span>.perspectivev3
!default.perspectivev3
<span class="k"><em></span>.xcworkspace
!default.xcworkspace
xcuserdata
profile
<span class="k"></em></span>.moved-aside
DerivedData
.idea/
<span class="c"># Pods - for those of you who use CocoaPods</span>
Pods</code></pre></figure></p>
<p>which I believe is the same .gitignore that GitHub sets up with all their repositories by default.</p>
Mac系统上的翻墙软件ShadowsocksX2015-06-30T00:00:00+00:00http://jiaxianhua.github.io/software/2015/06/30/mac-shadowsocksx<p>软件下载地址:<a href="http://ttt.tt/150/">http://ttt.tt/150/</a>
<a href="/assets/software/ShadowsocksX-2.5.dmg">ShadowsocksX</a></p>
<p>免费的代理服务:<a href="http://www.uudaili.org/free.html">http://www.uudaili.org/free.html</a></p>
exec与xargs区别2015-06-26T00:00:00+00:00http://jiaxianhua.github.io/linux/2015/06/26/the-different-between-exec-and-xarg<h2>原文:<a href="http://blog.chinaunix.net/uid-20662363-id-1904149.html">http://blog.chinaunix.net/uid-20662363-id-1904149.html</a></h2>
<p>exec:</p>
<blockquote><p>对符合条件的文件执行所给的Linux 命令,而不询问用户是否需要执行该命令。{}表示命令的参数即为所找到的文件,以;表示comman命令的结束。\是转义符,因为分号在命令中还有它用途,所以就用一个\来限定表示这是一个分号而不是表示其它意思。</p></blockquote>
<p>-ok:</p>
<blockquote><p>和-exec的作用相同,格式也一样,只不过以一种更为安全的模式来执行该参数所给出的shell给出的这个命令之前,都会给出提示,让用户来确定是否执行。</p></blockquote>
<p>xargs:</p>
<blockquote><p>要结合管道来完成</p>
<p>格式:<code>find [option] express |xargs command</code></p></blockquote>
<h2>我们看看<code>exec</code>和<code>xargs</code>都是如何传参数的。</h2>
<hr />
<h3>首先看看<code>exec</code>:</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>ls
index.skin1 skin1
<span class="gp">$ </span>find -type f -exec <span class="nb">echo </span>file <span class="o">{}</span> <span class="se">\;</span>
file ./skin1
file ./index.skin1</code></pre></figure></p>
<p>很明显,<code>exec</code>是对每个找到的文件执行一次命令,除非这单个的文件名超过了几k,否则不会出现命令行超长出错的问题。</p>
<h3>我们再看看xargs:</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>ls
index.skin1 skin1
<span class="gp">$ </span>find -type f | xargs <span class="nb">echo
</span>find -type f | xargs <span class="nb">echo</span>
./skin1 ./index.skin1</code></pre></figure></p>
<p>这里大家看到,<code>xargs</code>是把所有找到的文件名一股脑的转给命令。当文件很多时,这些文件名组合成的命令行参数很容易超长,导致命令出错。</p>
<p>同时,这也是 <code>find | xargs</code> 这种组合在处理有空格字符的文件名时之所以出错的原因。</p>
<p>这时执行的命令已经不知道这些空格那些是分割符、那些是文件名中所包含的!而用<code>exec</code>则不会有这两个问题。</p>
<p>下面是一个演示:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>mkdir TEST
<span class="gp">$ </span><span class="nb">cd </span>TEST
/home/xxx/TEST
<span class="gp">$ </span>touch <span class="s2">"file a"</span>
<span class="gp">$ </span>touch <span class="s2">"file b"</span>
<span class="gp">$ </span>ls
file a file b
<span class="gp">$ </span>find -type f | xargs rm
rm: 无法删除‘./file’: 没有那个文件或目录
rm: 无法删除‘a’: 没有那个文件或目录
rm: 无法删除‘./file’: 没有那个文件或目录
rm: 无法删除‘b’: 没有那个文件或目录
<span class="gp">123$ </span>ls
file a file b
<span class="gp">$ </span>find -type f -exec rm <span class="o">{}</span> <span class="se">\;</span>
<span class="gp">$ </span>ls
<span class="err">$</span></code></pre></figure></p>
<p>从这里可以看出<code>exec</code>的缺点是:</p>
<ul>
<li>每处理一个文件/目录,都要启动一次命令,效率不好;</li>
<li>格式麻烦,必须用 {} 做文件的代位符,必须用 \;</li>
<li><p>作为命令的结束符,书写不便。</p></li>
<li><p>而xargs不能操作文件名有空格的文件。</p></li>
</ul>
<p>所以如果要使用的命令支持一次处理多个文件,并且也知道这些文件里没有带空格的、文件数目也不大,那么使用 <code>xargs</code>比较方便; 否则,就要用 <code>exec</code> 了。</p>
实现项目下载需求时遇过的那些坑2015-06-25T00:00:00+00:00http://jiaxianhua.github.io/network/2015/06/25/troubles-of-realizing-download-module<p>来自DeveloperLx的github</p>
<p><a href="https://github.com/DeveloperLx/Troubles-of-realizing-download-module">https://github.com/DeveloperLx/Troubles-of-realizing-download-module</a></p>
<hr />
<h3>导语</h3>
<ul>
<li><p>当前市面上的APP,凡有涉及到视频、期刊、或其它大型文件传输、浏览等用途的,添加下载或缓存至本地的功能以避免网速的限制及依赖,毫无疑问都将给用户带来更好的体验。而谈到下载技术,就又不得不牵扯到了断点续传,队列任务等老生常谈的问题。这不,本人当前的项目,就恰好遇到了这样的需求。然而在经过大量调研之后,本人竟无法找到一篇总结得很好的文档,对此进行全面的介绍;能够寻到的一些活跃度并不高的开源项目,却又不能恰如其分并抱之以信赖满足项目的需求。所以仔细斟酌后,不得不选择自己动手,丰衣足食。钻研的过程中遇到了不少坑、不少困难,有些个别的地方真是不用不知道,一用才知道是如此得蹩脚,难怪很少有人对此进行系统完整的介绍。现将本人在实现下载模块时所用到的技术总结如下,相信本文中所蕴涵的干货一定不会令费心阅读的你感到失望!</p></li>
<li><p>话休絮烦。首先,说下载就离不开网络请求。而当今iOS开发技术当中,最广泛使用的网络请求框架无疑要属AFNetworking。经过对其进行简单研究后,你就会寻到最适合用来完成下载这件“小事”的组件,叫做<code>AFHTTPRequestOperation</code></p></li>
</ul>
<p><strong>现假定我们的需求是最常见,也是最能体现技术问题的一个,叫做:</strong></p>
<ul>
<li>下载队列在某一时刻,最多仅能有一个下载任务处于正在下载的状态中!</li>
</ul>
<hr />
<h6>-- 叙述的节奏似乎稍稍快了些</h6>
<blockquote><p>那就先来看下实现队列下载、断点续传等需求的关键示例代码吧!</p></blockquote>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c"> NSError * error = nil;</p>
<pre><code>// 创建下载队列
NSOperationQueue * downloadOperationQueue = [[NSOperationQueue alloc]init];
// 规定operationQueue中,最大可以同时执行的operation数量为1
downloadOperationQueue.maxConcurrentOperationCount = 1;
// 创建单个下载任务(访问已下载部分的文件,实现断点续传)
NSMutableURLRequest * downloadRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:DOWNLOAD_URL_STRING]];
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:downloadRequest];
AFHTTPRequestOperation * downloadOperation = [[AFHTTPRequestOperation alloc]initWithRequest:downloadRequest];
unsigned long long downloadedPartFileSize = 0;
if ([[NSFileManager defaultManager] fileExistsAtPath:DOWNLOADED_PART_FILE_PATH]) {
NSDictionary * fileAttributes = [[NSFileManager defaultManager]attributesOfItemAtPath:DOWNLOADED_PART_FILE_PATH error:&amp;error];
downloadedPartFileSize = [fileAttributes fileSize];
NSString * headerRangeFieldValue = [NSString stringWithFormat:@"bytes=%llu-", downloadedPartFileSize];
[downloadRequest setValue:headerRangeFieldValue forHTTPHeaderField:@"Range"];
}
downloadOperation.outputStream = [NSOutputStream outputStreamToFileAtPath:DOWNLOADED_PART_FILE_PATH append:YES];
[downloadOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
NSLog(@"%lld/%lld", totalBytesRead + downloadedPartFileSize, totalBytesExpectedToRead + downloadedPartFileSize);
}];
[downloadOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"downloadOperation completion block invoked");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"downloadOperation failure block invoked");
}];
// 将单个下载任务加入到下载队列当中
[downloadOperationQueue addOperation:downloadOperation];
// 暂停某下载任务
[downloadOperation pause];
// 继续某下载任务
[downloadOperation resume];
// 取消某下载任务(同时应将其已下载部分的文件删除)
[downloadOperation cancel];
[[NSFileManager defaultManager] removeItemAtPath:DOWNLOADED_PART_FILE_PATH error:&amp;error];
// 取消全部下载任务
[downloadOperationQueue cancelAllOperations];
// 此外还有若干方法用以判断相应一见其名便知其义的状态...
downloadOperation.isReady
downloadOperation.isExecuting
downloadOperation.isPaused
downloadOperation.isCancelled
downloadOperation.isFinished
// 判断downloadOperation是否存在在downloadOperationQueue当中
[downloadOperationQueue.operations containsObject:downloadOperation]</code></pre></figure>
</code></pre>
<ul>
<li>以上代码创建了一个AFHTTPRequestOperation对象作为单个下载任务,并将其加入到<em>NSOperationQueue</em>中。仿照上例,我们可以创建多个AFHTTPRequestOperation对象并加入到<em>NSOperationQueue</em>中,即形成了下载队列</li>
</ul>
<hr />
<ul>
<li>做到这里,你是不是认为已经没有神马技术问题啦?只要把operation一个个地添加到queue里, 下载任务就可以一个接一个地自动执行了!而如果我们将上一个operation暂停、取消,或是它自然地下载完成了,又或是它下载中途失败了,下一operation就会聪明地自动启动,继续其下载任务了!!?</li>
</ul>
<h2>错!!!!</h2>
<ul>
<li>接下来笔者将要告诉你的,就是本文最最核心的<code>干货</code>,绝对颠覆你的想象!!</li>
</ul>
<hr />
<ul>
<li><p>只要你亲手动手试一试,就会发现如下大跌眼球的惊恐现象!!</p></li>
<li><p>惊人事实 1: 对queue中前一个下载operation执行pause方法,下一个operation并不能自动启动进入正在执行的状态!!</p></li>
<li><p>惊人事实 2: 如果queue中前一个下载operation执行失败了(可用下载中途断网进行模拟),它将从queue中自动地被移除掉!!</p></li>
<li><p>惊人事实 3: 注意到代码里那个failure回调的block了没?它不仅仅将在operation执行失败的时候被调用,还会在operation被cancel的时候被调用!!所以对于神马叫做“operation的失败”,你要重新建立起你的世界观了!!</p></li>
<li><p>惊人事实 4: 如果对一个正处于pause状态的operation执行cancel会怎么样?答案是这个operation还保留在queue中!!并且仍然保持着pause状态!!仅有的一点变化,是它的isCancelled属性,变成了YES!!</p></li>
<li><p>......未完待续,本文要令你感到惊诧的,还有很多</p></li>
</ul>
<blockquote><p>由于这些问题间相互关系的错综复杂,为了清晰条理地予以说明,特将本人实验中所观察到operation的行为总结如下表。其中有悖于我们想象的结果,已用<code>彩色背景字体</code>标出</p></blockquote>
<h3></h3>
<table>
<thead>
<tr>
<th style="text-align:center;">Index </th>
<th style="text-align:center;"> Description </th>
<th style="text-align:center;"> isReady </th>
<th style="text-align:center;"> isExecuting </th>
<th style="text-align:center;"> isPaused </th>
<th style="text-align:center;"> isCancelled </th>
<th style="text-align:center;"> isFinished </th>
<th style="text-align:center;"> in Queue </th>
<th style="text-align:center;"> success block invoked </th>
<th style="text-align:center;"> failure block invoked </th>
<th style="text-align:center;"> 与预期不相符的事实 </th>
<th style="text-align:center;"> 可能的解决方案</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center;">1 </td>
<td style="text-align:center;"> 加入queue前 </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
</tr>
<tr>
<td style="text-align:center;">2 </td>
<td style="text-align:center;"> 加入queue后 </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
</tr>
<tr>
<td style="text-align:center;">3 </td>
<td style="text-align:center;"> 自然结束后 </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
</tr>
<tr>
<td style="text-align:center;">4 </td>
<td style="text-align:center;"> 正在下载时执行pause方法后 </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
</tr>
<tr>
<td style="text-align:center;">5 </td>
<td style="text-align:center;"> 正在下载时执行cancel方法后 </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> <code>YES</code> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> <code>YES</code> </td>
<td style="text-align:center;"> 被标记finished;fail block被调用 </td>
<td style="text-align:center;"> 不能仅凭fail block被调用判定某operation执行失败;必须同时判定isCancelled才是真正的失败,否则只是被人为地取消了 </td>
</tr>
<tr>
<td style="text-align:center;">6 </td>
<td style="text-align:center;"> 正在暂停时执行resume方法后 </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
</tr>
<tr>
<td style="text-align:center;">7 </td>
<td style="text-align:center;"> 正在暂停时执行cancel方法后 </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> <code>YES</code> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> <code>YES</code> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> Paused状态未被取消;未能从queue中被移除 </td>
<td style="text-align:center;"> 如想将暂停时的operation取消并从operationQueue中清除掉,必须首先执行[downloadOperation resume]后,再执行[downloadOperation cancel] </td>
</tr>
<tr>
<td style="text-align:center;">8 </td>
<td style="text-align:center;"> 中途意外失败后 </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> <code>YES</code> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> 被标记为finished;从queue中被移除 </td>
<td style="text-align:center;"> 必须用另外的方式记录执行中途意外失败的downloadOperation以实现中途失败的下载任务断点续传 </td>
</tr>
<tr>
<td style="text-align:center;">9 </td>
<td style="text-align:center;"> 等待上一任务结束时 </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
</tr>
<tr>
<td style="text-align:center;">10 </td>
<td style="text-align:center;"> 上一任务中途意外失败后 </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
</tr>
<tr>
<td style="text-align:center;">11 </td>
<td style="text-align:center;"> 上一任务正在执行时被暂停后 </td>
<td style="text-align:center;"> <code>YES</code> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> 上一operation被正在执行时被暂停后不能自动使下一operation开始执行 </td>
<td style="text-align:center;"> 前一个operation被暂停后,必须手动启动下一operation </td>
</tr>
<tr>
<td style="text-align:center;">12 </td>
<td style="text-align:center;"> 上一任务正在执行时被取消后 </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
</tr>
<tr>
<td style="text-align:center;">13 </td>
<td style="text-align:center;"> 上一任务正在暂停时被取消后 </td>
<td style="text-align:center;"> <code>YES</code> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> 上一operation被正在暂停时被取消后不能自动使下一operation开始执行 </td>
<td style="text-align:center;"> 前一个operation被暂停后,必须手动启动下一operation </td>
</tr>
<tr>
<td style="text-align:center;">14 </td>
<td style="text-align:center;"> 队列被执行cancelAllOperations </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> YES </td>
<td style="text-align:center;"> <code>YES</code> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> </td>
<td style="text-align:center;"> <code>YES</code> </td>
<td style="text-align:center;"> 被标记finished;fail block被调用 </td>
<td style="text-align:center;"> 不能仅凭fail block被调用判定某operation执行失败;必须同时判定 isCancelled才是真正的失败,否则只是被人为地取消了 </td>
</tr>
</tbody>
</table>
<hr />
<ul>
<li><p>有木有感到<em>AFHTTPRequestOperation</em>和<em>NSOperationQueue</em>是个多么坑爹的东东?为何就不能像我们想象中一样用得舒爽?</p></li>
<li><p><strong>原因就在于<em>AFHTTPRequestOperation</em>的父类<em>NSOperation</em>,在设计之处就不是为了下载的操作而生的!</strong>人家开始就仅仅是用来处理多线程的啊!!所以造成了<em>AFNetworking</em>在扩展这个类的时候,可用的资源、接口等等就非常少。对于什么下载任务暂停/继续,下载中途失败等等情况,很多问题几乎就是没有办法理想地解决的,只好用<em>NSOperation</em>中仅有的几种状态予以并不贴切的表示。于是乎就出现了上表中种种诡异的情况</p></li>
</ul>
<blockquote><p> 补充几点干货。然后告诉你一个本文之前偷偷误导了你的大坑!!</p></blockquote>
<ul>
<li><p>惊人事实 5:如果一个queue中有一个下载operation正在执行,此时对另一处在isReady状态的operation执行start方法会怎么样?你很可能会说:“没用的,因为之前设了queue.maxConcurrentOperationCount = 1嘛!” 可事实恰好相反,这个operation也会立刻被启动执行!!于是乎你不忍心看到的事情就出现了,这时queue将会有两个任务被同时执行!!maxConcurrentOperationCount完全失效了!!</p></li>
<li><p>惊人事实 6:承接上一点,如果此时另一条的状态不是isReady,而是isPaused暂停状态,你对其执行resume方法,此时会怎么样呢?哈哈,没错,你吸取了上一条的经验,终于猜对了!这个operation也会立刻启动被执行,不管当前的queue有没有另一个operation正在被执行!!从中我们就可以意识到,maxConcurrentOperationCount这个属性,只能管得自动启动每一operation时,先检查下是否正在执行的operation的数量已经超过那个数字了;可是如果你要手动start某一operation,对不起,这条限制半点都没有用处了......</p></li>
<li><p>惊人事实 7:从上表中我们可以看到,无论是一个operation自然地执行完毕,还是中途失败,还是被执行了cancel方法,都会被标记为isFinished,从operation中被移除掉,operation所认为的“完成”可完全不像我们想象中的那么狭义!问题来了,此时如果再对这个operation执行start方法会怎么样?对不起!没有任何用处!😭所以你如果想要让一个已失败的operation从断点处继续再开始执行下载该怎么办?不好意思,只好新建operation重新再来了......</p></li>
</ul>
<h6>基于实验我们又可以得出了这样的一张流程图:</h6>
<ul>
<li><img src="/assets/img/ios/flow.jpeg" alt="Flow" /></li>
</ul>
<hr />
<ul>
<li>头痛、抓狂得很啊!!本人刚开始实现下载模块相关需求的时候,就被这些问题坑了个体无完肤。最后得出了本文最大的关键结论,也就是前面所说的“大坑”:</li>
</ul>
<h3>不能够使用NSOperationQueue来进行多下载任务的管理!!!</h3>
<h6>理由如下:</h6>
<ol>
<li><p>你无法妥善地实现“队列中最多仅能有一个下载任务正在进行”这条产品经理臆测会让开发变简单的需求!!比方说,你让NSOperationQueue中一个operation暂停后,下一个任务并不会自动启动啊!有人说可以手动去start下一个operation,如果这个姑且算做可以接受,可是问题又来了:我们没有办法手动将一个operation置为isReady状态啊!!处于isReady状态的operation,要么是还未加入queue,要么是加入了还未轮到执行,但是它只要一执行,就再也回不到isReady的状态了!那我们要让暂停的operation恢复到等待下载状态该怎么搞?此时可能还有另一operation正在执行啊!!反之笔者搞了半天,是无能为力了</p></li>
<li><p>下载是需要一定时间的过程,需要不停地向服务器进行请求,那么就永远避免不了因为网络等原因中途会失败的问题。可要命的是,一旦下载失败,operation就会毫不妥协地从queue中被移除掉啊!!你能在这时候让你的下载任务从UI界面上消失掉吗?显然大BOSS是不会允许你这么干的。有人说可以重建operation再加入到queue中,可那样你只能将operation插到队尾,列表顺序就被打乱了啊!!你去瞧瞧看,operationQueue.operations,那可只是一个<strong>只读属性</strong>啊!!</p></li>
<li><p>......自己去体会吧,反正坑多的已经无力吐槽,再坚持下去也是枉费心思了。</p></li>
</ol>
<hr />
<ul>
<li>不幸的事情来了。笔者最后只得放弃NSOperationQueue,使用古老原始的工具--NSMutableArray来进行多下载任务的管理。这样的话所有operation的启动、移除等操作都必须依靠手动来执行。这个办法虽然办法土了些,可是起码对于每个operation的控制权又重新回到了我们手里。有得必有失嘛!当能恰当地实现了项目需求的时候,这点牺牲也就算不上神马了</li>
</ul>
<h6>在使用<strong>AFHTTPRequestOperation</strong>时我们还需要注意以下几点:</h6>
<ol>
<li>对isReady状态的operation执行resume、pause、cancel等方法是没有任何用处的,所以为了确保执行正确,在对operation执行resume、pause、cancel前,都要首先执行[operation start]。(对已经start过的operation执行start不会造成任何影响)</li>
<li>对处于isPaused的operation执行cancel方法是无法得到正确结果的,所以每次执行cancel方法前,都要先执行一下[operation resume]。 (同样对于正处于isExecuting状态的operation来说,执行resume方法也是不会造成任何影响的)</li>
<li>对于下载模块这个纠结之处来说,本地持久化下载记录的相关数据也是必不可少的,理由如下:
```
<ol type="a">
<li>AFHTTPRequestOperation、NSMutableArray这些都是运行时的东西,一关掉app,这些东西自然也都消失得无影无踪了。我们能让下载记录就此消失得无影无踪么?NO!显然是不能接受的</li>
<li>我们下载得到的那个文件,可能是已下载完成的,可能是只下载了部分的;而只下载了部分这种的,又可能是下载中途暂停了的,失败的,被取消的等等情况。请问单凭这个文件如何判断它是属于哪种情况?而且这还不够,有些下载任务根本可能就还未生成相应的下载文件,app就已经被关了啊!你能就把这种的下载任务扔掉吗?显然是绝不可以的</li>
<li>不使用operationQueue我们同样无法手动将operation标记为队列等待的isReady状态,怎么办?只有将operation设定为paused,然后相应的数据记录标记为isReady状态好了(本人使用的是CoreData进行本地持久化存储)</li>
<li>......用operation外的数据模型记录下载任务的状态好处还有很多,但同时带来的同步更新问题也有很多,具体就留给大家自己去体会了!
```</li>
</ol>
</li>
</ol>
<hr />
<ul>
<li>以上就是本人总结下载模块实现时需要注意到的种种内容。当然各位大神如果有更好的方案提出,比如用本人掌握得还不够好的stream如何实现上述需求,本人也愿虚心听取以将此处完善得更好。欢迎直言批评与不吝赐教!!</li>
</ul>
如何在Git中撤销一切2015-06-24T00:00:00+00:00http://jiaxianhua.github.io/git/2015/06/24/how-to-undo-almost-anything-with-git<p>翻译:李伟</p>
<p>审校:张帆</p>
<p>译自:<a href="https://github.com/blog/2019-how-to-undo-almost-anything-with-git">Github</a></p>
<p><img src="/assets/img/git/logo.png" alt="logo" /></p>
<p>任何一个版本控制系统中,最有用的特性之一莫过于 “撤销(undo)”操作。在Git中,“撤销”有很多种含义。</p>
<p>当你完成了一次新的提交(commit),Git会及时存储当前时刻仓库(repository)的快照(snapshot);你能够使用Git将项目回退到任何之前的版本。</p>
<p>下文中,我将列举几个常见的、需要“撤销”的场景,并且展示如何使用Git来完成这些操作。</p>
<h2>一、撤销一个公共修改 Undo a "public" change</h2>
<hr />
<p><strong>场景</strong>:你刚刚用git push将本地修改推送到了GitHub,这时你意识到在提交中有一个错误。你想撤销这次提交。</p>
<p>使用撤销命令:<code>git revert</code></p>
<p>发生了什么:git revert将根据给定SHA的相反值,创建一个新的提交。如果旧提交是“matter”,那么新的提交就是“anti-matter”——旧提交中所有已移除的东西将会被添加进到新提交中,旧提交中增加的东西将在新提交中移除。</p>
<p>这是Git最安全、也是最简单的“撤销”场景,因为这样不会修改历史记录——你现在可以git push下刚刚revert之后的提交来纠正错误了。</p>
<h2>二、修改最近一次的提交信息 Fix the last commit message</h2>
<hr />
<p><strong>场景</strong>:你只是在最后的提交信息中敲错了字,比如你敲了git commit -m "Fxies bug #42",而在执行git push之前你已经意识到你应该敲"Fixes bug #42"。</p>
<p>使用撤销命令:<code>git commit –amend或git commit --amend -m "Fixes bug #42"</code></p>
<p>发生了什么:git commit –amend将使用一个包含了刚刚错误提交所有变更的新提交,来更新并替换这个错误提交。由于没有staged的提交,所以实际上这个提交只是重写了先前的提交信息。</p>
<h2>三、撤销本地更改 Undo "local" changes</h2>
<hr />
<p><strong>场景</strong>:当你的猫爬过键盘时,你正在编辑的文件恰好被保存了,你的编辑器也恰在此时崩溃了。此时你并没有提交过代码。你期望撤销这个文件中的所有修改——将这个文件回退到上次提交的状态。</p>
<p>使用撤销命令:<code>git checkout --</code></p>
<p>发生了什么:git checkout将工作目录(working directory)里的文件修改成先前Git已知的状态。你可以提供一个期待回退分支的名字或者一个确切的SHA码,Git也会默认检出HEAD——即:当前分支的上一次提交。</p>
<p>注意:用这种方法“撤销”的修改都将真正的消失。它们永远不会被提交。因此Git不能恢复它们。此时,一定要明确自己在做什么!(或许可以用git diff来确定)</p>
<h2>四、重置本地修改 Reset "local" changes</h2>
<hr />
<p><strong>场景</strong>:你已经在本地做了一些提交(还没push),但所有的东西都糟糕透了,你想撤销最近的三次提交——就像它们从没发生过一样。</p>
<p>使用撤销命令:<code>git reset</code> 或 <code>git reset --hard</code></p>
<p>发生了什么:git reset将你的仓库纪录一直回退到指定的最后一个SHA代表的提交,那些提交就像从未发生过一样。默认情况下,git reset会保留工作目录(working directory)。这些提交虽然消失了,但是内容还在磁盘上。这是最安全的做法,但通常情况是:你想使用一个命令来“撤销”所有提交和本地修改——那么请使用--hard参数吧。</p>
<h2>五、撤销本地后重做 Redo after undo "local"</h2>
<hr />
<p><strong>场景</strong>:你已经提交了一些内容,并使用git reset –hard撤销了这些更改(见上面),突然意识到:你想还原这些修改!</p>
<p>使用撤销命令:<code>git reflog</code> 和 <code>git reset</code> 或者 <code>git checkout</code></p>
<p>发生了什么:git reflog是一个用来恢复项目历史记录的好办法。你可以通过git reflog恢复几乎任何已提交的内容。</p>
<p>你或许对git log命令比较熟悉,它能显示提交列表。git reflog与之类似,只不过git reflog显示的是HEAD变更次数的列表。</p>
<p>一些说明:</p>
<ol>
<li><p>只有HEAD会改变。当你切换分支时,用git commit提交变更时,或是用git reset撤销提交时,HEAD都会改变。但当你用git checkout --时, HEAD不会发生改变。(就像上文提到的情形,那些更改根本就没有提交,因此reflog就不能帮助我们进行恢复了)</p></li>
<li><p>git reflog不会永远存在。Git将会定期清理那些“不可达(unreachable)”的对象。不要期望能够在reflog里找到数月前的提交记录。</p></li>
<li><p>reflog只是你个人的。你不能用你的reflog来恢复其他开发者未push的提交。</p></li>
</ol>
<p><img src="/assets/img/git/undo-2.png" alt="undo-1" /></p>
<p>因此,怎样合理使用reflog来找回之前“未完成”的提交呢?这要看你究竟要做什么:</p>
<ol>
<li><p>如果你想恢复项目历史到某次提交,那请使用git reset --hard</p></li>
<li><p>如果你想在工作目录(working direcotry)中恢复某次提交中的一个或多个文件,并且不改变提交历史,那请使用git checkout--</p></li>
<li><p>如果你想确切的回滚到某次提交,那么请使用git cherry-pick。</p></li>
</ol>
<h2>六、与分支有关的那些事 Once more, with branching</h2>
<hr />
<p><strong>场景</strong>:你提交了一些变更,然后你意识到你正在master分支上,但你期望的是在feature分支上执行这些提交。</p>
<p>使用撤销命令:<code>git branch feature, git reset --hard origin/master</code> 和 <code>git checkout feature</code></p>
<p>发生了什么:你可能用的是git checkout -b来建立新的分支,这是创建和检出分支的便捷方法——但实际你并不想立刻切换分支。git branch feature会建立一个叫feature的分支,这个分支指向你最近的提交,但是你还停留在master分支上。</p>
<p>git reset --hard将master回退至origin/master,并忽略所有新提交。别担心,那些提交都还保留在feature上。</p>
<p>最后,git checkout将分支切换到feature,这个分支原封不动的保留了你最近的所有工作。</p>
<h2>七、事半功倍处理分支 Branch in time saves nine</h2>
<hr />
<p><strong>场景</strong>:你基于master新建了一个feature分支,但是master分支远远落后与origin/master。现在master分支与origin/master同步了,你期望此刻能在feature下立刻commit代码,并且不是在远远落后master的情况下。</p>
<p>使用撤销命令:<code>git checkout feature</code> 和 <code>git rebase master</code></p>
<p>发生了什么:你也许已经敲了命令:git reset(但是没用--hard,有意在磁盘上保存这些提交内容),然后敲了git checkout -b,之后重新提交更改,但是那样的话,你将失去本地的提交记录。不过,一个更好的方法:</p>
<p>使用git rebase master可以做到一些事情:</p>
<ol>
<li><p>首先,它定位你当前检出分支和master之间的共同祖先节点(common ancestor)。</p></li>
<li><p>然后,它将当前检出的分支重置到祖先节点(ancestor),并将后来所有的提交都暂存起来。</p></li>
<li><p>最后,它将当前检出分支推进至master末尾,同时在master最后一次提交之后,再次提交那些在暂存区的变更。</p></li>
</ol>
<h2>八、批量撤销/找回 Mass undo/redo</h2>
<hr />
<p><strong>场景</strong>:你开始朝一个既定目标开发功能,但是中途你感觉用另一个方法更好。你已经有十几个提交,但是你只想要其中的某几个,其他的都可以删除不要。</p>
<p>使用撤销命令:<code>git rebase -i</code></p>
<p>发生了什么:-i将rebases设置为“交互模式(interactive mode)”。rebase开始执行的操作就像上文讨论的一样,但是在重新执行某个提交时,它会暂停下来,让你修改每一次提交。</p>
<p>rebase -i将会打开你的默认文本编辑器,然后列出正在执行的提交,就像这样:</p>
<p><img src="/assets/img/git/undo-2.png" alt="undo-2" /></p>
<p>前两列最关键:第一列是选择命令,它会根据第二列中的SHA码选择相应的提交。默认情况下,rebase –i会认为每个更改都正通过pick命令被提交。</p>
<p>要撤销一个提交,直接在编辑器删除对应的行就可以了。如果在你的项目不再需要这些错误的提交,你可以直接删除上图中的第1行和3-4行。</p>
<p>如果你想保留提交但修改提交信息,你可以使用reword命令。即,将命令关键字pick换成reword(或者r)。你现在可能想立刻修改提交消息,但这么做不会生效——rebase –i将忽略SHA列后的所有东西。现有的提交信息会帮助我们记住0835fe2代表什么。当你敲完rebase –i命令后,Git才开始提示你重写那些新提交消息。</p>
<p>如果你需要将2个提交合并,你可以用squash或者fixup命令,如下图:</p>
<p><img src="/assets/img/git/undo-3.png" alt="undo-3" /></p>
<p>squash和fixup都是“向上”结合的——那些用了这些合并命令(编者按:指squash、fixup)的提交,将会和它之前的提交合并:上图中,0835fe2和6943e85将会合并成一个提交,而38f5e4e和af67f82将会合并成另一个提交。</p>
<p>当你用squash时,Git将会提示是否填写新的提交消息;fixup则会给出列表中第一个提交的提交信息。在上图中,af67f82是一个“Ooops”信息,因为这个提交信息已经同38f5e4e一样了。但是你可以为0835fe2和6943e85合并的新提交编写提交信息。</p>
<p>当你保存并退出编辑器时,Git将会按照从上到下的顺序执行你的提交。你可以在保存这些提交之前,修改提交的执行顺序。如果有需要,你可以将af67f82和0835fe2合并,并且可以这样排序:</p>
<p><img src="/assets/img/git/undo-4.png" alt="undo-4" /></p>
<h2>九、修复早先的提交 Fix an earlier commit</h2>
<hr />
<p><strong>场景</strong>:之前的提交里落下了一个文件,如果先前的提交能有你留下的东西就好了。你还没有push,并且这个提交也不是最近的提交,因此你不能用commit –amend。</p>
<p>使用撤销命令:<code>git commit --squash</code> 和 <code>git rebase --autosquash -i</code></p>
<p>发生了什么:git commit –squash将会创建一个新的提交,该提交信息可能像这样“squash! Earlier commit”。(你也可以手写这些提交信息,commit –squash只是省得让你打字了)。</p>
<p>如果你不想为合并的提交编写信息,也可以考虑使用命令git commit --fixup。这种情况下,你可能会使用commit --fixup,因为你仅希望在rebase中使用之前的提交信息。</p>
<p>rebase --autosquash –i将会启动rebase交互编辑器,编辑器会列出任何已完成的squash!和fixup!提交,如下图:</p>
<p><img src="/assets/img/git/undo-5.png" alt="undo-5" /></p>
<p>当使用--squash和–fixup时,你或许记不清你想修复的某个提交的SHA码——只知道它可能在一个或五个提交之前。你或许可以使用Git的^和~操作符手动找回。HEAD^表示HEAD的前一次提交。HEAD~4表示HEAD前的4次提交,加起来总共是前5次提交。</p>
<h2>十、停止跟踪一个已被跟踪的文件 Stop tracking a tracked file</h2>
<hr />
<p><strong>场景</strong>:你意外将application.log添加到仓库中,现在你每次运行程序,Git都提示application.log中有unstaged的提交。你在.gitignore中写上”*.log”,但仍旧没用——怎样告诉Git“撤销”跟踪这个文件的变化呢?</p>
<p>使用撤销命令: <code>git rm --cached application.log</code></p>
<p>发生了什么:尽管.gitignore阻止Git跟踪文件的变化,甚至是之前没被跟踪的文件是否存在,但是,一旦文件被add或者commit,Git会开始持续跟踪这个文件的变化。类似的,如果你用git add –f来“强制”add,或者覆盖.gitignore,Git还是会继续监视变化。所以以后最好不要使用–f来add .gitignore文件。</p>
<p>如果你希望移除那些应当被忽略的文件,git rm –cached可以帮助你,并将这些文件保留在磁盘上。因为这个文件现在被忽略了,你将不会在git status中看到它,也不会再把这个文件commit了。</p>
<p>以上就是如何在Git上撤销的方法。如果你想学习更多Git命令用法,可以移步下面相关的文档:</p>
<ul>
<li><p><a href="http://git-scm.com/docs/git-checkout">checkout</a></p></li>
<li><p><a href="http://git-scm.com/docs/git-commit">commit</a></p></li>
<li><p><a href="http://git-scm.com/docs/git-rebase">rebase</a></p></li>
<li><p><a href="http://git-scm.com/docs/git-reflog">reflog</a></p></li>
<li><p><a href="http://git-scm.com/docs/git-reset">reset</a></p></li>
<li><p><a href="http://git-scm.com/docs/git-revert">revert</a></p></li>
<li><p><a href="http://git-scm.com/docs/git-rm">rm</a></p></li>
</ul>
<p>原文地址:<a href="https://github.com/blog/2019-how-to-undo-almost-anything-with-git">Github</a></p>
<p>译文地址:<a href="http://www.jointforce.com/jfperiodical/article/show/796?m=d03">http://www.jointforce.com/jfperiodical/article/show/796?m=d03</a></p>
unity3D的FingerGestures插件2015-06-16T00:00:00+00:00http://jiaxianhua.github.io/unity/2015/06/16/fingergestures<p>FingerGestures是一个unity3D插件,用来处理用户动作,手势。 译自<a href="http://fingergestures.fatalfrog.com/docs/manual:start">FingerGestures官方文档</a></p>
<h2>目录</h2>
<hr />
<ul>
<li><a href="#package_content">FingerGestures包结构</a></li>
<li><a href="#samples-list">FingerGestures例子列表</a></li>
<li><a href="#setting_up">设置场景</a></li>
<li><a href="#tap_gesture">教程:识别一个轻敲手势</a></li>
<li><a href="#detecting_gesture">教程:手势识别器</a></li>
<li><a href="#detecting_tap_gesture">教程:轻击手势识别器</a></li>
<li><a href="#detecting_drag_gesture">教程:拖拽手势识别器</a></li>
<li><a href="#detecting_swipe_gesture">教程:滑动手势识别器</a></li>
<li><a href="#detecting_long_press_gesture">教程:长按手势识别器</a></li>
<li><a href="#detecting_pinch_gesture">教程:缩放手势识别器</a></li>
<li><a href="#detecting_twist_gesture">教程:旋转手势识别器</a></li>
<li><a href="#detecting_custom_gesture">教程:自定义手势识别器</a></li>
<li><a href="#detecting_finger_event">教程:识别手势事件</a></li>
<li><a href="#using_net_event">建议:使用.net代理事件</a></li>
</ul>
<hr />
<h2>fingerGestures包结构 <a name="package_content"></a></h2>
<hr />
<table class="table table-striped table-condensed">
<tbody><tr class="row0">
<th class="col0"> 路径,相对Assets/Plugin/… </th><th class="col1"> 描述 </th>
</tr>
<tr class="row1">
<td class="col0"> FingerGestures/ </td><td class="col1"> 插件的根目录 </td>
</tr>
<tr class="row2">
<td class="col0"> FingerGestures/Prefabs </td><td class="col1"> 可以直接拖放到场景中的预设资源(prefabs)</td>
</tr>
<tr class="row3">
<td class="col0"> FingerGestures/Scripts </td><td class="col1"> 核心脚本和组件</td>
</tr>
<tr class="row4">
<td class="col0"> FingerGestures/Scripts/Gesture Recognizers </td><td class="col1"> 每个手势识别
的脚本</td>
</tr>
<tr class="row5">
<td class="col0"> FingerGestures/Scripts/Finger Event Detectors </td><td class="col1"> 每个触摸事件检测器的脚本 </td>
</tr>
<tr class="row6">
<td class="col0"> FingerGestures/Scripts/Components </td><td class="col1"> 手势识别和触摸事件所需要添加的额外组件</td>
</tr>
<tr class="row7">
<td class="col0"> FingerGestures/Toolbox </td><td class="col1"> FingerGestures 自带的工具箱脚本 </td>
</tr>
<tr class="row8">
<td class="col0"> FingerGestures/Samples.unitypackage </td><td class="col1"> 所有例子的子包 </td>
</tr>
<tr class="row9">
<td class="col0"> FingerGestures/PlayMaker Actions.unitypackage </td><td class="col1"> FingerGestures对PlayMaker扩展的插件 </td>
</tr>
<tr class="row10">
<td class="col0"> Editor/FingerGestures </td><td class="col1"> FingerGestures对编辑器的扩展 </td>
</tr>
</tbody></table>
<p></p>
<h2>FingerGestures例子列表 <a name="samples-list"></a></h2>
<hr />
<ul>
<li><p><strong>Finger Event(鼠标或手指事件)</strong> <br/>
<strong>FingerEventsPart1:</strong> 展示如何通过不同的检测器( FingerEventDetectors )去检测鼠标或者手指的上(down)、下(up),按下不移动(stationary,悬停(hover) 事件。<br/>
<strong>FingerEventsPart2:</strong> 展示如何识别不同鼠标或者手指动作(FingerMotionDetector)。</p></li>
<li><p><strong>Gestures(手势)</strong> <br/>
<strong>BasicGestures:</strong> 识别单击(react to tap),双击(double tap),拖动(drag),长按(long——press),滑动(swipe)等基础手势。<br/>
<strong>PinchAndTwist:</strong> 两个或多个手指同时在触摸屏上挤压(pinch)或扭转(twist)时,触发手势的事件。(PS:通常都是用来缩放或旋转)<br/>
<strong>PointCloudGestures:</strong> 示范如何识别一个点云(point cloud)手势。(PS:通常是指用用户画的图案作为识别)</p></li>
<li><p><strong>Toolbox(工具箱)</strong><br/>
<strong>Camera(放入摄像机的脚本):</strong><br/>
Toolbox-DragView: 展示使用<code>TBDragView</code>脚本,实现拖动视角。<br/>
Toolbox-Orbit: 展示使用<code>TBOrbit</code>脚本,实现围绕目标旋转视角。<br/>
Toolbox-Pan: 展示使用<code>TBPan</code>脚本,实现以自身为轴旋转视角。<br/>
Toolbox-PinchZoom: 展示使用<code>TBPinchZoom</code>脚本,实现变焦。</p>
<p> <strong>Object-Based(放入普通场景对象的脚本):</strong><br/>
Toolbox-Drag: 展示使用<code>TBDrag</code>脚本,实现简单的物体拖动<br/>
Toolbox-Hover: 展示使用<code>TBHoverChangeMaterial</code> 和 <code>TBHoverChangeScale</code>脚本,实现当鼠标或者手指悬停在物体上时候的响应。(PS:类似鼠标放到图标上,图标发亮的效果) <br/>
Toolbox-PinchToScale 展示使用<code>TBPinchToScale</code>脚本,实现缩放物体<br/>
Toolbox-TwistToRotate: 展示使用<code>TBTwistToRotate</code>脚本,实现旋转物体</p></li>
</ul>
<h2>设置场景 <a name="setting_up"></a></h2>
<hr />
<p>需要在场景中实例化一个FingerGesture组件才可使用。 FingerGesture在项目中的作用是管理用户输入和识别手势和鼠标或手指事件。 <br/>
有两种添加方式,一是直接把Plugins\FingerGestures\Prefabs下的<code>FingerGestures</code> prefab文件拖入场景中。二是可以创建一个空物件,然后把<code>FingerGestures</code>组件添加进去。</p>
<p><img src="/assets/img/unity/fingergestures/scene_setup_fingergestures.png" alt="1" /></p>
<p>使用<code>Make Persistent</code>标志可以让使FingerGestures 单例在跨场景后一直有效,所以只要保证它在第一个场景设置就足够。</p>
<h2>教程:识别一个轻敲手势 <a name="tap_gesture"></a></h2>
<hr />
<p>该章节会学习到如何识别一个简单的单击动作,然后到特殊物件的单击动作识别,最后到识别一个三个手指的双击动作。</p>
<ul>
<li><p><strong>初始化</strong><br/>
第一步,如上章节设置;<br/>
第二步,创建一个GameObject 命名为<code>Gestures</code> ;<br/>
第三步,给<code>Gestures</code>添加一个<code>TapRecognizer</code>组件,并保持默认设置,你可以在项目面板搜索到它或者直接打开Component > FingerGestures > Gestures > Tap menu item。</p>
<p> <img src="/assets/img/unity/fingergestures/tut_tap1.png" alt="1" /></p>
<p> TapRecognizer 是其中一种手势识别器,它用于监控用户输入而且当一个有效的单击动作被识别时候工作。<br/>
第四步,创建一个新的C# script 叫做 <code>TapTutorial</code>并添加到第二步创建的<code>Gestures</code>中。</p></li>
<li><p><strong>轻敲屏幕</strong><br/>
第一步,点击TapGestures组件上的<code>Copy Event To Clipboard</code>按钮,它会把TapGesture所需要的时间信号代码copy到黏贴板。<br/>
第二步,粘贴到<code>TapTutorial</code>脚本里,如下:</p></li>
</ul>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">public</span> <span class="k">class</span> <span class="nc">TapTutorial</span> <span class="p">:</span> <span class="n">MonoBehaviour</span>
<span class="p">{</span>
<span class="k">void</span> <span class="nf">OnTap</span><span class="p">(</span> <span class="n">TapGesture</span> <span class="n">gesture</span> <span class="p">)</span>
<span class="p">{</span>
<span class="cm">/<em> your code here </em>/</span>
<span class="p">}</span>
<span class="p">}</span> </code></pre></figure></p>
<pre><code>`OnTap`函数匹配定义在TapRecognizer 组件内的信息名属性,当识别器要识别一个轻敲手势,它会使用unity3d的`SendMessage API`先向Gestures物件内所有的脚本广播`OnTap`信息,只要TapTutorial绑定在该物件上,它的`OnTap`函数就会被调用到。
出于性能考虑,通常使用.net标准的事件模型代替unity3d的SendMessage API。
第三步,修改`OnTop`函数:
</code></pre>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">void</span> <span class="nf">OnTap</span><span class="p">(</span> <span class="n">TapGesture</span> <span class="n">gesture</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="s">"Tap gesture detected at "</span> <span class="p">+</span> <span class="n">gesture</span><span class="p">.</span><span class="n">Position</span> <span class="p">+</span>
<span class="s">". It was sent by "</span> <span class="p">+</span> <span class="n">gesture</span><span class="p">.</span><span class="n">Recognizer</span><span class="p">.</span><span class="n">name</span> <span class="p">);</span>
<span class="p">}</span> </code></pre></figure></p>
<pre><code>`gesture`参数包含着手势事件数据,在上面的代码,我们主要输出了位置和`TapRecognizer`内工作的事件。你还可以在`gesture`参数内获得更多属性,例如通过`gesture.Fingers`获得鼠标或手指相关的手势列表,还有可以通过`gesture.Selection`获得当前是哪个场景被轻敲 。
第四步,可以测试,通过敲不同位置,可以看到debug信息输出。
</code></pre>
<h2>教程:手势识别器 <a name="detecting_gesture"></a></h2>
<hr />
<p>在FingerGesture里,用户的手势都由<code>GestureRecognizers</code>组件来处理,它是顺序处理被识别匹配的用户动作的。</p>
<ul>
<li><p><strong>找到GestureRecognizers</strong> <br/>
每种手势都有自己的脚本,存放脚本的路径在<code>Plugins\FingerGestures\Scripts\Gesture Recognizers</code> 。</p>
<p> <img src="/assets/img/unity/fingergestures/gesture_recognizers_folder.png" alt="1" /> <br/>
你也可以从 <code>Component > FingerGestures > Gestures menu</code>里面找到。 <br/>
<img src="/assets/img/unity/fingergestures/fingergesture_menu_gestures.png" alt="1" /></p></li>
<li><p><strong>基本使用</strong> <br/>
要识别一个特殊手势,你需要:<br/>
1、添加对应的<code>GestureRecognizer</code>组件到场景中的目标物件。 <br/>
2、配置它的属性。 <br/>
3、监听它的手势事件和对应响应。</p>
<p> 作为手势事件通知的一部分,<code>GestureRecognizer</code>传递一个包含相关信息(位置,手指触屏点列表,选择的场景物件,相关的GestureRecognizer等)的事件参数。</p>
<p> 一个手势识别器有以下监听事件的方式: <br/>
1、使用标准的.net 委托事件模型,每一个手势识别器都暴露一个.net事件接口。 <br/>
2、使用unity的<code>SendMessage()</code>函数 :
手势事件将会被广播到游戏对象里所有的组件。 <br/>
手势事件也可以指向当前相关的场景对象,这时候需要把手势识别器配置到 <code>Raycaster</code>组件中才能检测场景对象。</p>
<p> 这取决于你的选择。.net的事件模型较为高效,unity的<code>SendMessage()</code>较为方便。</p></li>
<li><p><strong>属性</strong> <br/>
由同一基类派生出来的各种手势识别器共用一个通用配置和一些函数。例如,我们可以看到<code>TapRecognizer</code>和<code>SwipeRecognizer</code>组件的配置放置在同一个对象里。<br/>
<img src="/assets/img/unity/fingergestures/gesture_recognizer_properties_swipe_tap.png" alt="1" /> <br/>
设置: <br/>
你可以看到,两个组件共用了一部分配置:<code>fingers setup</code>,<code>reset mode</code>,<code>event notification settings</code>,<code>references to additional components</code>... <br/>
同样,每个手势识别器都有自己独特的配置,例如滑动识别器要设置距离阀值、速度、和偏差。而多点触控可以设置最大持续时间等。</p>
<p> 事件信息广播: <br/>
此处使用<code>SendMessage()</code>函数去通知其他系统。你可以使用<code>Message Name</code>属性去指定响应的函数名。 <br/>
通常,<code>Message Target</code>会设置你加入的手势识别器组件。但你也可以设置别的对象。</p>
<p> 组件: <br/>
你可以收到手动指定添加组件。例如:添加一个<code>ScreenRaycaster</code>组件让手势识别器获知场景内对象碰撞。并把消息发送到相应的监听器。它允许识别器转发消息到正在有关联的场景对象。</p></li>
</ul>
<h2>教程:轻击手势识别器 <a name="detecting_tap_gesture"></a></h2>
<hr />
<p><img src="/assets/img/unity/fingergestures/tap_recognizer.png" alt="1" /></p>
<ul>
<li><p><strong>属性</strong> <br/>
<code>Required Taps</code> :连续轻击的次数。 <br/>
<code>Max Delay Between Taps</code> :两次轻击间最大的时间间隔。(秒) <br/>
<code>Movement Tolerance</code>:连续轻敲时候,和第一次轻击的位置相隔的偏差大小。 <br/>
<code>Max Duration</code>:最大可以识别的手指数。</p></li>
<li><p><strong>事件</strong></p></li>
</ul>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">void</span> <span class="nf">OnTap</span><span class="p">(</span> <span class="n">TapGesture</span> <span class="n">gesture</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// 轻击的数量
</span> <span class="kt">int</span> <span class="n">taps</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">Taps</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure></p>
<h2>教程:拖拽手势识别器 <a name="detecting_drag_gesture"></a></h2>
<hr />
<p><img src="/assets/img/unity/fingergestures/drag_recognizer.png" alt="1" /></p>
<ul>
<li><p><strong>属性</strong> <br/>
<code>Movement Tolerance</code>:最小的拖动距离才触发识别器。 <br/>
<code>Apply Same Direction Constraint</code>:只能用于多点拖拽,打开后,如果所有点不是向同一个方向拖拽,识别器将不会识别。</p></li>
<li><p><strong>事件</strong></p></li>
</ul>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">void</span> <span class="nf">OnDrag</span><span class="p">(</span> <span class="n">DragGesture</span> <span class="n">gesture</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// 当前识别器阶段 (Started/Updated/Ended)
</span> <span class="n">ContinuousGesturePhase</span> <span class="n">phase</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">Phase</span><span class="p">;</span></p>
<pre><code><span class="c1">// 最后一帧的拖拽/移动数据
</code></pre>
<p></span> <span class="n">Vector2</span> <span class="n">deltaMove</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">DeltaMove</span><span class="p">;</span></p>
<pre><code><span class="c1">//完整的拖拽数据
</code></pre>
<p></span> <span class="n">Vector2</span> <span class="n">totalMove</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">TotalMove</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure></p>
<h2>教程:滑动手势识别器 <a name="detecting_swipe_gesture"></a></h2>
<hr />
<p><img src="/assets/img/unity/fingergestures/swipe_recognizer.png" alt="1" /></p>
<ul>
<li><p><strong>属性</strong> <br/>
<code>Min Distance</code>: 必须滑动的最小距离。 <br/>
<code>Max Distance</code>:允许滑动的最大距离。 <br/>
<code>Min Velocity</code>:滑动时候最小速度。<br/>
<code>Max Deviation</code>:允许的最大角度偏差。(度)</p></li>
<li><p><strong>事件</strong></p></li>
</ul>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">void</span> <span class="nf">OnSwipe</span><span class="p">(</span> <span class="n">SwipeGesture</span> <span class="n">gesture</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// 完整的滑动数据
</span> <span class="n">Vector2</span> <span class="n">move</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">Move</span><span class="p">;</span></p>
<pre><code><span class="c1">// 滑动的速度
</code></pre>
<p></span> <span class="kt">float</span> <span class="n">velocity</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">Velocity</span><span class="p">;</span></p>
<pre><code><span class="c1">// 大概的滑动方向
</code></pre>
<p></span> <span class="n">FingerGestures</span><span class="p">.</span><span class="n">SwipeDirection</span> <span class="n">direction</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">Direction</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure></p>
<h2>教程:长按手势识别器 <a name="detecting_long_press_gesture"></a></h2>
<hr />
<p><img src="/assets/img/unity/fingergestures/longpress_recognizer.png" alt="1" /></p>
<ul>
<li><p><strong>属性</strong> <br/>
<code>Press Duration</code>:最少长按时间。 <br/>
<code>Move Tolerance</code>:长按过程中允许的最大移动偏差。</p></li>
<li><p><strong>事件</strong></p></li>
</ul>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">void</span> <span class="nf">OnLongPress</span><span class="p">(</span> <span class="n">LongPressGesture</span> <span class="n">gesture</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// 长按持续时间
</span> <span class="kt">float</span> <span class="n">elapsed</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">ElapsedTime</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure></p>
<h2>教程:缩放手势识别器 <a name="detecting_pinch_gesture"></a></h2>
<hr />
<p><img src="/assets/img/unity/fingergestures/pinch_recognizer.png" alt="1" /></p>
<ul>
<li><p><strong>属性</strong> <br/>
<code>Minimum DOT</code> :允许的小向量点积。 <br/>
<code>Minimum Distance</code>:两个手指第一次触屏时候允许的最短路径。</p></li>
<li><p><strong>事件</strong></p></li>
</ul>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">void</span> <span class="nf">OnPinch</span><span class="p">(</span> <span class="n">PinchGesture</span> <span class="n">gesture</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// 识别器当前状态(Started/Updated/Ended)
</span> <span class="n">ContinuousGesturePhase</span> <span class="n">phase</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">Phase</span><span class="p">;</span></p>
<pre><code><span class="c1">// 当前两个手指的距离
</code></pre>
<p></span> <span class="kt">float</span> <span class="n">gap</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">Gap</span><span class="p">;</span></p>
<pre><code><span class="c1">// 当前与上一帧的变动值
</code></pre>
<p></span> <span class="kt">float</span> <span class="n">delta</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">Delta</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure></p>
<h2>教程:旋转手势识别器 <a name="detecting_twist_gesture"></a></h2>
<hr />
<p><img src="/assets/img/unity/fingergestures/twist_recognizer.png" alt="1" /></p>
<ul>
<li><p><strong>属性</strong> <br/>
<code>Minimum DOT</code> :允许的小向量点积。 <br/>
<code>Minimum Rotation</code>:必须的最小自旋角度。</p></li>
<li><p><strong>事件</strong></p></li>
</ul>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">void</span> <span class="nf">OnTwist</span><span class="p">(</span> <span class="n">TwistGesture</span> <span class="n">gesture</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">// 识别器当前状态 (Started/Updated/Ended)
</span> <span class="n">ContinuousGesturePhase</span> <span class="n">phase</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">Phase</span><span class="p">;</span></p>
<pre><code><span class="c1">// 最近一次角度变化(度)
</code></pre>
<p></span> <span class="kt">float</span> <span class="n">delta</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">DeltaRotation</span><span class="p">;</span></p>
<pre><code><span class="c1">// 总的角度变化(度)
</code></pre>
<p></span> <span class="kt">float</span> <span class="n">total</span> <span class="p">=</span> <span class="n">gesture</span><span class="p">.</span><span class="n">TotalRotation</span><span class="p">;</span>
<span class="p">}</span> </code></pre></figure></p>
<ul>
<li><strong>桌面仿真</strong> <br/>
在桌面环境,你可以通过<code>left-CTRL</code>键加上鼠标转轮去调节角度。也可以在<code>Mouse Input Provider</code>配置别的按键。</li>
</ul>
<h2>教程:自定义手势识别器 <a name="detecting_custom_gesture"></a></h2>
<hr />
<p>自从FingerGestures 3.0之后,可以通过<code>PointCloudRecognizer</code>识别自定义手势。利用基于<a href="http://depts.washington.edu/aimgroup/proj/dollar/pdollar.html">$P recognizer</a> 是手势匹配算法实现。现在只支持单手指操作的识别,将来会支持多点自定义手势。 <br/>
<img src="/assets/img/unity/fingergestures/customgesturesample.png" alt="1" /> <br/>
点云识别器会对比用户输入和已经设置好的手势模板,然后会返回最近接近的匹配结果,会返回匹配得分和差距值。 <br/>
点云识别器是规模和方向固定不变的,这就意味着它可以识别画得比较大或者小的,也或者是反方向的(李若:从左到右变成从右到左)。</p>
<ul>
<li><p><strong>点云识别器模板</strong> <br/>
一个模板包括要识别的手势的数据。是通过一个编辑器编辑的。 <br/>
<img src="/assets/img/unity/fingergestures/pointcloud_template_loop.png" alt="1" /> <br/>
创建一个模板需要以下步骤: <br/>
1:在你的项目编码,右击-> create ->PonitCloud Gesture Template <br/>
<img src="/assets/img/unity/fingergestures/create_pointcloud_template.png" alt="1" /> <br/>
一个新的模板就好添加到项目里面,可以自己重命名。 <br/>
2:选择模板然后在 Inspecrot 面板内点击 Edit。 <br/>
<img src="/assets/img/unity/fingergestures/new_pointcloud_template.png" alt="1" /> <br/>
3:然后开始画图案。 <br/>
<img src="/assets/img/unity/fingergestures/gesture_template_inspector_updated.png" alt="1" /></p></li>
<li><p><strong>使用点云识别器</strong> <br/>
第一步: <br/>
1:保证场景对象已经设置好了finger gesture的属性。 <br/>
2:创建一个新的<code>Gestures</code>对象。 <br/>
3:添加一个<code>PointCloudRecognizer</code>组件。 <br/>
<img src="/assets/img/unity/fingergestures/pointcloudrecognizer1.png" alt="1" /> <br/>
以下属性需要特别注意。 <br/>
<code>Max Match Distance</code>:控制识别的精确的程度。数值越低,越精确。 <br/>
<code>Sampling Distance</code>: 连贯动作取样时候,两点间隔的最小距离。越小越精确,但是取样会更多。 <br/>
<code>Gesture Templates List</code>:我们指定的模板列表。</p>
<p> 第二步: <br/>
添加刚刚创建的模板拖放到手势模板列表中。 <br/>
<img src="/assets/img/unity/fingergestures/pointcloudrecognizer2.png" alt="1" /></p>
<p> 第三步: <br/>
1、创建一个c#文件,此处命名为<code>PointCloudTutorial.cs</code>。 <br/>
2、在<code>PointCloudRecognizer</code> 下面创建一个手势对象。 <br/>
3、编辑c#文件:</p></li>
</ul>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">public</span> <span class="k">class</span> <span class="nc">PointCloudTutorial</span> <span class="p">:</span> <span class="n">MonoBehaviour</span>
<span class="p">{</span>
<span class="k">void</span> <span class="nf">OnCustomGesture</span><span class="p">(</span> <span class="n">PointCloudGesture</span> <span class="n">gesture</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="s">"Recognized custom gesture: "</span> <span class="p">+</span> <span class="n">gesture</span><span class="p">.</span><span class="n">RecognizedTemplate</span><span class="p">.</span><span class="n">name</span> <span class="p">+</span>
<span class="s">", match score: "</span> <span class="p">+</span> <span class="n">gesture</span><span class="p">.</span><span class="n">MatchScore</span> <span class="p">+</span>
<span class="s">", match distance: "</span> <span class="p">+</span> <span class="n">gesture</span><span class="p">.</span><span class="n">MatchDistance</span> <span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure></p>
<pre><code>手势事件保护下面几个重要属性:
`gesture.RecognizedTemplate`: 被认为是最佳匹配的手势模板。
`gesture.MatchScore`:一个百分比的值,表示匹配的程度。
`gesture.MatchDistance`:一个测量绝对值,表示匹配程度。
你还可以使用其他手势的属性。 例如位置和选择对象等属性。
</code></pre>
<ul>
<li><strong>用代码创建模板</strong> <br/>
你可以使用api字自己的编辑器扩展中在运行时候创建手势模板。</li>
</ul>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">void</span> <span class="nf">Awake</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">PointCloudGestureTemplate</span> <span class="n">triangle</span> <span class="p">=</span> <span class="n">ScriptableObject</span><span class="p">.</span><span class="n">CreateInstance</span><span class="p"><</span><span class="n">PointCloudGestureTemplate</span><span class="p">>();</span>
<span class="n">triangle</span><span class="p">.</span><span class="n">name</span> <span class="p">=</span> <span class="s">"Triangle Gesture Template"</span><span class="p">;</span>
<span class="n">triangle</span><span class="p">.</span><span class="nf">BeginPoints</span><span class="p">();</span>
<span class="n">triangle</span><span class="p">.</span><span class="nf">AddPoint</span><span class="p">(</span> <span class="m">0</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">1</span> <span class="p">);</span>
<span class="n">triangle</span><span class="p">.</span><span class="nf">AddPoint</span><span class="p">(</span> <span class="m">0</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">2</span> <span class="p">);</span>
<span class="n">triangle</span><span class="p">.</span><span class="nf">AddPoint</span><span class="p">(</span> <span class="m">0</span><span class="p">,</span> <span class="m">3</span><span class="p">,</span> <span class="m">1</span> <span class="p">);</span>
<span class="n">triangle</span><span class="p">.</span><span class="nf">AddPoint</span><span class="p">(</span> <span class="m">0</span><span class="p">,</span> <span class="m">1</span><span class="p">,</span> <span class="m">1</span> <span class="p">);</span>
<span class="n">triangle</span><span class="p">.</span><span class="nf">EndPoints</span><span class="p">();</span></p>
<pre><code><span class="n">PointCloudGestureTemplate</span> <span class="n">square</span> <span class="p">=</span> <span class="n">ScriptableObject</span><span class="p">.</span><span class="n">CreateInstance</span><span class="p">&lt;</span><span class="n">PointCloudGestureTemplate</span><span class="p">&gt;();</span>
<span class="n">square</span><span class="p">.</span><span class="n">name</span> <span class="p">=</span> <span class="s">"Square Gesture Template"</span><span class="p">;</span>
<span class="n">square</span><span class="p">.</span><span class="nf">BeginPoints</span><span class="p">();</span>
<span class="n">square</span><span class="p">.</span><span class="nf">AddPoint</span><span class="p">(</span> <span class="m">0</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">1</span> <span class="p">);</span>
<span class="n">square</span><span class="p">.</span><span class="nf">AddPoint</span><span class="p">(</span> <span class="m">0</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">3</span> <span class="p">);</span>
<span class="n">square</span><span class="p">.</span><span class="nf">AddPoint</span><span class="p">(</span> <span class="m">0</span><span class="p">,</span> <span class="m">4</span><span class="p">,</span> <span class="m">3</span> <span class="p">);</span>
<span class="n">square</span><span class="p">.</span><span class="nf">AddPoint</span><span class="p">(</span> <span class="m">0</span><span class="p">,</span> <span class="m">4</span><span class="p">,</span> <span class="m">1</span> <span class="p">);</span>
<span class="n">square</span><span class="p">.</span><span class="nf">AddPoint</span><span class="p">(</span> <span class="m">0</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">1</span> <span class="p">);</span>
<span class="n">square</span><span class="p">.</span><span class="nf">EndPoints</span><span class="p">();</span>
<span class="n">PointCloudRegognizer</span> <span class="n">recognizer</span> <span class="p">=</span> <span class="n">gameObject</span><span class="p">.</span><span class="n">AddComponent</span><span class="p">&lt;</span><span class="n">PointCloudRegognizer</span><span class="p">&gt;();</span>
<span class="n">recognizer</span><span class="p">.</span><span class="nf">AddTemplate</span><span class="p">(</span> <span class="n">triangle</span> <span class="p">);</span>
<span class="n">recognizer</span><span class="p">.</span><span class="nf">AddTemplate</span><span class="p">(</span> <span class="n">square</span> <span class="p">);</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<pre><code>第一个参数`AddPoint `是一个笔画的顺序,该api暂时只支持单线笔画的手势。
当`EndPoints() `调用时候,手势模板会被格式化,所有的点都会重新绘制成0到1范围的数。
</code></pre>
<h2>教程:识别手指事件 <a name="detecting_finger_event"></a></h2>
<hr />
<p>FingerGestures 可以识别向上,向下,悬停,移动,长按等单点输入手势。各种<code>FingerEventDetector</code>组件用于识别对应的手指事件,与
<code>GestureRecognizers</code>类似,都是通过广播信息去触发。</p>
<ul>
<li><strong>FingerEventDetector</strong> <br/>
所有的手指事件识别器都派生与一个基础抽象类。通常,每个<code>FingerEventDetector</code>实例监控着所有手指事件信号。也可以配置<code>Finger Index Filter</code>属性,让其只跟踪特定的手指事件。 <br/>
和手势识别器一样,手指事件识别器传递一个事件数据对象,改该对象派生于<code>FingerEvent</code>类,包含以下属性:</li>
</ul>
<table class="table table-striped table-condensed">
<tbody><tr class="row0">
<th class="col0"> 属性</th><th class="col1"> 类型</th><th class="col2"> 描述</th>
</tr>
<tr class="row1">
<td class="col0"> Name </td><td class="col1"> string </td><td class="col2"> 消息的名字 </td>
</tr>
<tr class="row2">
<td class="col0"> Detector </td><td class="col1"> FingerEventDetector</td><td class="col2"> 该次事件中的手指事件识别器</td>
</tr>
<tr class="row3">
<td class="col0"> Finger </td><td class="col1"> FingerGestures.Finger</td><td class="col2"> 该次事件中的手指类</td>
</tr>
<tr class="row4">
<td class="col0"> Position</td><td class="col1"> Vector2</td><td class="col2">事件所发生的位置 </td>
</tr>
<tr class="row5">
<td class="col0"> Selection </td><td class="col1"> GameObject </td><td class="col2"> 被选中游戏对象 (依赖`ScreenRaycaster `组件)</td>
</tr>
<tr class="row6">
<td class="col0">Hit</td><td class="col1"> RaycastHit</td><td class="col2"> 光线投射碰撞,由`ScreenRaycaster`提供,在正常显示上非常有用</td>
</tr>
</tbody></table>
<p></p>
<ul>
<li><strong>FingerUpDetector</strong></li>
</ul>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">void</span> <span class="nf">OnFingerUp</span><span class="p">(</span> <span class="n">FingerUpEvent</span> <span class="n">e</span> <span class="p">)</span>
<span class="p">{</span>
<span class="c1">//手指已经持续的时间
</span> <span class="kt">float</span> <span class="n">elapsedTime</span> <span class="p">=</span> <span class="n">e</span><span class="p">.</span><span class="n">TimeHeldDown</span><span class="p">;</span>
<span class="p">}</span> </code></pre></figure></p>
<ul>
<li><strong>FingerHoverDetector</strong></li>
</ul>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">void</span> <span class="nf">OnFingerHover</span><span class="p">(</span> <span class="n">FingerHoverEvent</span> <span class="n">e</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">e</span><span class="p">.</span><span class="n">Phase</span> <span class="p">==</span> <span class="n">FingerHoverPhase</span><span class="p">.</span><span class="n">Enter</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Finger</span> <span class="p">+</span> <span class="s">" entered object: "</span> <span class="p">+</span> <span class="n">e</span><span class="p">.</span><span class="n">Selection</span> <span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="k">if</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Phase</span> <span class="p">==</span> <span class="n">FingerHoverPhase</span><span class="p">.</span><span class="n">Exit</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Finger</span> <span class="p">+</span> <span class="s">" exited object: "</span> <span class="p">+</span> <span class="n">e</span><span class="p">.</span><span class="n">Selection</span> <span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span> </code></pre></figure></p>
<ul>
<li><strong>FingerMotionDetector</strong> <br/>
该识别器能够识别两种事件。 <br/>
1、OnFingerMove :当手指位置距离上一帧位置有发生变化。 <br/>
2、OnFingerStationary :当手指与上一帧位置一样。</li>
</ul>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">void</span> <span class="nf">OnFingerMove</span><span class="p">(</span> <span class="n">FingerMotionEvent</span> <span class="n">e</span> <span class="p">)</span>
<span class="p">{</span>
<span class="kt">float</span> <span class="n">elapsed</span> <span class="p">=</span> <span class="n">e</span><span class="p">.</span><span class="n">ElapsedTime</span><span class="p">;</span></p>
<pre><code><span class="k">if</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Phase</span> <span class="p">==</span> <span class="n">FingerMotionPhase</span><span class="p">.</span><span class="n">Started</span> <span class="p">)</span>
<span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Finger</span> <span class="p">+</span> <span class="s">" started moving at "</span> <span class="p">+</span> <span class="n">e</span><span class="p">.</span><span class="n">Position</span><span class="p">);</span>
<span class="k">else</span> <span class="k">if</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Phase</span> <span class="p">==</span> <span class="n">FingerMotionPhase</span><span class="p">.</span><span class="n">Updated</span> <span class="p">)</span>
<span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Finger</span> <span class="p">+</span> <span class="s">" moving at "</span> <span class="p">+</span> <span class="n">e</span><span class="p">.</span><span class="n">Position</span> <span class="p">);</span>
<span class="k">else</span> <span class="k">if</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Phase</span> <span class="p">==</span> <span class="n">FingerMotionPhase</span><span class="p">.</span><span class="n">Ended</span> <span class="p">)</span>
<span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Finger</span> <span class="p">+</span> <span class="s">" stopped moving at "</span> <span class="p">+</span> <span class="n">e</span><span class="p">.</span><span class="n">Position</span> <span class="p">);</span>
</code></pre>
<p><span class="p">}</span></p>
<p><span class="k">void</span> <span class="nf">OnFingerStationary</span><span class="p">(</span> <span class="n">FingerMotionEvent</span> <span class="n">e</span> <span class="p">)</span>
<span class="p">{</span>
<span class="kt">float</span> <span class="n">elapsed</span> <span class="p">=</span> <span class="n">e</span><span class="p">.</span><span class="n">ElapsedTime</span><span class="p">;</span></p>
<pre><code><span class="k">if</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Phase</span> <span class="p">==</span> <span class="n">FingerMotionPhase</span><span class="p">.</span><span class="n">Started</span> <span class="p">)</span>
<span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Finger</span> <span class="p">+</span> <span class="s">" started stationary state at "</span> <span class="p">+</span> <span class="n">e</span><span class="p">.</span><span class="n">Position</span> <span class="p">);</span>
<span class="k">else</span> <span class="k">if</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Phase</span> <span class="p">==</span> <span class="n">FingerMotionPhase</span><span class="p">.</span><span class="n">Updated</span> <span class="p">)</span>
<span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Finger</span> <span class="p">+</span> <span class="s">" is still stationary at "</span> <span class="p">+</span> <span class="n">e</span><span class="p">.</span><span class="n">Position</span> <span class="p">);</span>
<span class="k">else</span> <span class="k">if</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Phase</span> <span class="p">==</span> <span class="n">FingerMotionPhase</span><span class="p">.</span><span class="n">Ended</span> <span class="p">)</span>
<span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="n">e</span><span class="p">.</span><span class="n">Finger</span> <span class="p">+</span> <span class="s">" stopped being stationary at "</span> <span class="p">+</span> <span class="n">e</span><span class="p">.</span><span class="n">Position</span> <span class="p">);</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<h2>建议:使用.net代理事件 <a name="using_net_event"></a></h2>
<hr />
<p>当使用unity的<code>SendMessage()</code>函数广播事件消息非常方便,但是效率低而且不够.NET代理事件灵活。 <br/>
* <strong>Gesture Events</strong> <br/>
每个手势识别器都暴露一个公共的<code>OnGesture</code>.NET事件,可以匹配手势事件和手指事件。用法跟用<code>SendMessage()</code>一样。</p>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="na">[RequireComponent( typeof( TapGesture ) )]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">TapTutorial</span> <span class="p">:</span> <span class="n">MonoBehaviour</span>
<span class="p">{</span>
<span class="k">void</span> <span class="nf">Start</span><span class="p">()</span>
<span class="p">{</span>
<span class="c1">// 在对象里面寻找轻击事件识别器
</span> <span class="n">TapRecognizer</span> <span class="n">tap</span> <span class="p">=</span> <span class="n">GetComponent</span><span class="p"><</span><span class="n">TapRecognizer</span><span class="p">>();</span></p>
<pre><code> <span class="c1">// 订阅它的.NET事件
</code></pre>
<p></span> <span class="n">tap</span><span class="p">.</span><span class="n">OnGesture</span> <span class="p">+=</span> <span class="n">MyTapEventHandler</span><span class="p">;</span>
<span class="p">}</span></p>
<pre><code><span class="k">void</span> <span class="nf">MyTapEventHandler</span><span class="p">(</span> <span class="n">TapGesture</span> <span class="n">gesture</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="s">"Tap detected at "</span> <span class="p">+</span> <span class="n">gesture</span><span class="p">.</span><span class="n">Position</span> <span class="p">);</span>
<span class="p">}</span>
</code></pre>
<p><span class="p">}</span> </code></pre></figure></p>
<p>有时候你需要停止监听事件。你可以用以下办法:</p>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="n">tap</span><span class="p">.</span><span class="n">OnGesture</span> <span class="p">-=</span> <span class="n">MyTapEventHandler</span><span class="p">;</span> </code></pre></figure></p>
<p>注意停止监听事件时候相关对象的生命周期,有可能会导致内存泄露,这是.NET代理事件的陷阱。</p>
<p>另外一种方法是,<code>FingerGestures</code>单例暴露一个全局的<code>OnGestureEvent</code>钩子,可以监听到任何手势事件。</p>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="k">void</span> <span class="nf">Start</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">FingerGestures</span><span class="p">.</span><span class="n">OnGestureEvent</span> <span class="p">+=</span> <span class="n">FingerGestures_OnGestureEvent</span><span class="p">;</span>
<span class="p">}</span></p>
<p><span class="k">void</span> <span class="nf">FingerGestures_OnGestureEvent</span><span class="p">(</span> <span class="n">Gesture</span> <span class="n">gesture</span> <span class="p">)</span>
<span class="p">{</span>
<span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="n">gesture</span><span class="p">.</span><span class="n">Recognizer</span><span class="p">.</span><span class="n">name</span> <span class="p">+</span> <span class="s">" fired its gesture event"</span> <span class="p">);</span></p>
<pre><code><span class="k">if</span><span class="p">(</span> <span class="n">gesture</span> <span class="k">is</span> <span class="n">TapGesture</span> <span class="p">)</span>
<span class="n">Debug</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span> <span class="s">"Tapped: "</span> <span class="p">+</span> <span class="p">((</span><span class="n">TapGesture</span><span class="p">)</span><span class="n">gesture</span><span class="p">).</span><span class="n">Taps</span> <span class="p">);</span>
</code></pre>
<p><span class="p">}</span></code></pre></figure></p>
<ul>
<li><strong>Finger Event</strong> <br/>
跟上面类似用法。</li>
</ul>
<p><figure class="highlight"><pre><code class="language-c#" data-lang="c#"><span class="n">FingerUpDetector</span><span class="p">.</span><span class="nf">OnFingerUp</span><span class="p">(</span> <span class="n">FingerUpEvent</span> <span class="n">e</span> <span class="p">)</span>
<span class="n">FingerDownDetector</span><span class="p">.</span><span class="nf">OnFingerDown</span><span class="p">(</span> <span class="n">FingerDownEvent</span> <span class="n">e</span> <span class="p">)</span>
<span class="n">FingerHoverDetector</span><span class="p">.</span><span class="nf">OnFingerHover</span><span class="p">(</span> <span class="n">FingerHoverEvent</span> <span class="n">e</span> <span class="p">)</span>
<span class="n">FingerMotionDetector</span><span class="p">.</span><span class="nf">OnFingerMove</span><span class="p">(</span> <span class="n">FingerMotionEvent</span> <span class="n">e</span> <span class="p">)</span>
<span class="n">FingerMotionDetector</span><span class="p">.</span><span class="nf">OnFingerStationary</span><span class="p">(</span> <span class="n">FingerMotionEvent</span> <span class="n">e</span> <span class="p">)</span>
<span class="n">FingerGestures</span><span class="p">.</span><span class="nf">OnFingerEvent</span><span class="p">(</span> <span class="n">FingerEvent</span> <span class="n">e</span> <span class="p">)</span> </code></pre></figure></p>
<p>原文:<a href="http://dp0304.com/unity3d/2013/07/28/fingergestures/">http://dp0304.com/unity3d/2013/07/28/fingergestures/</a></p>
矩阵2015-06-10T00:00:00+00:00http://jiaxianhua.github.io/math/2015/06/10/matrix<p>矩阵是3D数学的重要基础。它主要用来描述两个坐标系统间的关系,通过定义一种运算而将一个坐标系中的向量转换到另一个坐标系中。</p>
<h2>矩阵-数学定义</h2>
<hr />
<p>线性代数中,<strong>矩阵</strong>就是以行和列形式组织的矩形数字块</p>
<ul>
<li>向量是标量的数组</li>
<li>矩阵是向量的数组</li>
</ul>
<h3>矩阵的维度和记法</h3>
<hr />
<p>向量的维度定义为:</p>
<p><code>它所包含的数的个数。</code></p>
<p>矩阵的维度定义为:</p>
<p><code>它包含了多少行和多少列。</code></p>
<p>一个<em>rXc</em>的矩阵有<em>r</em>行、<em>c</em>列。</p>
<p>下面是一个4X3矩阵的例子:</p>
<p><img src="/assets/img/math/matrix-1.png" alt="matrix-1" /></p>
<p>这个4X3矩阵展示了矩阵的标准记法。将数字排列成一个方块,用方括号括起来。</p>
<blockquote><p>注意,有些地方可能用圆括号而不是方括号来包围这个方块。而且另外一些人则使用竖线,这里保留竖线记法,但将它用在一个与矩阵相关却完全不同的概念上:矩阵行列式。</p></blockquote>
<p>用黑体大写字母表示矩阵:如<strong>M,A,R</strong>。需要引用矩阵的分量时,采用下标法,常使用对应的斜体小写字母。如下面的3X3矩阵所示:</p>
<p><img src="/assets/img/math/matrix-2.png" alt="matrix-2" /></p>
<p><em>m(ij)</em>表示<strong>M</strong>的第<em>i</em>行第<em>j</em>列元素。矩阵的下标从<code>1</code>开始,所以第一行和第一列都用数字<code>1</code>.</p>
<h3>方阵</h3>
<hr />
<p>行数和列数相同的矩阵称作<strong>方阵</strong>。</p>
<p>方阵的<strong>对角线元素</strong>就是方阵中行号和列号相同的元素。</p>
<p>例如,3X3矩阵<strong>M</strong>的对角线元素为<em>m(11),m(22),m(33)</em>。</p>
<p><img src="/assets/img/math/matrix-3.png" alt="matrix-3" /></p>
<p>其它元素均为<strong>非对角线元素</strong>。简单的说,方阵的对角线元素就是方阵对角线上的元素。</p>
<p>如果所有非对角线元素都为0,那么称这种矩阵为<strong>对角</strong>矩阵。例如:</p>
<p><img src="/assets/img/math/matrix-4.png" alt="matrix-4" /></p>
<p><strong>单位矩阵</strong>是一种特殊的对角矩阵。<em>n</em>维单位矩阵记作<em>I(n)</em>,是nXn矩阵,对角线元素为1,其它元素为0。例如,3X3单位矩阵:</p>
<p><img src="/assets/img/math/matrix-5.png" alt="matrix-5" /></p>
<p>3D单位矩阵</p>
<p>通常,上下文会说明特定情况下单位矩阵的维数,此时少省略下标直接记单位矩阵为<strong>I</strong>。</p>
<p><strong>单位矩阵</strong>非常特殊,因为它是矩阵的<code>乘法单位元</code>。其基本性质是用任意一个矩阵乘以单位矩阵,都将得到原矩阵。所以,在某种意义上,单位矩阵对矩阵的作用就犹如<strong>1</strong>对于标量的作用。</p>
<h3>向量作为矩阵使用</h3>
<hr />
<p>矩阵的行数和列数可以是任意正整数,当然也包括1。</p>
<p>向量:一行或一列的矩阵。</p>
<p>一个n向量能被当作<em>1Xn</em>矩阵或<em>nX1</em>矩阵。<em>1Xn</em>矩阵被称作<code>行向量</code>,<em>nX1</em>矩阵称作<code>列向量</code>。行向量平着写,列向量竖着写,例如:</p>
<p><img src="/assets/img/math/matrix-6.png" alt="matrix-6" /></p>
<h3>转置</h3>
<hr />
<p>考虑一个<em>rXc</em>的矩阵<strong>M</strong>。<strong>M</strong>的转置记作M(T)。</p>
<p><img src="/assets/img/math/matrix-7.png" alt="matrix-7" /></p>
<p>转置矩阵</p>
<p>对于向量来说,转置将使行向量变成列向量,使列向量成为行向量。</p>
<p><img src="/assets/img/math/matrix-8.png" alt="matrix-8" /></p>
<p>行向量和列向量之间的转置</p>
<p><img src="/assets/img/math/matrix-9.png" alt="matrix-9" /></p>
<h3>标量和矩阵的乘法</h3>
<hr />
<p>矩阵<strong>M</strong>能和标量<em>k</em>相乘,结果是一个和<strong>M</strong>维数相同的矩阵。</p>
<p><img src="/assets/img/math/matrix-10.png" alt="matrix-10" /></p>
<p>标量乘以3X3矩阵</p>
<h3>矩阵乘法</h3>
<hr />
<p>一个<em>rXn</em>矩阵<strong>A</strong>能够乘以<em>nXr</em>矩阵<strong>B</strong>,结果是一个<em>rXc</em>矩阵,记作<strong>AB</strong>。</p>
<p>例如,设<strong>A</strong>为4X2矩阵,<strong>B</strong>为2X5矩阵,那么结果<strong>AB</strong>为4X5矩阵:</p>
<p><img src="/assets/img/math/matrix-11.png" alt="matrix-11" /></p>
<p>矩阵乘法计算如下:</p>
<pre><code>记rXn矩阵A与nXc矩阵B的积rXc矩阵AB为C。C的任意元素C(ij)等于A的第i行向量与B的和j行微量的点乘结果
</code></pre>
<p>正式定义为:</p>
<p><img src="/assets/img/math/matrix-12.png" alt="matrix-12" /></p>
<p>对结果中的任意元素C(ij),取A的第i行和B的第j列,将行和列中对应元素相乘,然后将结果相加(等于A的i行和B的j列的点积)。C(ij)就等于这个和。</p>
<p>下面展示了怎样计算C(24):</p>
<p><img src="/assets/img/math/matrix-13.png" alt="matrix-13" /></p>
<p>C的第2行第4列的元素等于A的第2行和B的第4列的点积。</p>
<p>另一种帮助记忆这个法则的方法是将B写在C上面。这种写法的目的是使A的行和B的列与C中的对应元素对齐。</p>
<p><img src="/assets/img/math/matrix-14.png" alt="matrix-14" /></p>
<p>对于几何中的应用,特别关注方阵相乘--特别是2X2矩阵和3X3乱加的情况。</p>
<p><img src="/assets/img/math/matrix-15.png" alt="matrix-15" /></p>
<p>2X2矩阵的乘法</p>
<p>实数2X2矩阵的示例:</p>
<p><img src="/assets/img/math/matrix-16-1.png" alt="matrix-16-1" /></p>
<p><img src="/assets/img/math/matrix-16-2.png" alt="matrix-16-2" /></p>
<p>下面公式给出了3X3矩阵的情况:</p>
<p><img src="/assets/img/math/matrix-17.png" alt="matrix-17" /></p>
<p>3X3矩阵的乘法</p>
<p>实数3X3矩阵的示例:</p>
<p><img src="/assets/img/math/matrix-18.png" alt="matrix-18" /></p>
<h3>向量与矩阵的乘法</h3>
<hr />
<p>因为向量能被当作是一行或一列的矩阵,所以能够与矩阵相乘。</p>
<p>这里行向量和列向量的区别非常重要。</p>
<p>下面公式展示了3D行、列向量如何左乘、右乘3X3矩阵:</p>
<p><img src="/assets/img/math/matrix-19.png" alt="matrix-19" /></p>
<ul>
<li>行向量左乘矩阵时,结果是行向量。</li>
<li>列向量右乘矩阵时,结果是列向量。</li>
</ul>
<p>关于矩阵和向量相乘的注意事项:</p>
<ul>
<li>结果向量中的每个元素都是原向量与矩阵中单独行或列的点积。</li>
<li>矩阵中的各元素决定了输入向量中特定元素在输出向量中占的比重。如:m(11)决定了输入<em>x</em>对输出<em>x</em>值的贡献。</li>
<li>矩阵---向量乘法满足对向量加法的分配律。对于向量v、w和矩阵<strong>M</strong>,有:(v+w)M=vM+wM</li>
</ul>
<h3>行向量与列向量</h3>
<hr />
<p>使用行向量依据。</p>
<ul>
<li>在文字中行向量形式更好一些。列向量书写麻烦,可用<strong>行向量转置的形式书写列向量</strong>。</li>
<li>行向量左乘矩阵形式更加方便。</li>
<li>DirectX使用行向量。</li>
</ul>
<p>列向量依据。</p>
<ul>
<li>等式中使用列向量形式更好。</li>
<li>线性代数书中多使用列向量。</li>
<li>多本计算机图形学<strong>圣经</strong>使用列向量。</li>
<li>OpenGL使用列向量。</li>
</ul>
<p>3D数学编程中,形式转换经常是错误的根源。</p>
<h2>矩阵--几何解释</h2>
<hr />
<p>一般来说,方阵能描述任意<code>线性变换</code>。</p>
<p>下面是一组非常有用的变换:</p>
<ul>
<li>旋转</li>
<li>缩放</li>
<li>投影</li>
<li>镜象</li>
<li>仿射</li>
</ul>
<h3>矩阵是怎样变换向量的</h3>
<hr />
<p>向量[1, -3, -4]能被解释成位移[1, 0, 0],随后位移[0, -3, 0],最后位移[0, 0, 4]。</p>
<p>依据三角形法则将这个位移序列解释成向量的加法:</p>
<p><img src="/assets/img/math/matrix-20.png" alt="matrix-20" /></p>
<p>一般来说,任意向量v都能写为<strong>扩展</strong>形式:</p>
<p><img src="/assets/img/math/matrix-21.png" alt="matrix-21" /></p>
<p>另一种略有差别的形式为:</p>
<p><img src="/assets/img/math/matrix-22.png" alt="matrix-22" /></p>
<p>注意右边的向量就是x,y,z轴。向量的坐标都表明了平行于相应坐标轴的有向位移。</p>
<p>将公式重写,p,g,r定义为指向+x,+y,+z方向的单位向量。</p>
<p><img src="/assets/img/math/matrix-23.png" alt="matrix-23" /></p>
<p>将向量表示为基向量的线性组和</p>
<p><img src="/assets/img/math/matrix-24.png" alt="matrix-24" /></p>
<p>将矩阵解释为基向量集合</p>
<p>用一个向量乘以该矩阵,得到:</p>
<p><img src="/assets/img/math/matrix-25.png" alt="matrix-25" /></p>
<p>这和前面计算转换后的v的等式相同。</p>
<blockquote><p>如果把矩阵的行解释为坐标系的基向量,那么乘以该矩阵就相当于执行了一次坐标转换。</p>
<p>若有aM=b,我们就可以说,M将a转换到b。</p></blockquote>
<p>从这一点看,术语<strong>转换</strong>和<strong>乘法</strong>是等价的。</p>
<h3>矩阵的形式</h3>
<hr />
<p>基向量[1, 0, 0], [0, 1, 0], [0, 0, 1]乘以任意矩阵M时的情况:</p>
<p><img src="/assets/img/math/matrix-26.png" alt="matrix-26" /></p>
<p>用基向量[1, 0, 0]乘以M时,结果是M的第1行。其它两行也有同样的结果。</p>
<blockquote><p>矩阵的每一行都能解释为转换后的基向量。</p></blockquote>
<ul>
<li>有了一种简单的方法来形象化解释矩阵所代表的变换。</li>
<li>有了反向建立矩阵的可能--给出一个期望的变换(如旋转、缩放等),能够构造一个矩阵代表些变换。我们所做的一切就是计算基向量的变换,然后将变换后的基向量填入矩阵。</li>
</ul>
<p>看下列2X2矩阵:</p>
<p><img src="/assets/img/math/matrix-27.png" alt="matrix-27" /></p>
<p>这个矩阵的代表的变换是什么?首先,从矩阵中抽出基向量p和q:</p>
<p><img src="/assets/img/math/matrix-28.png" alt="matrix-28" /></p>
<p>以<strong>原</strong>基向量(x轴,y轴)做参考,在笛卡尔平面中展示了这些向量。</p>
<p>x基向量变换到上面的p向量,y基向量变换至q向量。所以2D中想象矩阵的方法就是想象由行向量构成的<strong>L</strong>形状。M代表的部分变换是逆时针旋转26度。</p>
<p><img src="/assets/img/math/matrix-29.png" alt="matrix-29" /></p>
<p>2D旋转矩阵的行向量</p>
<p>当然,所有向量都被线性变换所影响,不只是基向量。从<strong>L</strong>形状能够得到变换最直观的印象,把基向量构成的整个2D平行四边形画完整有助于进一步看到变换对其它向量的影响。</p>
<p><img src="/assets/img/math/matrix-30.png" alt="matrix-30" /></p>
<p>矩阵行向量构成的2D平行四边形</p>
<p>平行四边形称作<code>偏转盒</code>,在盒子中画一个物体有助于理解。</p>
<p><img src="/assets/img/math/matrix-31.png" alt="matrix-31" /></p>
<p>在盒子中画一个物体</p>
<p>很明显,矩阵M不仅旋转坐标系,还会拉伸它。</p>
<p>这种技术也能应用到3D转换中。2D中有两个基向量,构成<strong>L</strong>型,3D中有三个基向量,它们形成一个<strong>三角架</strong>。首先,展示一个转换前的物品。</p>
<p>考虑以下3D变换矩阵:</p>
<p><img src="/assets/img/math/matrix-32.png" alt="matrix-32" /></p>
<p>从矩阵的行中抽出基向量,能想象出该矩阵所代表的变换。变换后的基向量、立方体、茶壶如下所示。</p>
<p><img src="/assets/img/math/matrix-33.png" alt="matrix-33" /></p>
<p>转换前的茶壶、单位立方体和基向量</p>
<p><img src="/assets/img/math/matrix-34.png" alt="matrix-34" /></p>
<p>转换后的茶壶、单位立方体和基向量</p>
<p>这个变换包含z轴顺时针旋转45度和不规则的缩放,使得茶壶比以前<strong>高</strong>。</p>
<blockquote><p>变换并没有影响到z轴,因为矩阵的第三行是[0, 0, 1]。</p></blockquote>
<h2>总结</h2>
<hr />
<ul>
<li>方阵的行能被解释为坐标系的基向量。</li>
<li>为了将向量从原坐标系变换到新坐标系,用它乘以一个矩阵。</li>
<li>从原坐标系到这些基向量定义的新坐标系的变换是一种线性变换。线性变换保持直线和平行线,但角度、长度、面积可能会被改变。</li>
<li>零向量乘以任何矩阵仍然得到零向量。因此,方阵所代表的线性变换的原点和原坐标系的原点一致。变换不包含原点。</li>
<li>可以通过想象变换后的坐标系的基向量来想象矩阵。这些基向量在2D中构成<strong>L</strong>型,在3D中构成<strong>三角架</strong>型。用一个盒子及辅助物更有助于理解。</li>
</ul>
矩阵与线性变换2015-06-10T00:00:00+00:00http://jiaxianhua.github.io/math/2015/06/10/matrix-and-linear-transformation<h2>变换物体与变换坐标系</h2>
<hr />
<p>在2D中的例子“将一个物体顺时针旋转20度”。变换物体(这里是旋转),意味着旋转物体上所有的点。这些点被移动到一个新位置,使用同一坐标系来描述变换前和变换后点的位置。</p>
<p><img src="/assets/img/math/matrix-and-linear-transformation-1.png" alt="matrix-and-linear-transformation-1" /></p>
<p><strong>顺时针将物体旋转$$ 20^{\circ} $$</strong></p>
<p>现在,和<code>变换坐标系</code>的概念进行比较。旋转坐标系后时,物体上的点实际没有移动,我们只是在另外一个坐标系中描述它的位置而已。</p>
<p><img src="/assets/img/math/matrix-and-linear-transformation-2.png" alt="matrix-and-linear-transformation-2" /></p>
<p><strong>顺时针旋转坐标系$$ 20^{\circ} $$</strong></p>
<p>这两种变换在某种意义上是等价的,现在,先看看它们各自的做优点。</p>
<p>变换物体的用处非常明显。例如,为了渲染一辆车,必须将点从车的物体坐标系变换到世界坐标系,接着到摄像机坐标系。</p>
<p><code>那么,为什么还要变换坐标系呢?</code></p>
<p>其实旋转坐标系能起到很好的作用。</p>
<p><img src="/assets/img/math/matrix-and-linear-transformation-3.png" alt="matrix-and-linear-transformation-3" /></p>
<p><strong>旋转坐标系的盒子</strong></p>
<p>上图展示了一把枪,正向汽车发射子弹。我们一开始就知道世界坐标系中枪的位置和子弹的弹道。现在,想象一下世界坐标系被旋转到和车的物体坐标系重合的位置,而与此同时保持车、枪、子弹的弹道不动。这样,我们得到了枪和子弹弹道在车的物体坐标系中的坐标,接着就可以作碰撞检测以检查子弹是否会击中汽车了。</p>
<p>当然,也可以将车旋转到世界坐标系,在世界坐标系中作碰撞检测,但这要花费更多的时间,因为车的模型可能有大量的顶点和三角形,计算量太大。现在,不用担心实际变换的细节问题,这正是下面要解释的。</p>
<p>可以变换物体,也可以变换坐标系,某些情况下一种方法比另一种更合适。</p>
<p>对这两种变换保持一种概念上的区别还是有必要的,有些情况下需要进行物体变换,另外一些情况下则需要进行坐标系的变换。然而,这两种变换实际上是等价的,将物体变换一个量等价于将坐标系变换一个<code>相反</code>的量。</p>
<p>如下图右边的那幅图显示出坐标系沿顺时针方向旋转了$$ 20^{\circ} $$。现在,旋转整个图(坐标系和车),使坐标系指向回到<code>标准</code>。因为旋转的是整个图,所以仅仅相当于换了一个角度来看这张图,没有改变车和坐标系的相对位置。</p>
<p><img src="/assets/img/math/matrix-and-linear-transformation-4.png" alt="matrix-and-linear-transformation-4" /></p>
<p><strong>旋转坐标系相当于以相反的量旋转物体</strong></p>
<blockquote><p>变换物体相当于以相反的量变换描述这个物体的坐标系。</p>
<p>当有多个变换时,则需要以相反的顺序变换相反的量。</p></blockquote>
<h2>旋转</h2>
<hr />
<h3>2D中的旋转</h3>
<hr />
<p>在2D环境中,物体只能绕某个点旋转,因为现在暂不考虑平移,这里我们进一步限制物体,使其只绕原点旋转。2D中绕原点的旋转只有一个参数:角度$$ \theta $$,它描述了旋转量。逆时针旋转经常(不是必须)被认为是正方向,顺时针方向是负方向。</p>
<p>下图展示了基向量$$ p, q $$绕原点旋转,等到新的基向量$$ {p}', {q}' $$。</p>
<p><img src="/assets/img/math/matrix-and-linear-transformation-5.png" alt="matrix-and-linear-transformation-5" /></p>
<p><strong>绕2D中的原点旋转</strong></p>
<p>知道旋转后基向量的值,就可以用下列公式构造矩阵:</p>
<p>$$</p>
<h1>R\left( \theta \right) </h1>
<h1>\left[ \dfrac {p'} {q'}\right]</h1>
<p>\begin{bmatrix}
cos\theta & sin\theta \
-sin\theta & cos \theta
\end{bmatrix}
$$</p>
<p><strong>2D旋转矩阵</strong></p>
Jekyll使用MathJax来显示数学式2015-06-10T00:00:00+00:00http://jiaxianhua.github.io/latex/2015/06/10/jekyll-math-formula<blockquote><p>使用Jekyll写作文章的时候有可能需要内嵌一些数学公式, MathJax就是用来干这个的,试用了一下感觉非常方便。步骤如下:</p></blockquote>
<h2>修改html头部</h2>
<hr />
<p>在每个页面开头加上这么一句,在Jekyll下可以通过修改<code>default.html</code><strong>(_layouts/default.html)</strong>加上。</p>
<p><figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="o"><</span><span class="nx">script</span> <span class="nx">type</span><span class="o">=</span><span class="s2">"text/javascript"</span>
<span class="nx">src</span><span class="o">=</span><span class="s2">"http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"</span><span class="o">></span>
<span class="o"><</span><span class="sr">/script></span></code></pre></figure></p>
<h2>本地安装kramdown</h2>
<hr />
<p>因为rdiscount和默认的markdown在解析带公式文件的时候都会出现一些问题,所以最简单办法还是安装kramdown。</p>
<p><code>$ gem install kramdown --version 1.6.0</code></p>
<p><code>$ gem install kramdown</code></p>
<h2>修改_config.yml</h2>
<hr />
<p>把markdown选项修改为:</p>
<p><code>markdown: kramdown</code></p>
<h2>输入公式</h2>
<hr />
<p>然后在发布的时候就可以使用$$来把需要显示的数学公式扩起来。像这样:</p>
<p><code>$$ a^2 + b^2 = c^2 $$</code></p>
<p>发布出来就是漂亮的公式了。</p>
<p>$$ a<sup>2</sup> + b<sup>2</sup> = c<sup>2</sup> $$</p>
向量2015-06-09T00:00:00+00:00http://jiaxianhua.github.io/math/2015/06/09/vector<h2>向量-数学定义</h2>
<hr />
<h3>标量</h3>
<hr />
<p>平时所用数字的技术称谓。</p>
<h3>向量</h3>
<hr />
<p>对数学家而言,<strong>向量</strong>就是一个数字列表。</p>
<p>对程序员而言,<strong>向量</strong>就是<strong>数组</strong>。</p>
<h4>记法</h4>
<hr />
<p>书写向量时,用方括号将一列数括起来。</p>
<p>水平书写的向量叫<strong>行向量</strong>。</p>
<p>如:[1, 2, 3]。</p>
<p>垂直书写的向量叫<strong>列向量</strong>。</p>
<p>如:</p>
<p><img src="/assets/img/math/vector-1.png" alt="vector-1" /></p>
<h2>向量-几何定义</h2>
<hr />
<p>向量是有<strong>大小</strong>和<strong>方向</strong>的有向线段。</p>
<ul>
<li>向量的大小就是向量的长度(模)。向量有非负的长度。</li>
<li>微量的方向描述了空间中向量的指向。</li>
</ul>
<blockquote><p>注意:方向并不完全和方位等同。</p></blockquote>
<h3>向量的形式</h3>
<hr />
<p><img src="/assets/img/math/vector-2.png" alt="vector-2" /></p>
<p>2D 向量</p>
<p><img src="/assets/img/math/vector-3.png" alt="vector-3" /></p>
<p>向量的头和尾</p>
<h3>位置与位置</h3>
<hr />
<p>向量没有位置。只有大小和方向。</p>
<p>有大小和方向,却没有位置的量。</p>
<ul>
<li>位移:向前(方向)三步(大小)</li>
<li>速度:50公里/小时(大小)和北(方向)</li>
</ul>
<h3>向量的表达</h3>
<hr />
<p><img src="/assets/img/math/vector-4.png" alt="vector-4" /></p>
<p>通过列出各维度上的有符号位移来表达向量</p>
<h3>将向量表示为位移序列</h3>
<hr />
<p>思考微量所代表的位移的一个好办法是将向量分解成与轴平等的分量。把这些分量的位移组合起来,就得到了向量作为整体所代表的位移。</p>
<p><img src="/assets/img/math/vector-5.png" alt="vector-5" /></p>
<p>将向量表示为位移序列</p>
<h3>向量与点</h3>
<hr />
<p><strong>点</strong>有位置,但没有大小和厚度。</p>
<p><strong>向量</strong>有大小和方向,但没有位置。</p>
<p><img src="/assets/img/math/vector-6.png" alt="vector-6" /></p>
<p>指明点与指名向量</p>
<h3>相对位置</h3>
<hr />
<p>进位我们的位置,能通过描述它与已知眯之间的相对关系指明。</p>
<p>任何对于位置的描述只有在一定参考系内才有意义。</p>
<p>理论上,能够建立一个包容一切的参考系,并选择一个点作为这个空间的原点,然后定义<strong>绝对</strong>坐标系。</p>
<h3>点和向量的关系</h3>
<hr />
<p><img src="/assets/img/math/vector-7.png" alt="vector-7" /></p>
<p>点和向量的关系</p>
向量运算2015-06-09T00:00:00+00:00http://jiaxianhua.github.io/math/2015/06/09/vector-operation<h2>零向量</h2>
<hr />
<p><strong>零向量</strong>:大小为零的向量。</p>
<p>零向量也是惟一一个没有方向的向量。</p>
<p>对于其它任意数m,存在无数多个大小(模)为m的向量。它们构成一个圆。</p>
<p><img src="/assets/img/math/vector-operation-1.png" alt="vector-operation-1" /></p>
<p>对任意正值m,有无数个向量的大小等于它</p>
<h2>向向量</h2>
<hr />
<p>x + (-x) = 0</p>
<h3>运算法则</h3>
<hr />
<p>向量的负向量:只要简单的将向量的每个分量都变负即可。</p>
<p><img src="/assets/img/math/vector-operation-2.png" alt="vector-operation-2" /></p>
<p>向量变负</p>
<p>-[x y] = [-x -y]</p>
<p>-[x y z] = [-x -y -z]</p>
<p>-[x y z w] = [-x -y -z -w]</p>
<h3>几何解释</h3>
<hr />
<p>向量变负,得到一个和原向量大小相等,方向相反的向量。</p>
<p><img src="/assets/img/math/vector-operation-3.png" alt="vector-operation-3" /></p>
<h2>向量的大小(长度或模)</h2>
<hr />
<p><strong>向量的大小</strong>没有明确表示,需要计算。</p>
<p><strong>向量的大小</strong>也称作向量的<strong>长度</strong>或<strong>模</strong>。</p>
<h3>运算法则</h3>
<hr />
<p>线性代数中,向量的大小用向量两边加竖线表示,这个标量的<strong>绝对值</strong>在标量两边加单竖线表示类似。</p>
<p><img src="/assets/img/math/vector-operation-4.png" alt="vector-operation-4" /></p>
<p>向量大小</p>
<p>向量的大小就是向量各分量平方和的平方根。</p>
<p><img src="/assets/img/math/vector-operation-5.png" alt="vector-operation-5" /></p>
<p>2D, 3D 向量的大小</p>
<h3>几何解释</h3>
<hr />
<p><img src="/assets/img/math/vector-operation-6.png" alt="vector-operation-6" /></p>
<p>向量大小公式的几何解释</p>
<h2>标量与向量的乘法</h2>
<hr />
<p>标量与向量不能相加,但能相乘。相乘结果将得到一个向量,与原向量平行,但长度不同可方向相反。</p>
<h3>运算法则</h3>
<p>标量与向量的乘法非常直接,将向量的每个分量都与标量相乘即可。</p>
<p><img src="/assets/img/math/vector-operation-7.png" alt="vector-operation-7" /></p>
<p>向量与标量相乘</p>
<p>应用到3D向量,如:</p>
<p><img src="/assets/img/math/vector-operation-8.png" alt="vector-operation-8" /></p>
<p>3D向量与标量相乘</p>
<p>向量也能除以非零标量,效果等同于乘以标量的倒数。</p>
<p><img src="/assets/img/math/vector-operation-9.png" alt="vector-operation-9" /></p>
<blockquote><p>注意</p></blockquote>
<ul>
<li>标量与向量相乘时,不需要写乘号。将两个量挨着写即表示相乘(常将标量写在左边)。</li>
<li>标量与向量的乘法和除法优先级高于加法和减法。</li>
<li>标量不能除以向量,并且向量不能除以另一个向量。</li>
<li>负向量能被认为是乘法的特殊情况,乘以标量-1。</li>
</ul>
<h3>几何解释</h3>
<hr />
<p>向量乘以标量<em>k</em>的效果是以因子<strong>|k|</strong>缩放向量的长度。</p>
<p><img src="/assets/img/math/vector-operation-10.png" alt="vector-operation-10" /></p>
<p>一个2D向量被多个因子乘的效果</p>
<h2>标量化向量</h2>
<hr />
<p><strong>单位向量</strong>就是大小为1的向量。</p>
<p>单位向量经常被称作<strong>标准化向量</strong>或更简单地称为<strong>法线</strong>。</p>
<h3>运算法则</h3>
<hr />
<p>对任意非零向量v,都能计算出一个和v方向相同的单位向量v(norm)。这个过程被称作向量的<strong>标准化</strong>,要标准化向量,将向量除以它的大小(模)即可。</p>
<p><img src="/assets/img/math/vector-operation-11.png" alt="vector-operation-11" /></p>
<p>标准化向量</p>
<blockquote><p>零向量不能被标准化。数学上这是不允许的,因为将导致除零。几何上也没有意义,因为零向量没有方向。</p></blockquote>
<h3>几何解释</h3>
<hr />
<p>2D环境中,如果以原点为尾画一个单位向量,那么向量的头将接触到圆心在原点的单位圆(单位圆的半径为1)。</p>
<p>3D环境中,单位向量将触到单位球。</p>
<p><img src="/assets/img/math/vector-operation-12.png" alt="vector-operation-12" /></p>
<p>2D中的标准化向量</p>
<h2>向量的加法和减法</h2>
<hr />
<p>如果两个向量的维数相同,那么它们能相加,相减。结果向量的维数与原向量相同。向量的回减法的记法和标量加减法的记法相同。</p>
<h3>运算法则</h3>
<hr />
<p>向量加法的运算法则很简单:两个向量相加,将对应分量相加即可。</p>
<p><img src="/assets/img/math/vector-operation-13.png" alt="vector-operation-13" /></p>
<p>两个向量相加</p>
<p>减法解释为加负向量,a-b=a+(-b)。</p>
<p><img src="/assets/img/math/vector-operation-14.png" alt="vector-operation-14" /></p>
<p>两个向量相减</p>
<blockquote><p>注意</p></blockquote>
<ul>
<li>向量不能与标量或维数不同的向量相加减。</li>
<li>和标量加法一样,向量的加法满足交换律,但向量减法不满足交换律。永远有 a+b=b+a,但 a-b=-(b-a),仅当a=b时,a-b=b-a。</li>
</ul>
<h3>几何解释</h3>
<hr />
<p>向量a和b相加的几何解释为:</p>
<p>平衡向量,使向量a的头连接向量b的尾,接着从a的尾向b的头画一个向量。这就是向量加法的<strong>三角形法则</strong>。</p>
<p>向量的减法与之类似。</p>
<p><img src="/assets/img/math/vector-operation-15.png" alt="vector-operation-15" /></p>
<p>2D向量加减法的三角形法则</p>
<p>三角形法则能扩展到多个向量的情形中。</p>
<p><img src="/assets/img/math/vector-operation-16.png" alt="vector-operation-16" /></p>
<p>三角形法则扩展到多个向量</p>
<p>向量能被解释为与轴平行的位移序列。</p>
<p><img src="/assets/img/math/vector-operation-17.png" alt="vector-operation-17" /></p>
<p>向量解释为位移序列</p>
<h3>一个点到另一个点的向量</h3>
<hr />
<p>通过三角形法则和向量减法解决。</p>
<p><img src="/assets/img/math/vector-operation-18.png" alt="vector-operation-18" /></p>
<p>用2D向量减法计算从a到b的向量</p>
<h2>距离公式</h2>
<hr />
<p>计算两点之间的距离。</p>
<p>从几何意义上说,两点之间的距离等于从一个点到另一个点的向量的长度。</p>
<p>3D情况:</p>
<p><img src="/assets/img/math/vector-operation-19.png" alt="vector-operation-19" /></p>
<p>a到b的距离等于向量d的长度。</p>
<p><img src="/assets/img/math/vector-operation-20.png" alt="vector-operation-20" /></p>
<p>3D距离公式</p>
<p>2D中的公式更简单。</p>
<p><img src="/assets/img/math/vector-operation-21.png" alt="vector-operation-21" /></p>
<p>2D距离公式</p>
<h2>向量点乘</h2>
<hr />
<p>标题和向量可以相乘,两个向量也可以相乘,有两种不同类型的向量乘法。</p>
<p>首先是<strong>点乘</strong>(<strong>内积</strong>)。</p>
<h3>运算法则</h3>
<hr />
<p>术语<strong>点乘</strong>来自记法<em>a.b</em>中的点号。与标量与向量的乘法一样,向量点乘的优先给高于加法和减法。</p>
<blockquote><p>标量乘法和标量与向量的乘法经常可以省略乘号,但在点乘中不能省略。</p></blockquote>
<p>向量点乘就是对应分量乘积的和,其结果是一个<strong>标量</strong>:</p>
<p><img src="/assets/img/math/vector-operation-22.png" alt="vector-operation-22" /></p>
<p>向量点乘</p>
<p>用连加符号简写为:</p>
<p><img src="/assets/img/math/vector-operation-23.png" alt="vector-operation-23" /></p>
<p>向量点乘的连加记法</p>
<p>应用到2D、3D中,为:</p>
<p><img src="/assets/img/math/vector-operation-24.png" alt="vector-operation-24" /></p>
<p>2D与3D点乘</p>
<h3>几何解释</h3>
<hr />
<p>一般来说,点乘结果描述了两个向量的<strong>相似</strong>程序,点乘结果越大,两向量越相近。几何解释更加直接。</p>
<p><img src="/assets/img/math/vector-operation-25.png" alt="vector-operation-25" /></p>
<p>点乘和向量间的夹角相关</p>
<p>点乘等于向量大小与夹角的cos值的积:</p>
<p><img src="/assets/img/math/vector-operation-26.png" alt="vector-operation-26" /></p>
<p>向量点乘的几何解释</p>
<p>如果a、b是单位向量,就可以以避免除法运算。这种情况下,分母是1,只剩下:</p>
<p><img src="/assets/img/math/vector-operation-27.png" alt="vector-operation-27" /></p>
<p>计算两个向量的夹角</p>
<p>如果不需要的确切值而只需要a和b夹角的类型,可以只取用点乘结果的符号。</p>
<p><img src="/assets/img/math/vector-operation-28.png" alt="vector-operation-28" /></p>
<p>点乘结果的符号可大致确定 的类型</p>
<p>向量大小并不影响点乘结果的符号,所以上表是和a、b无关的。</p>
<blockquote><p>如果a、b中任意一个为0,那么a.b的结果也等于0.</p></blockquote>
<p>因此,点乘对零向量的解释是,零向量的任意其它向量都垂直。</p>
<h3>向量投影</h3>
<hr />
<p>给定两个向量v和n,能将v分解成两个分量:v(||)和v(<em>|</em>)。它们分别平行和垂直于n,并满足v=v(<em>|</em>)+v(||)。一般称平行分量v(||)为v在n上的投影。</p>
<p>使用点乘计算投影。</p>
<p><img src="/assets/img/math/vector-operation-29.png" alt="vector-operation-29" /></p>
<p>向量的投影</p>
<p><img src="/assets/img/math/vector-operation-30.png" alt="vector-operation-30" /></p>
<p>v(||)计算公式</p>
<p>只要能够示出v(||)的模,就能够计算出该投影向量的值,利用三角分解求值:</p>
<p><img src="/assets/img/math/vector-operation-31.png" alt="vector-operation-31" /></p>
<p><img src="/assets/img/math/vector-operation-32.png" alt="vector-operation-32" /></p>
<p>向量的投影</p>
<p>当然,如果n是单位向量,除法就不必要了。</p>
<p>知道v(||),求v(<em>|</em>)就很容易了,如下:</p>
<p><img src="/assets/img/math/vector-operation-33.png" alt="vector-operation-33" /></p>
<h2>向量叉乘</h2>
<hr />
<p>另一种向量乘法称作<strong>叉乘</strong>(<strong>叉积</strong>),仅可应用于3D向量。和点乘不一样,点乘得到一个标量并满足交换律,向量叉乘得到一个向量并且不满足交换律。</p>
<h3>运算法则</h3>
<hr />
<p>和点乘一样,术语<strong>叉乘</strong>来自记法<em>a X b</em>中的叉号。这里把叉乘号写出来,不能像标量乘法那样省略它。</p>
<p>叉乘公式为:</p>
<p><img src="/assets/img/math/vector-operation-34.png" alt="vector-operation-34" /></p>
<p>叉乘</p>
<p>示例如下:</p>
<p><img src="/assets/img/math/vector-operation-35.png" alt="vector-operation-35" /></p>
<p>叉乘的运算优先级和点乘一样,乘法在加减法之前计算。当点乘和叉乘在一直时,<em>叉乘优先计算</em>:a.bXc = a.(bXc)。</p>
<p>因此点乘返回一个标量,同时标量和向量间不能叉乘,所以(a.b)Xc没有定义。</p>
<p>运算a.(bXc)称作<strong>三重积</strong>。</p>
<h3>几何解释</h3>
<hr />
<p>叉乘得到的向量垂直于原来的两个向量。</p>
<p><img src="/assets/img/math/vector-operation-36.png" alt="vector-operation-36" /></p>
<p>向量叉乘</p>
<p>图中,向量a和b在一个平面中,向量aXb指向该平面的正上方,垂直于a和b。</p>
<p>aXb的长度等于向量的大小与向量夹角sin值的积,如下:</p>
<p><img src="/assets/img/math/vector-operation-37.png" alt="vector-operation-37" /></p>
<p>叉乘的长度与向量夹角的sin值有关。</p>
<p>可以看到,||aXb||也等于以a和b为两边的平行四边形的面积。</p>
<p><img src="/assets/img/math/vector-operation-38.png" alt="vector-operation-38" /></p>
<p>叉乘和平等四边形的面积</p>
<p>由经典几何知道可知平行四边形的面积是<em>bh</em>,即底和高的乘积。可以验证这一点,通过把一端的三角形<em>切</em>下来移到另一边,可构成一个矩形。</p>
<p><img src="/assets/img/math/vector-operation-39.png" alt="vector-operation-39" /></p>
<p>平行四边形面积</p>
<p>设a、b分别为a、b的长度。</p>
<p><img src="/assets/img/math/vector-operation-40.png" alt="vector-operation-40" /></p>
<p><img src="/assets/img/math/vector-operation-41.png" alt="vector-operation-41" /></p>
<p>如果a、b平行或任意一个为0,则aXb=0。叉乘对零向量的解释为:它平行任意其它向量。</p>
<blockquote><p>注意这点和点乘的解释不同</p>
<p>点乘的解释是和任意其它向量垂直。</p>
<p>当然,定义零向量平行或垂直于任意向量都是不对的,因为零向量没有方向。</p></blockquote>
<p>aXb指向哪个方向呢?</p>
<p>通过将a的头与b的尾相接,并检查从a到b是顺时针还是逆时针,能够确定aXb的方向。</p>
<p>在<strong>左手坐标系</strong>中</p>
<ul>
<li>如果a和b呈顺时针,aXb指向自己。</li>
<li>如果a和b呈逆时针,aXb远离自己。</li>
</ul>
<p>在<strong>右手坐标系</strong>中,恰好相反。</p>
<ul>
<li>如果a和b呈顺时针,aXb远离自己。</li>
<li>如果a和b呈逆时针,aXb指向自己。</li>
</ul>
<p><img src="/assets/img/math/vector-operation-42.png" alt="vector-operation-42" /></p>
<p>顺时针方向</p>
<p><img src="/assets/img/math/vector-operation-43.png" alt="vector-operation-43" /></p>
<p>逆时针方向</p>
<blockquote><p>注意,探测顺时针还是逆时针时,必须让a的头与b的尾相接。</p></blockquote>
<p>叉乘最重要的应用就是创建垂直于平面、三角形或多边形的向量。</p>
<h2>线性代数公式</h2>
<hr />
<p>下表列出了一些有用的公式。</p>
<p><img src="/assets/img/math/vector-operation-44.png" alt="vector-operation-44" /></p>
<p><img src="/assets/img/math/vector-operation-45.png" alt="vector-operation-45" /></p>
坐标系2015-06-08T00:00:00+00:00http://jiaxianhua.github.io/math/2015/06/08/coordinate-system<h2>世界坐标系</h2>
<hr />
<p>世界坐标系是一个特殊的坐标系,它建立了描述其它坐标系所需要的参考框架。</p>
<p>从另一方面说,能够用世界坐标系描述其他坐标系的位置,而不能用更大的,外部的坐标系来描述世界坐标系。</p>
<blockquote><p>世界坐标系也被广泛称作<em>全局坐标系</em>或者<em>宇宙坐标系</em>。</p></blockquote>
<p>关于世界坐标系的典型问题都是关于初始位置和环境的。如:</p>
<ul>
<li>每个我们的位置和方向。</li>
<li>摄像机的位置和方向。</li>
<li>世界中每一点的地形是什么(如山丘、建筑、湖泊等)。</li>
<li>各物体从哪里来,到哪里去(NPC的运动策略)。</li>
</ul>
<h2>物体坐标系</h2>
<hr />
<blockquote><p>某些情况下,物体坐标系也被称作<em>模型坐标系</em>。因为模型顶点的坐标都是在模型坐标系中描述的,有时候它也称作<em>身体坐标系</em>。</p></blockquote>
<p>在物体坐标系中可能会遇到的问题如:</p>
<ul>
<li>周围有需要互相作用的物体吗?(我要攻击它吗?)</li>
<li>哪个方向?在我前面吗?我左边一点?右边?(我应该向它射击还是转身就跑?)</li>
</ul>
<h2>摄像机坐标系</h2>
<hr />
<p><img src="/assets/img/3d/Camera_coordinate_system.png" alt="Camera_coordinate_system" /></p>
<p>关于屏幕坐标系的典型问题是哪些物体应该在屏幕上绘制出来。如:</p>
<ul>
<li>3D空间中的给定点在摄像机前方吗?</li>
<li>3D空间中的给定点是在屏幕上,还是超出了摄像机平截锥体的左、右、上、下边界?(平截锥体就是摄像机能观察到的区域)</li>
<li>某个物体是否在屏幕上?它的部分在,或全部不在?</li>
<li>两个物体,认在前面?(该总是称作可见几天检测)</li>
</ul>
<h2>惯性坐标系</h2>
<hr />
<p>惯性坐标系的原点和物体坐标系的原点重合,但惯性坐标系的轴平行于世界坐标系的轴。</p>
<p><img src="/assets/img/3d/InertialSpace-1.png" alt="InertialSpace-1" /></p>
<p>机器人的物体坐标系、惯性坐标系和世界坐标系</p>
<p><img src="/assets/img/3d/InertialSpace-2.png" alt="InertialSpace-2" /></p>
<p>机器人的物体坐标系</p>
<p><img src="/assets/img/3d/InertialSpace-3.png" alt="InertialSpace-3" /></p>
<p>机器人的惯性坐标系</p>
<p><img src="/assets/img/3d/InertialSpace-4.png" alt="InertialSpace-4" /></p>
<p>世界坐标系</p>
<h3>为什么要引入惯性坐标系?</h3>
<blockquote><p>因为从物体坐标系到惯性坐标系只需要旋转,从惯性坐标系到世界坐标系只需要平移。</p></blockquote>
3d 左手坐标系与右手坐标系2015-06-08T00:00:00+00:00http://jiaxianhua.github.io/3d/2015/06/08/3d-coordinate-system<h2>左手坐标系</h2>
<hr />
<p>伸出左手,让大拇指与食指成“L”形,大拇指向右,食指向上。其余手指指向前方。</p>
<p>大拇指,食指和其余手指分别代表x,y,z轴的正方向。</p>
<p><img src="/assets/img/3d/left_hand.png" alt="left_hand" /></p>
<h2>右手坐标系</h2>
<hr />
<p>伸出右手,食指向上,其余三指向前,大拇指指向左。</p>
<p>大拇指,食指和其余手指分别代表x,y,z轴的正方向。</p>
<p><img src="/assets/img/3d/right_hand.png" alt="right_hand" /></p>
和程序员职业相关的15个谜语2015-05-29T00:00:00+00:00http://jiaxianhua.github.io/joke/2015/05/29/programmers-exclusive-15-riddle<h2>谜语</h2>
<hr />
<ol>
<li><p>深夜造访(打一网络安全术语)</p></li>
<li><p>连胜六场又赢了(打一知名操作系统)</p></li>
<li><p>小米大合唱(打一著名互联网厂商)</p></li>
<li><p>月老难扯二人姻缘(打一网络技术)</p></li>
<li><p>悟空出了五指山(打一技术用语)</p></li>
<li><p>E(打一开发语言)</p></li>
<li><p>话又说回来了(打一网络完全术语)</p></li>
<li><p>禽流感(打一常见 PC/服务器故障)</p></li>
<li><p>不达目的誓不罢休(打一著名网络解决方案提供商)</p></li>
<li><p>梦中交谈(打一热门技术)</p></li>
<li><p>单个花生超产(打一芯片技术)</p></li>
<li><p>太上老君的金丹(打一网络安全术语)</p></li>
<li><p>王老吉面壁(打一网络安全设备)</p></li>
<li><p>拳王的金腰带(打一网络技术)</p></li>
<li><p>整个界面都是阿凡达(打一 PC/服务器故障现象)</p></li>
</ol>
<h3>参考答案:</h3>
<hr />
<ol>
<li>黑客</li>
<li>win7</li>
<li>谷歌</li>
<li>无线</li>
<li>解压</li>
<li>C++</li>
<li>循环语句</li>
<li>死机</li>
<li>思科</li>
<li>虚拟化</li>
<li>多核</li>
<li>密钥</li>
<li>防火墙</li>
<li>宽带</li>
<li>蓝屏</li>
</ol>
零基础学习Cocos2d-x-72015-05-28T00:00:00+00:00http://jiaxianhua.github.io/cocos2d-x/2015/05/28/cocos2dx-7<h2>如何在iPhone模拟器上运行Cocos2d-x Test 项目</h2>
<hr />
<p>下载源代码。</p>
<p>地址为:<a href="http://cn.cocos2d-x.org/download/">http://cn.cocos2d-x.org/download/</a></p>
<p>Mac 地址:<a href="http://www.cocos2d-x.org/filedown/cocos2d-x-3.6.zip">http://www.cocos2d-x.org/filedown/cocos2d-x-3.6.zip</a></p>
<ol>
<li><p>clone the repo from GitHub.</p>
<pre><code> $ git clone git@github.com:cocos2d/cocos2d-x.git
</code></pre></li>
<li><p>After cloning the repo, please execute <code>download-deps.py</code> to download and install dependencies.</p>
<pre><code> $ cd cocos2d-x
$ python download-deps.py
</code></pre></li>
<li><p>After running <code>download-deps.py</code>.</p>
<pre><code> $ cd cocos2d-x
$ git submodule update --init
</code></pre></li>
</ol>
<h3>Running Tests</h3>
<hr />
<p>Select the test you want from Xcode Scheme chooser.</p>
<p>For OS X / iOS</p>
<pre><code>$ cd cocos2d-x/build
$ open cocos2d_tests.xcodeproj
</code></pre>
<h3>说明</h3>
<hr />
<p>样本代码已经在以下环境下测试:</p>
<p>机器名称 | MacBook Air
操作系统 | OS X Yosemite 10.10.3 (14D136)
Xcode版本 | Xcode 6.3.2
iOS | SDK 8.3</p>
<h3>测试界面</h3>
<hr />
<ul>
<li>cpp_empty_test_iOS</li>
</ul>
<p>酷!整个测试样例很快就会出现!</p>
<p><img src="/assets/img/cocos2dx/cpp_empty_test_iOS.png" alt="cpp_empty_test_iOS" /></p>
<ul>
<li>cpp_test_iOS</li>
</ul>
<p><img src="/assets/img/cocos2dx/cpp_test_iOS.png" alt="cpp_test_iOS" /></p>
<p>恭喜选择C++!很快你就能在iPhone中用C++编写Cocos2d游戏了。</p>
零基础学习Cocos2d-x-62015-05-27T00:00:00+00:00http://jiaxianhua.github.io/cocos2d-x/2015/05/27/cocos2dx-6<h2><a href="http://cn.cocos2d-x.org/article">Cocos2d-x 开发者文档</a></h2>
<p><a href="http://cn.cocos2d-x.org/doc/cocos2d-x-3.0/index.html">Cocos2d-x v3.0 API文档</a></p>
零基础学习Cocos2d-x-52015-05-26T00:00:00+00:00http://jiaxianhua.github.io/cocos2d-x/2015/05/26/cocos2dx-5<h2>架构和目录结构</h2>
<hr />
<h3>Cocos2d-x</h3>
<hr />
<h4>架构</h4>
<p><img src="/assets/img/cocos2dx/cocos2d-x-architecture.jpg" alt="cocos2d-x-architecture" /></p>
<h4>目录结构</h4>
<p>从github下载Cocos2d-x repo资源包后,或解压稳定版ZIP压缩包后,就会看到如下的目录结构:</p>
<table>
<thead>
<tr>
<th>目录名称 </th>
<th> 说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>CocosDenshion </td>
<td> 音频支持。注意:Android平台中背景音乐和短音效所使用的系统API不同。</td>
</tr>
<tr>
<td>cocos2dx </td>
<td> Cocos2d-x框架的主目录。</td>
</tr>
<tr>
<td>document </td>
<td> 你可以下载doxygen文档系统,利用该系统打开本文档文件夹内的doxygen.config文件,然后再生成离线API文档。</td>
</tr>
<tr>
<td>extensions </td>
<td> 如果需要更多图形用户界面的控制功能、网络访问、CocosBuilder支持甚至2.5D功能,你可以使用using namespace cocos2d::extension。</td>
</tr>
<tr>
<td>external </td>
<td> 包括box2d及Chipmunk库。</td>
</tr>
<tr>
<td>licenses </td>
<td> cocos2d依赖很多其他开源项目。所有授权许可文件都在这个目录。</td>
</tr>
<tr>
<td>samples </td>
<td> 重要!这是你该开始用到的文件。从Cpp/HelloCpp开始学习,你会在TestCpp中发现所有类的用法。lua和js样本也在这个目录。</td>
</tr>
<tr>
<td>scripting </td>
<td> 我知道你不喜欢C++,写起来太复杂。没问题,我们有Lua和Javascript。Scripting文件夹包括来自火狐的lua官方引擎和SpiderMonkey引擎。</td>
</tr>
<tr>
<td>template </td>
<td> 该目录包括在不同集成开发环境及不同平台中创建Cocos2d-x新项目的模板。这里汇集了数量庞大覆盖各种开发环境和平台的模板!</td>
</tr>
<tr>
<td>tools </td>
<td> 包括将C++绑定至lua及javascript的脚本文件。</td>
</tr>
<tr>
<td>CHANGELOG </td>
<td> 作者修订记录文档。</td>
</tr>
<tr>
<td>cocos2d-win32.vc2010.sln </td>
<td> 配套Visual Studio 2010打开。注意:VS 2008自Cocos2d-x v2.0版本以来就不再支持。</td>
</tr>
<tr>
<td>cocos2d-win32.vc2012.sln </td>
<td> 配套Visual Studio 2012打开。</td>
</tr>
<tr>
<td>create-android-project.bat </td>
<td> 在Windows平台运行。具体用法请参考如何用脚本创建Android项目。</td>
</tr>
<tr>
<td>create-android-project.sh </td>
<td> 在Linux或OS X平台运行。具体用法请参考如何用脚本创建Android项目。</td>
</tr>
<tr>
<td>install-templates-msvc.bat </td>
<td> 执行该文件安装后,你可以在Visual Studio中创建空的Cocos2d-x项目。</td>
</tr>
<tr>
<td>install-templates-xcode.sh </td>
<td> 执行该文件安装后,你可以在Xcode中创建空的Cocos2d-x项目。</td>
</tr>
</tbody>
</table>
<h3>Cocos2d-html5</h3>
<hr />
<h4>架构</h4>
<p><img src="/assets/img/cocos2dx/cocos2d-html5-architecture.png" alt="cocos2d-html5-architecture" /></p>
<h4>目录结构</h4>
<table>
<thead>
<tr>
<th>目录 </th>
<th> 说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>CocosDenshion </td>
<td> 音频支持。注意:你至少需要两种音频格式以支持所有浏览器。</td>
</tr>
<tr>
<td>cocos2d </td>
<td> 主目录,包括所有主目录文件及与Cocos2d-x及Cocos2d-iPhone一样的API。平台兼容性佳!</td>
</tr>
<tr>
<td>box2d </td>
<td> V2.1a版本的Box2dweb Physics引擎。</td>
</tr>
<tr>
<td>chipmunk </td>
<td> 无版本号的Chipmunk Physic引擎</td>
</tr>
<tr>
<td>Demo </td>
<td> 包括几个demo程序,你可以从这些demo程序入手。</td>
</tr>
<tr>
<td>extensions </td>
<td> 如果需要更多图形用户界面的控制功能、EditBox、CocosBuilder支持、Cocos Studio支持或任何第三方库,都在这里。</td>
</tr>
<tr>
<td>HelloHTML5Wrold </td>
<td> 预备的一个Hello World程序,你可以从这入手。</td>
</tr>
<tr>
<td>lib </td>
<td> 包括一个MIN版本的引擎,当你使用ant工具运行/cocos2d/build.xml文件时,会将所有引擎文件打包成一个文件。</td>
</tr>
<tr>
<td>licenses </td>
<td> cocos2d依赖很多其他开源项目。所有授权许可文件都在这个目录。</td>
</tr>
<tr>
<td>samples </td>
<td> 你可在Test文件中发现所有类的用法。该目录还包括样本游戏。所有测试样本和游戏样本均可以在JSB中运行。重要!这是你该开始用到的文件。</td>
</tr>
<tr>
<td>template </td>
<td> 用于创建Cocos2d-html5新项目的模板。</td>
</tr>
<tr>
<td>tools </td>
<td> 包括JSDoc及Closure Compiler编译器。</td>
</tr>
<tr>
<td>index.html </td>
<td> Cocos2d-html5索引文件。</td>
</tr>
<tr>
<td>AUTHORS </td>
<td> 作者列表</td>
</tr>
<tr>
<td>CHANGELOG </td>
<td> 该文件记录上述各位作者的修订记录。</td>
</tr>
</tbody>
</table>
<h3>Javascript绑定</h3>
<p>跨平台虽然很好,但还不够好。用C++语言编程不仅超级慢,C++代码还无法在网络浏览器中运行。这就是为什么我们选择增加Javascript绑定功能。通过JSB我们可以将javascript代码打包至:</p>
<ol>
<li>Cocos2d-x+SpiderMonkey上的本地应用,SpiderMonkey能将javascript代码解析成C语言。</li>
<li>Cocos2d-html5上的网页应用。</li>
</ol>
<p><img src="/assets/img/cocos2dx/jsbinding-1.jpg" alt="jsbinding-1" /></p>
<p>我们在Cocos2d-iphone、Cocos2d-x 和Cocos2d-html5框架中使用的API组合是一样的。所以,我们可以百分之百在本地Cocos2d-x或Cocos2d-iphone中创建javascript游戏。当你要在浏览器上运行时,你只需要将引擎切换至Cocos2d-html5,而无需修改源代码。</p>
<p>优势:</p>
<ol>
<li>编码速度要比C++快得多</li>
<li>无需处理野指针、引用计数及内存泄露的问题</li>
<li>跨越本地及网络平台</li>
<li>在线更新</li>
</ol>
<p><img src="/assets/img/cocos2dx/jsbinding-2.jpg" alt="jsbinding-2" /></p>
零基础学习Cocos2d-x-42015-05-25T00:00:00+00:00http://jiaxianhua.github.io/cocos2d-x/2015/05/25/cocos2dx-4<h2>Cocos2d系列产品关系</h2>
<hr />
<p>自Cocos2d-iPhone取得成功后,Cocos2d系列产品已经推出许多子项目。这里列出了一些最为出名的旗下分支游戏引擎。</p>
<p><img src="/assets/img/cocos2dx/coocsFamily.png" alt="coocsFamily" /></p>
<p>蓝色方框中的分支引擎(包括Cocos2d-iPhone, Cocos2d-x, Cocos2d-HTML5及 Cocos Builder 编辑器)在同一路径图上合作运行,并已发布协调版本。这意味着开发人员可以期待一条完整的工具链为网络设备或移动设备开发多平台游戏。</p>
<p>从下表可知,不同分支引擎支持不同的目标平台并使用不同的编程语言。</p>
<table>
<thead>
<tr>
<th style="text-align:left;">分支引擎 </th>
<th style="text-align:center;"> 编程语言 </th>
<th style="text-align:right;"> 平台</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;">Cocos2d </td>
<td style="text-align:center;"> Python </td>
<td style="text-align:right;"> 桌面操作系统</td>
</tr>
<tr>
<td style="text-align:left;">Cocos2d-iphone </td>
<td style="text-align:center;"> Objective-C, JavaScript </td>
<td style="text-align:right;"> iOS</td>
</tr>
<tr>
<td style="text-align:left;">Cocos2d-x </td>
<td style="text-align:center;"> C++, Lua, JavaScript </td>
<td style="text-align:right;"> 参见支持平台和编程语言部分</td>
</tr>
<tr>
<td style="text-align:left;">Cocos2d-html5 </td>
<td style="text-align:center;"> JavaScript </td>
<td style="text-align:right;"> 支持HTML5的浏览器</td>
</tr>
<tr>
<td style="text-align:left;">ShinyCocos </td>
<td style="text-align:center;"> Ruby </td>
<td style="text-align:right;"> iOS</td>
</tr>
<tr>
<td style="text-align:left;">Cocos2d-windows </td>
<td style="text-align:center;"> C++ </td>
<td style="text-align:right;"> Windows XP/7</td>
</tr>
<tr>
<td style="text-align:left;">Cocos2d-android </td>
<td style="text-align:center;"> Java </td>
<td style="text-align:right;"> Android</td>
</tr>
<tr>
<td style="text-align:left;">Cocos2d-android-1 </td>
<td style="text-align:center;"> Java </td>
<td style="text-align:right;"> Android</td>
</tr>
<tr>
<td style="text-align:left;">Cocos2d-xna </td>
<td style="text-align:center;"> C# </td>
<td style="text-align:right;"> Microsoft and MonoGame platforms</td>
</tr>
<tr>
<td style="text-align:left;">Cocos2d-javascript </td>
<td style="text-align:center;"> JavaScript </td>
<td style="text-align:right;"> 支持HTML5的浏览器</td>
</tr>
<tr>
<td style="text-align:left;">Cocos-net </td>
<td style="text-align:center;"> C# </td>
<td style="text-align:right;"> Mono-supported 平台</td>
</tr>
<tr>
<td style="text-align:left;">Cocos3d </td>
<td style="text-align:center;"> Objective-C </td>
<td style="text-align:right;"> iOS</td>
</tr>
</tbody>
</table>
零基础学习Cocos2d-x-32015-05-24T00:00:00+00:00http://jiaxianhua.github.io/cocos2d-x/2015/05/24/cocos2dx-3<h2>Cocos API风格说明</h2>
<hr />
<p>适用版本:v3.0-beta2及更高版本</p>
<h3>两阶段构造器及静态create()函数</h3>
<hr />
<p>在Cocos2d-x引擎中,我们使用了两阶段构造器,这不仅指Objective-C实现文件(implementation),也与Symbian SDK及Bada SDK相似。我认为这一举措在C++编程中很不错。</p>
<p>第一阶段是运行C++类构造器。在C++类的默认构造器中,成员变量须设定为默认值。但我们不应在默认构造器中编写任何逻辑。例如</p>
<p><figure class="highlight"><pre><code class="language-c--" data-lang="c++"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4
5
6
7</pre></td><td class="code"><pre><span class="n">MyClass</span><span class="o">::</span><span class="n">MyClass</span><span class="p">()</span> <span class="c1">// c++ class constructor
</span><span class="o">:</span><span class="n"><em>data</span><span class="p">(</span><span class="nb">NULL</span><span class="p">)</span> <span class="c1">// set all member variables to default values
</span><span class="p">,</span><span class="n"></em>flag</span><span class="p">(</span><span class="nb">false</span><span class="p">)</span>
<span class="p">,</span><span class="n"><em>count</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">memset</span><span class="p">(</span><span class="n"></em>array</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">_array</span><span class="p">));</span> <span class="c1">// only set default values here, but not logics
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<p>我们之所以不应在这里编写任何逻辑,是因为C++默认构造器不能返回表明我们逻辑正确与否的bool 值。</p>
<p>使用C++关键词try/catch(捕获/异常)同样会给调用方返回失败状态,但是使用try/catch(捕获/异常)显然将增加你源代码编译后的二进制文件的大小。</p>
<p>第二阶段是调用MyClass::init()函数,如下。</p>
<p><figure class="highlight"><pre><code class="language-c--" data-lang="c++"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4
5
6</pre></td><td class="code"><pre><span class="n">bool</span> <span class="n">MyClass</span><span class="o">::</span><span class="n">initWithFilename</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span><span class="o">&</span> <span class="n">filename</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// just take loading texture as a sample, this behaviour can fail if the image file doesn't exist.
</span> <span class="n">bool</span> <span class="n">bReturnValue</span> <span class="o">=</span> <span class="n">loadTextureIntoMemory</span><span class="p">(</span><span class="n">filename</span><span class="p">);</span>
<span class="k">return</span> <span class="n">bReturnValue</span><span class="p">;</span>
<span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<p>所以,我们可以构建如下</p>
<p><figure class="highlight"><pre><code class="language-c--" data-lang="c++"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4
5
6
7
8
9</pre></td><td class="code"><pre><span class="n">MyClass</span> <span class="o">*</span><span class="n">obj</span> <span class="o">=</span> <span class="k">new</span> <span class="n">MyClass</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">true</span> <span class="o">==</span> <span class="n">obj</span><span class="o">-></span><span class="n">initWithFilename</span><span class="p">(</span><span class="s">"texture.png"</span><span class="p">))</span>
<span class="p">{</span>
<span class="c1">// congratulations, go ahead!
</span> <span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="c1">// error process
</span> <span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<p>在Cocos2d-x引擎中,我们已对这一两阶段构造器进行包装,并在静态函数create()中自动释放引用计数。除了单例模式,每一个cocos2d类都有自己的static CCClass *CCClass::create(...)方法。极力推荐这一方法。代码样本:</p>
<p><figure class="highlight"><pre><code class="language-c--" data-lang="c++"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3</pre></td><td class="code"><pre><span class="n">Sprite</span> <span class="o">*</span><span class="n">monster</span> <span class="o">=</span> <span class="n">Sprite</span><span class="o">::</span><span class="n">create</span><span class="p">(</span><span class="s">"Monster.png"</span><span class="p">);</span>
<span class="n">monster</span><span class="o">-></span><span class="n">setPosition</span><span class="p">(</span><span class="n">ccp</span><span class="p">(</span><span class="n">visibleSize</span><span class="p">.</span><span class="n">width</span><span class="o">/</span><span class="mi">2</span> <span class="o">+</span> <span class="n">origin</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="n">visibleSize</span><span class="p">.</span><span class="n">height</span><span class="o">/</span><span class="mi">2</span> <span class="o">+</span> <span class="n">origin</span><span class="p">.</span><span class="n">y</span><span class="p">));</span>
<span class="k">this</span><span class="o">-></span><span class="n">addChild</span><span class="p">(</span><span class="n">monster</span><span class="p">);</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<p>所以,如果你希望新建cocos2d的对象,比如Sprite, Label, Action,你必须首先从头文件或API文件中找到它的CocosClass::create() 方法。</p>
<h3>doSomething()</h3>
<p>这是最常见的函数名,在Cocos2d-x/-html5引擎中处处都有应用到。第一个字是一个动词,第二个字是一个名词。比如:replaceScene(CCScene *) 和 getTexture()。</p>
<h3>doWithResource()</h3>
<p>它是doSomething()方法的变体。在initWithTexture(CCTexture *) 和 initWithFilename(const std::string&)中,你经常可以看见这一函数名。</p>
<h3>onEventCallback()</h3>
<p>当你看到类似void onEnter()的函数名时,onAction类型表明这是一个回调函数。当引发一定条件时,其他类将调用这一方法。比如:</p>
<p><figure class="highlight"><pre><code class="language-c--" data-lang="c++"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4
5
6
7</pre></td><td class="code"><pre><span class="k">class</span> <span class="nc">Layer</span>
<span class="p">{</span>
<span class="k">public</span><span class="o">:</span>
<span class="k">virtual</span> <span class="kt">void</span> <span class="n">onEnter</span><span class="p">();</span>
<span class="k">virtual</span> <span class="kt">void</span> <span class="n">onExit</span><span class="p">();</span>
<span class="k">virtual</span> <span class="kt">void</span> <span class="n">onEnterTransitionDidFinish</span><span class="p">();</span>
<span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<h3>getInstance()</h3>
<p>在Cocos2d-x引擎中,如果你没有发现create(),只发现了getInstance()方法,它就属于单例模式类。比如,TextureCache::getInstance()。单例类对应的析构方式是destroyInstance()。 在v3.0之前,单例类的构造方式是CocosClass::sharedCocosClass(),比如TextureCache::sharedTextureCache()。这个方法在v3.0中仍然可以兼容,但不保证在v3.0更后面的版本中仍然保留。</p>
<h3>属性</h3>
<p>因为在C++ 和 C++11中没有"property" (“属性”)这个概念,所以我们在Cocos2d-x引擎中使用了许多getter和setters。</p>
<h3>setProperty()</h3>
<p>改变属性的值。这通常会影响到对象的行为。比如 _sprite->setPosition(ccp(0,0)) 会将精灵移动到左下角。</p>
<h3>getProperty()</h3>
<p>与setProperty不同,getProperty将不会改变对象的成员变量及行为。比如,我们通常使用 _director->getVisibleSize().width获得可见大小或窗口大小以计算对象的方位。</p>
<p>getProperty()函数的默认类型如下:</p>
<p><figure class="highlight"><pre><code class="language-c--" data-lang="c++"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1</pre></td><td class="code"><pre><span class="k">const</span> <span class="n">CCSize</span><span class="o">&</span> <span class="n">getSize</span><span class="p">()</span> <span class="k">const</span><span class="p">;</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<p>首先,根据性能优化,在此,我们返回CCSize引用,而不是构建另一个CCSize。当改变这一CCSize&时,该对象的内部成员变量将会受到影响,所以这一CCSize&属于常量。</p>
<p>第二,getSize()方法不应改变该对象的其他成员变量,所以这一函数本身属于常量。</p>
<h3>isProperty()</h3>
<p>和getProperty一样,但会返回一个boolean值。</p>
<p>总结起来:</p>
<p>如果属性为“只读”,将不会有setProperty(type)方法;
如果属性为一个bool值,将会有setProperty(bool)及 isProperty()方法。 比如:Sprite::isDirty()和Sprite::setDirty(bool bDirty)。
如果属性不是一个bool值,将会有 setProperty(type) 和 getProperty() 方法。比如: void Sprite::setTexture(Texture2D <em>) 和 Texture2D </em>CCSprite::getTexture()。</p>
零基础学习Cocos2d-x-22015-05-23T00:00:00+00:00http://jiaxianhua.github.io/cocos2d-x/2015/05/23/cocos2dx-2<h2>支持平台和编程语言</h2>
<hr />
<p>“o”代表支持并能正常工作, “i”代表支持但不在主干版本内,可以从历史版本里面找到
“w”代表正在研发</p>
<p>Cocos2d-x</p>
<table>
<thead>
<tr>
<th></th>
<th>平台</th>
<th>C++</th>
<th>Lua</th>
<th>Javascript</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>移动平台</strong></td>
<td>iOS</td>
<td>o</td>
<td>o</td>
<td>o</td>
</tr>
<tr>
<td></td>
<td>Android</td>
<td>o</td>
<td>o</td>
<td>o</td>
</tr>
<tr>
<td></td>
<td>WindowsPhone8</td>
<td>o</td>
<td>o</td>
<td></td>
</tr>
<tr>
<td></td>
<td>BlackBerry</td>
<td>i</td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>Marmalade</td>
<td>i</td>
<td></td>
<td></td>
</tr>
<tr>
<td><strong>桌面平台</strong></td>
<td>win32</td>
<td>o</td>
<td>o</td>
<td>o</td>
</tr>
<tr>
<td></td>
<td>Linux</td>
<td>o</td>
<td>o</td>
<td>o</td>
</tr>
<tr>
<td></td>
<td>Mac OS X</td>
<td>o</td>
<td>o</td>
<td>o</td>
</tr>
<tr>
<td></td>
<td>Win8 Metro</td>
<td>o</td>
<td>o</td>
<td></td>
</tr>
<tr>
<td></td>
<td>Native Client</td>
<td>i</td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td>Emscripten</td>
<td>i</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<h3>Cocos2d-html5</h3>
<table>
<thead>
<tr>
<th>浏览器</th>
<th>Canvas</th>
<th>WebGL</th>
</tr>
</thead>
<tbody>
<tr>
<td>Chrome</td>
<td>o</td>
<td>o</td>
</tr>
<tr>
<td>FireFox</td>
<td>o</td>
<td>o</td>
</tr>
<tr>
<td>Safari</td>
<td>o</td>
<td>o</td>
</tr>
<tr>
<td>IE 9及以上版本</td>
<td>o</td>
</tr>
<tr>
<td>移动浏览器</td>
<td>o</td>
<td>i</td>
</tr>
<tr>
<td>其他支持HTML5的浏览器</td>
<td>o</td>
<td>o</td>
</tr>
</tbody>
</table>
<p></p>
零基础学习Cocos2d-x-12015-05-22T00:00:00+00:00http://jiaxianhua.github.io/cocos2d-x/2015/05/22/cocos2dx-1<h2>【开发者指南】第一章:什么是Cocos2d-x</h2>
<hr />
<h3>Cocos2d-x</h3>
<hr />
<p>Cocos2d-x创始于2010年,它是一个开源跨平台的游戏引擎。它包含了很多强大的特性可以让游戏开发者轻松的创建出伟大的游戏作品。</p>
<h3>为什么选择Cocos2d-x</h3>
<hr />
<p>为什么选择Cocos2d-x而不是现有的其它游戏引擎呢?</p>
<ul>
<li>Cocos2d-x使用的为最新C++ API(请参阅这里)</li>
<li></li>
<li>跨平台-桌面和手机平台</li>
<li></li>
<li>支持在桌面开发环境对游戏进行测试或者调试,然后就可在手机,桌面或者目标平台中运行该游戏。</li>
<li></li>
<li>一个巨大的API集合,包括精灵,动作,动画,粒子特效,场景切换,计时器,事件处理(触摸,按键,加速,鼠标移动),声音,文件输入输出,持久化,骨骼,3D等。</li>
</ul>
<h3>阅读帮助</h3>
<hr />
<ul>
<li>auto用来创建局部变量。</li>
<li>using namespace cocos2d;用来缩短类型。</li>
<li>每一章都有可编译运行源代码示例用来演示本章中的概念。</li>
<li>类的名称,方法名和其它API组件会使用默认的字体来书写。比如:Sprite</li>
<li><em>italics</em> 字用来标识概念或者关键字。</li>
</ul>
<h3>帮助</h3>
<hr />
<ul>
<li><a href="http://discuss.cocos2d-x.org/">英文论坛</a></li>
<li><a href="http://www.cocoachina.com/bbs/thread.php?fid=41">中文论坛</a></li>
<li><a href="https://github.com/cocos2d/cocos2d-x/issues">Bug Tracker</a></li>
<li><a href="https://github.com/cocos2d/cocos2d-x/issues">缺陷 追踪</a></li>
<li>IRC。我们在IRC <a href="https://webchat.freenode.net/">Freenode</a>中的Cocos2d频道。</li>
</ul>
<h3>修订历史</h3>
<hr />
<ul>
<li>2014-10-28 - initial release</li>
<li>2014-10-28 - 首次发</li>
</ul>
零基础学习Cocos2d-x 2015-05-21T00:00:00+00:00http://jiaxianhua.github.io/cocos2d-x/2015/05/21/cocos2dx-0<p>Cocos2d-x是一个开源游戏引擎,游戏开发快速、简易、功能强大。其核心优势在于允许开发人员利用C++、Lua及Javascript来进行跨平台部署,覆盖平台包括iOS、Android、Windows Phone等多个平台,Cocos2d-x 3.x版本中新增了强大的3D功能,帮助开发者们方便开发出能够跨平台高效运行的2D/3D游戏。</p>
<ul>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=1925">什么是Cocos2d-x</a></li>
<li><a href="http://cn.cocos2d-x.org/article/index?type=cocos2d-x&url=/doc/cocos-docs-master/manual/framework/native/v2/getting-started/supported-platforms-and-programming-languages/zh.md">支持平台和编程语言</a></li>
<li><p><a href="http://cn.cocos2d-x.org/article/index?type=cocos2d-x&url=/doc/cocos-docs-master/manual/framework/native/v3/easy-to-learn-api-style/zh.md%20Cocos">API风格说明</a></p></li>
<li><p><a href="http://cn.cocos2d-x.org/article/index?type=cocos2d-x&url=/doc/cocos-docs-master/manual/framework/native/v3/about/relationships-in-cocos2d-family/zh.md">Cocos2d系列产品关系</a></p></li>
<li><a href="http://cn.cocos2d-x.org/article/index?type=cocos2d-x&url=/doc/cocos-docs-master/manual/framework/native/v2/getting-started/architecture-and-directory-structure/zh.md">架构和目录结构</a></li>
<li><a href="http://cn.cocos2d-x.org/doc/cocos2d-x-3.0/index.html%20Cocos2d-x%20v3.0">API文档</a></li>
</ul>
<h3>环境搭建</h3>
<hr />
<p>环境搭建是学习Cocos引擎的第一步。环境搭建并不难,选择自己需要的开发平台,不到1个小时就可以搞定。</p>
<p>Mac上Cocos2d-x的iOS/Android多平台环境搭建。提供环境搭建文档和开发者经验总结供大家学习参考。</p>
<ul>
<li><a href="http://cn.cocos2d-x.org/article/index?type=cocos2d-x&url=/doc/cocos-docs-master/manual/framework/native/v3/getting-started/setting-up-development-environments-on-mac-with-xcode/zh.md">【文档】在Mac下搭建Cocos2d-x iOS开发环境</a></li>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=1157">【经验分享】Cocos2d-x 3.0 Android环境搭建</a></li>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=2533">【经验分享】Cocos2d-x 3.3 Mac下搭建Android开发环境</a></li>
<li><a href="http://cn.cocos2d-x.org/article/index?type=cocos2d-x&url=/doc/cocos-docs-master/manual/framework/native/v3/getting-started/setting-up-development-environments-on-mac-with-eclipse/zh.md">【文档】在Mac下搭建Android开发环境</a></li>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=1367">【经验分享】Cocos2d-x 3.1 环境搭建和创建工程</a></li>
</ul>
<p>Windows/Windows Phone上Cocos2d-x的iOS/Android多平台环境搭建。提供环境搭建文档和开发者经验总结供大家学习参考。</p>
<ul>
<li><a href="http://cn.cocos2d-x.org/article/index?type=cocos2d-x&url=/doc/cocos-docs-master/manual/framework/native/v3/getting-started/setting-up-development-environments-on-windows7-with-vs2013/zh.md">【文档】在Windows7上搭建Cocos2d-x Win32开发环境</a></li>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=2544">【经验分享】Windows7上搭建Cocos2d-x 3.4开发环境</a></li>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=1259">【经验分享】Cocos2d-x Android 环境搭建问题汇总</a></li>
<li><p><a href="http://cn.cocos2d-x.org/tutorial/show?id=1941">【经验分享】Windows Phone 8开发环境搭建</a></p></li>
<li><p><a href="http://cn.cocos2d-x.org/article/index?type=cocos2d-x&url=/doc/cocos-docs-master/manual/framework/native/v3/getting-started/setting-up-development-environments-on-windows7-with-eclipse/zh.m">【文档】在Windows7上搭建Cocos2d-x Android开发环境</a></p></li>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=2532">【经验分享】Win8.1下Cocos2d-x 3.4环境搭建</a></li>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=781">【经验分享】Cocos2d-x 3.x 全平台新手开发配置教程</a></li>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=1478">【经验分享】Android平台新手开发环境配置教程</a></li>
</ul>
<p>Cocos Studio升级为cocos。更优秀的产品、更优质的服务。游戏开发一站式解决方案, 低成本、高效率;高性能,一次制作,多终端平台共享;Windows与Mac同步发布更新,团队协作更容易;丰富的插件素材库,游戏开发更灵活更便捷。</p>
<ul>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=2407">学习使用Cocos构建一个完整项目</a></li>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=2438">学习使用Cocos制作《闹钟》</a></li>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=2496">Cocos如何绑定Lua自定义类</a></li>
<li><p><a href="http://cn.cocos2d-x.org/tutorial/show?id=2650%20Cocos%20Studio">扩展说明</a></p></li>
<li><p><a href="http://cn.cocos2d-x.org/tutorial/show?id=2348">Cocos引擎常见问题FAQ</a></p></li>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=2419%20Cocos%20v2.1.2">Beta打包功能简介</a></li>
<li><a href="http://cn.cocos2d-x.org/tutorial/show?id=2531">Cocos与Cocos2d-x协作教程--多分辨率支持</a></li>
</ul>
iOS证书说明和发布内购流程整理2015-05-20T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/20/ios-app-store<h2>摘要</h2>
<hr />
<p>网上关于苹果证书生成和设置的教程比较多,但大多数只是讲了相关流程和步骤,有的也只是简要进行了相关说明,总之介绍的不够详细,自己最初在接触的时候也是比较困惑,下面进行总结</p>
<h3>关于证书</h3>
<hr />
<ol>
<li><p>首先通过钥匙串访问——证书助理——从证书颁发机构请求证书——填写证书信息(邮箱,常用名称,存储到磁盘)——存储为(自定义名称.certSigningReuqest,简称CSR文件,只是为了提交到苹果开发者账号中,然后就没用了)到本地</p></li>
<li><p>苹果开发者账号中,创建证书(Development和Production)——上传CSR文件——下载证书运行 ( xxx.cer文件)</p></li>
</ol>
<blockquote><p>注意:只有在当前电脑中生成本地生成证书,上传到苹果开发账号,然后下载cer文件运行后,钥匙串中才有证书以及对应的秘钥</p></blockquote>
<p><img src="/assets/img/ios/Certificate-1.jpg" alt="Certificate-1" />
<img src="/assets/img/ios/Certificate-2.jpg" alt="Certificate-2" />
<img src="/assets/img/ios/Certificate-3.png" alt="Certificate-3" />
<img src="/assets/img/ios/Certificate-4.png" alt="Certificate-4" />
<img src="/assets/img/ios/Certificate-5.png" alt="Certificate-5" /></p>
<p>如果开发者B,登录开发者账号,下载证书(cer文件)运行,<strong>只有证书没有秘钥,是不能正常使用的</strong></p>
<p><img src="/assets/img/ios/Certificate-6.jpg" alt="Certificate-6" /></p>
<p><strong>所以如果有新同事加入到开发组的时候,应该从本地钥匙串中选择证书,导出p12文件(包含证书和秘钥)给同事。另外可以给同事一份Provisioning Profiles文件(配置文件),用于本地开发识别测试设备</strong></p>
<p>导出p12文件:钥匙串——选择证书——右键导出——存储为——设置p12文件密码</p>
<p>(发给同事后,双击p12文件,输入密码,本地安装证书成功)</p>
<p>需要强调一点,证书和项目关系其实并不大,证书一般有效期只有一年,当证书过期后,只需要重新生成一份证书,上传到开发者账号就行,同时因为原有证书过期,需要重新生成Provisioning Profiles文件。然后给同事们最新的p12文件和Provisioning Profiles文件就行</p>
<p>所以开发者账号中的证书,配置文件是可以放心操作的(比如误删了,或者找不到证书秘钥了)</p>
<h4>xcode中添加苹果开发者账号</h4>
<p>Xcode工具栏——Xcode——Preferences——Accounts—— 左下角 Add Apple ID——输入苹果账号,密码</p>
<p>在项目的target——general——team中可以选择项目对应的开发者账号</p>
<p><img src="/assets/img/ios/Certificate-7.png" alt="Certificate-7" /></p>
<p>(当bulid的新设备未在开发者账号的devices添加devicetoken的时候,xcode会进行提示无法识别设备,可以在xcode中fix issue,xcode会自动在开发者账号中,创建一个新的针对这个设备的Provisioning Profiles配置文件,然后安装到本地,唯一的不好就是开发者账号的配置文件下会有很多零散的配置文件)</p>
<h3>关于App的发布</h3>
<hr />
<p>修改项目的version,以及项目的版本debug为release</p>
<p><strong>(debug改为release后需要进行测试,一些第三方类库可能release版会有一些不兼容)</strong></p>
<p>Product——Scheme——Edit Scheme 修改 Run/Test/Analyze/Archive 的build configuration (发布的时候,只需要Archive就可以了)</p>
<p><img src="/assets/img/ios/Certificate-8.jpg" alt="Certificate-8" /></p>
<p>苹果开发者中心——iTunes Connect——我的APP——创建/选择应用——填写基本修改/添加新版本(构建版本)</p>
<h4>发布验证</h4>
<p>Product——Desination——选择iOS Device</p>
<p>Product——Archive——右侧点击Validate——选择证书——validate——等待——Validate Successful——右侧点击Submit to App Store(提交构建版本)——Submission Successful</p>
<p><img src="/assets/img/ios/Certificate-9.jpg" alt="Certificate-9" />
<img src="/assets/img/ios/Certificate-10.jpg" alt="Certificate-10" />
<img src="/assets/img/ios/Certificate-11.jpg" alt="Certificate-11" /></p>
<p>苹果开发者中心——iTunes Connect——我的APP——选择应用——提交构建版本成功——选择自动发布/手动发布——提交审核</p>
<p>等待审核</p>
<h4>关于苹果内购</h4>
<p>传送门:<a href="http://mobile.51cto.com/iphone-410162.htm">专题:iOS应用内置付费IAP</a></p>
<p><img src="/assets/img/ios/Certificate-12.png" alt="Certificate-12" /></p>
<p>购买流程:</p>
<ol>
<li><p>程序向服务器发送请求,获得一份产品列表。</p></li>
<li><p>服务器返回包含产品标识符的列表。</p></li>
<li><p>程序向App Store发送请求,得到产品的信息。</p></li>
<li><p>App Store返回产品信息。</p></li>
<li><p>程序把返回的产品信息显示给用户(App的store界面)</p></li>
<li><p>用户选择某个产品</p></li>
<li><p>程序向App Store发送支付请求</p></li>
<li><p>App Store处理支付请求并返回交易完成信息。</p></li>
<li><p>程序从信息中获得数据,并发送至服务器。</p></li>
<li><p>服务器纪录数据,并进行审(我们的)查。</p></li>
<li><p>服务器将数据发给App Store来验证该交易的有效性。</p></li>
<li><p>App Store对收到的数据进行解析,返回该数据和说明其是否有效的标识。</p></li>
<li><p>服务器读取返回的数据,确定用户购买的内容。</p></li>
<li><p>服务器将购买的内容传递给程序。</p></li>
</ol>
<p><strong>因为涉及到ipa破解软件,存在假购买情况,需要后台进行订单验证</strong></p>
<p>下图自己画的基于ios7的购买流程图(前后台接口调用验证等)</p>
<p><img src="/assets/img/ios/Certificate-13.png" alt="Certificate-13" /></p>
TableView-142015-05-19T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/19/tableview-14<h2>Displaying a Refresh Control for Table Views</h2>
<hr />
<h3>Problem</h3>
<hr />
<p>You want to display a nice refresh UI control on top of your table views that allows your users to intuitively pull down the table view in order to update its contents. Examples of a refresh control in two of its different states are shown in <em>Figure 4-25</em>.</p>
<p><img src="/assets/img/ios/TableView-25.png" alt="TableView-25" /></p>
<p><em>Figure 4-25. Two different states of the refresh control</em></p>
<h3>Solution</h3>
<hr />
<p>Simply create a table view controller and set its refresh Control property to a new instance of UIRefreshControl class, as shown here:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void) viewDidLoad {
[super viewDidLoad];</p>
<pre><code>/* Create the refresh control */
self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self
action:@selector(handleRefresh:)
forControlEvents:UIControlEventValueChanged];
</code></pre>
<p>}</code></pre></figure></p>
<p>Refresh controls are a new UI component added to the iOS 6 SDK. They are simple visual indicators that appear on top of table views and tell the user that something is about to get updated. For instance, prior to iOS 6, in order to refresh your mailbox in the Mail app, you had to press a refresh button. In iOS 6, now you can simply drag the list of your emails down, as if you wanted to see what’s above there in the list that you haven’t read already. Once iOS detects this gesture of yours, it will trigger a refresh. Isn’t that cool? Twitter’s iPhone app started this whole thing when they added a refresh control to their apps, so kudos to them for this. Apple has realized this is in fact a really nice and intuitive way of updating table views and has since added a dedicated component to the SDK to implement it. The class name for this component is UIRefresh Control.</p>
<p>Create a new instance of this class simply by calling its init method. Once you are done, add this instance to your table view controller, as described in the Solution section of this recipe.</p>
<p>Now you’ll want to know when the user has triggered a refresh on your table view. To do this, simply call the <strong>addTarget:action:forControlEvents:</strong> instance method of your refresh control and pass the target object and a selector on that object that takes care of the refresh for you. Pass UIControlEventValueChanged to the forControlEvents parameter of this method.</p>
<p>Here—I want to demonstrate this to you. In this example, we will have a table view controller that displays date and time formatted as strings. Once the user refreshes the list by pulling it down, we will add the current date and time again to the list and refresh our table view. This way, every time the user pulls the list down, it triggers a refresh that will allow us to add the current date and time to the list and refresh the table view to display the new date and time. So let’s start in the implementation file of our table view controller and define our refresh control and our data source:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import "ViewController.h"</p>
<p>@interface ViewController ()
@property (nonatomic, strong) NSMutableArray *times;
@end</code></pre></figure></p>
<p>The times property is a simple mutable array that will contain all the instances of NSDate in it as the user refreshes the table view. We have already seen the initialization of our table view controller in the Solution section of this recipe, so I won’t write it again here. But as you saw there, we have hooked the UIControlEventValueChanged event of our refresh control to a method called handleRefresh:. In this method, all we are going to do is add the current date and time to our array of date and times and then refresh the table view:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void) handleRefresh:(id)paramSender {
/<em> Put a bit of delay between when the refresh control is released and when we actually do the refreshing to make the UI look a bit smoother than just doing the update without the animation </em>/
int64_t delayInSeconds = 1.0f;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
/<em> Add the current date to the list of dates that we have so that when the table view is refreshed, a new item will appear on the screen so that the user will see the difference between the before and the after of the refresh </em>/
[self.times addObject:[NSDate date]];</p>
<pre><code> [self.refreshControl endRefreshing];
[self.tableView reloadData];
});
</code></pre>
<p>}</code></pre></figure></p>
<p>Last but not least, we will provide the date to our table view through the table view’s delegate and data source methods:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void) viewDidLoad {
[super viewDidLoad];</p>
<pre><code>self.times = [NSMutableArray arrayWithObject:[NSDate date]];
/* Create the refresh control */
self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self
action:@selector(handleRefresh:)
forControlEvents:UIControlEventValueChanged];
</code></pre>
<p>}</p>
<ul>
<li><p>(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}</p></li>
<li><p>(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.times.count;
}</p></li>
<li><p>(UITableViewCell <em>)tableView:(UITableView </em>)tableView cellForRowAtIndexPath:(NSIndexPath <em>)indexPath {
static NSString </em>CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];</p>
<p> if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}</p>
<p> cell.textLabel.text = [NSString stringWithFormat:@"%@",
self.times[indexPath.row]];</p>
<p> return cell;
}</code></pre></figure></p></li>
</ul>
<p>Give this a go in either the simulator or the device. Once you open the app, at first you will see only one date/time added to the list. Keep dragging the table view down to get more items in the list (see <em>Figure 4-26</em>).</p>
<p><img src="/assets/img/ios/TableView-26.png" alt="TableView-26" /></p>
<p><em>Figure 4-26. Our table view controller is populated by a new item every time it is refreshed</em></p>
TableView-132015-05-19T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/19/tableview-13<h2>Utilizing the UITableViewController for Easy Creation of Table Views</h2>
<hr />
<h3>Problem</h3>
<hr />
<p>You want to be able to create table views quickly.</p>
<h3>Solution</h3>
<hr />
<p>Use the <em>UITableViewController</em> view controller, which by default comes with a table view controller.</p>
<p>The iOS SDK contains a really handy class called UITableViewController that comes predefined with a table view instance inside it. In order to take advantage of this class, all you have to really do is create a new class that subclasses the aforementioned class. Here, I will walk you through the steps necessary to create a new Xcode project that utilizes the table view controller:</p>
<ol>
<li>In Xcode, from the menu bar, choose File → New → Project...</li>
<li>On the lefthand side of the screen, make sure the iOS category is selected. Then choose the Application subcategory. On the righthand side, choose the Empty Application template and then press the Next button, as shown in <em>Figure 4-20</em>.</li>
<li>In the next screen, simply choose a name for your project and make sure that you are using Automatic Reference Counting. Also make sure everything except for the Organization Name and the Company Identifier in your dialog is the same as the one that I am demonstrating to you in <em>Figure 4-21</em>. Once you are done, press the Next button.</li>
<li>In the next screen, you are given the opportunity to save your application to disk. Simply save the application in a place that makes sense to you and press the Create button.</li>
<li>In Xcode, choose the File → New → File... menu.</li>
<li>In the dialog, make sure iOS is the main category on the lefthand side and that Cocoa Touch is the subcategory that is selected. Then on the righthand side of the dialog, choose the Objective-C class as shown in <em>Figure 4-22</em>.</li>
<li>In the next screen, you get to choose the superclass of your new class. This step is very important. Make sure that you set your superclass to <strong>UITableView Controller</strong>. Also make sure the rest of your settings are the same as those that I am demonstrating in <em>Figure 4-23</em>. After you are done, press the Next button.</li>
<li>In the next screen, you get the chance to save your table view controller in your project. Go on, save it as the ViewController class and press the Create button.</li>
<li>In the implementation file of your app delegate, remember to import this view controller’s header file and then create an instance of this class and set it as the root view controller of your application, as shown here:</li>
</ol>
<p><img src="/assets/img/ios/TableView-20.png" alt="TableView-20" /></p>
<p><em>Figure 4-20. Creating a new empty application that will later contain our table view controller</em></p>
<p><img src="/assets/img/ios/TableView-21.png" alt="TableView-21" /></p>
<p><em>Figure 4-21. Configuring our new empty application in Xcode</em></p>
<p><img src="/assets/img/ios/TableView-22.png" alt="TableView-22" /></p>
<p><em>Figure 4-21. Configuring our new empty application in Xcode</em></p>
<p><img src="/assets/img/ios/TableView-23.png" alt="TableView-23" /></p>
<p><em>Figure 4-23. Setting the superclass of our new object that will become the table view controller</em></p>
<p>Now if you try to compile your project, you will see that the compiler will give you the following warnings:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">ViewController.m:47:2: Potentially incomplete method implementation.
ViewController.m:54:2: Incomplete method implementation.</code></pre></figure></p>
<p>This simply tells you that there are warnings that you have to take care of in the implementation file of your view controller. If you open this file, you will see that Apple has inserted <strong>#warning</strong> macros in the table view controller class template, which are causing these warnings to be displayed on your screen. One warning is placed inside the <em>numberOfSectionsInTableView:</em> method and the other one is inside the <em>tableView:numberOfRowsInSection:</em> method. The reason we are seeing these warnings is that we have not coded the logic for these methods. The minimum information that the table view controller must have is the number of sections to display, the number of rows to display, and the cell object to be displayed for each row. The reason you are not seeing any warnings for the lack of cell object implementation is that Apple by default provides a dummy implementation of this method that creates empty cells for you.</p>
<blockquote><p>The table view controller by default is the data source and the delegate of the table view. You do not have to specify a delegate or a data source separately to the table view.</p></blockquote>
<p>Now let’s go into the implementation of our table view controller and make sure that we have an array of strings (just as an example) that we can feed into our table view:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import "ViewController.h"</p>
<p>@interface ViewController ()
@property (nonatomic, strong) NSArray *items;
@end</p>
<p>@implementation ViewController</p>
<ul>
<li><p>(void) viewDidLoad {
[super viewDidLoad];</p>
<p> // Custom initialization
self.items = @[ @"Anthony Robbins", @"Steven Paul Jobs", @"Paul Gilbert", @"Yngwie Malmsteen"];
}</p></li>
<li><p>(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}</p></li>
<li><p>(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.items.count;
}</p></li>
<li><p>(UITableViewCell <em>)tableView:(UITableView </em>)tableView cellForRowAtIndexPath:(NSIndexPath <em>)indexPath {
static NSString </em>CellIdentifier = @"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];</p>
<p> if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}</p>
<p> cell.textLabel.text = self.items[indexPath.row];</p>
<p> return cell;
}</code></pre></figure></p></li>
</ul>
<p>Now if we run our app, we will see something similar to what is shown in <em>Figure 4-24</em>.</p>
<p><img src="/assets/img/ios/TableView-24.png" alt="TableView-24" /></p>
<p><em>Figure 4-24. Our strings are properly displayed in the table view</em></p>
<p>That’s pretty much all there is to know about table view controllers. Remember, as mentioned before, that your table view controller is the delegate and the data source of your table view now. So you can implement the methods in the UITableViewData Source protocol as well as the <em>UITableViewDelegate</em> protocol’s methods right in the implementation of your table view controller.</p>
TableView-122015-05-18T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/18/tableview-12<h2>Deleting Cells and Sections from Table Views</h2>
<hr />
<h3>Problem</h3>
<hr />
<p>You want to delete sections and/or cells from table views using animations.</p>
<h3>Solution</h3>
<hr />
<p>In order to delete sections from a table view, follow these steps:</p>
<ol>
<li><p>First delete the section(s) in your data source, whether you are using a data model like Core Data or a dictionary/array.</p></li>
<li><p>Invoke the deleteSections:withRowAnimation: instance method of UITableView on your table view. The first parameter that you need to pass to this method is of type NSIndexSet and this object can be instantiated using the indexSetWithIndex: class method of NSIndexSet class, where the given index is an unsigned integer. Using this approach, you will be able to delete only one section at a time. If you intend to delete more than one section at a time, use the indexSetWithIndexesInRange: class method of NSIndexSet to create the index set using a range and pass that index set to the aforementioned instance method of UITableView.</p></li>
</ol>
<p>If you want to delete cells from your table view, follow these steps:</p>
<ol>
<li>First, delete the cell(s) from your data source. Again, it doesn’t matter if you are using Core Data, a simple dictionary, array, or anything else. The important thing is to delete the objects that represent the table view cells from your data source.</li>
</ol>
<h3>Discussion</h3>
<hr />
<p>In your UI code, sometimes you might need to delete cells and/or sections. For instance, you might have a switch, and when the switch is turned on by the user, you might want to insert a few rows into your table view. After the switch is turned off by the user, you will then want to delete those rows. It’s not always table view cells (rows) that you have to delete. Sometimes you might need to delete a whole section or a few sections simultaneously from your table view. The key for deleting cells and sections from table views is to first delete the data corresponding to those cells/sections from your data source, and then call the appropriate deletion method on the table view. After the deletion method finishes, the table view will refer back to its data source object. If the number of cells/sections in the data source doesn’t match the number of cells/sections in the table view after the deletion is complete, your app will crash. But don’t worry—if you ever do make this mistake, the debug text that gets printed to the console is descriptive enough to point you in the right direction.</p>
<p>Let’s have a look at how we can delete sections from a table view. For this recipe, we will display a table view on a view controller that is displayed inside a navigation controller. Inside the table view, we will display two sections, one for odd numbers and another for even numbers. We will only display 1, 3, 5, and 7 for odd numbers and 0, 2, 4, and 6 for even numbers. For the first exercise, we are going to place a navigation bar button on our navigation bar and make that button responsible for deleting the section with odd numbers in it. <em>Figure 4-16</em> shows what we want the results to look like.</p>
<p><img src="/assets/img/ios/TableView-16.png" alt="TableView-16" /></p>
<p><em>Figure 4-16. The user interface to display two sections in a table view and a button that will delete the Odd Numbers section</em></p>
<p>First things first. Let’s define our view controller:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import <UIKit/UIKit.h></p>
<p>@interface TableViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) UITableView <em>tableViewNumbers;
@property (nonatomic, strong) NSMutableDictionary </em>dictionaryOfNumbers;
@property (nonatomic, strong) UIBarButtonItem *barButtonAction;
@end</code></pre></figure></p>
<p>The tableViewNumbers property is our table view. The barButtonAction property is the bar button that we’ll display on the navigation bar. Last but not least, the dictionary OfNumbers property is our data source for the table view. In this dictionary, we will place two values of type NSMutableArray that contain our numbers of type NSNumber. They are mutable arrays, so that, later in this chapter, we will be able to delete them individually from the arrays in the dictionary. We will keep the keys for these arrays in our dictionary as static values in the implementation file of our view controller, so that we can later simply extract them from the dictionary using the static keys (if the keys were not static, finding our arrays in the dictionary would have to be done with string comparison, which is slightly more time-consuming than simply associating the object with a static key that doesn’t change during the lifetime of our view controller). Now let’s define the static string keys for our arrays inside the data source dictionary:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import "TableViewController.h"</p>
<p>@implementation TableViewController</p>
<p>static NSString <em>SectionOddNumbers = @"Odd Numbers";
static NSString </em>SectionEvenNumbers = @"Even Numbers";</p>
<p>@end</code></pre></figure></p>
<p>We now need to populate our data source dictionary with values before we create our table view. Here is the simple method that will take care of populating the dictionary for us:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#pragma mark - Populating the Data Source Dictionary
- (void) constructDictionaryOfNumbers {
self.dictionaryOfNumbers = [[NSMutableDictionary alloc] init];
NSMutableArray *arrayOfEvenNumbers =
[[NSMutableArray alloc] initWithObjects:
[NSNumber numberWithUnsignedInteger:0],
[NSNumber numberWithUnsignedInteger:2],
[NSNumber numberWithUnsignedInteger:4],
[NSNumber numberWithUnsignedInteger:6],
nil];</p>
<p>NSMutableArray *arrayOfOddNumbers =
[[NSMutableArray alloc] initWithObjects:
[NSNumber numberWithUnsignedInteger:1],
[NSNumber numberWithUnsignedInteger:3],
[NSNumber numberWithUnsignedInteger:5],
[NSNumber numberWithUnsignedInteger:7],
nil];</p>
<p>[self.dictionaryOfNumbers setObject:arrayOfEvenNumbers
forKey:SectionEvenNumbers];
[self.dictionaryOfNumbers setObject:arrayOfOddNumbers
forKey:SectionOddNumbers];
}</code></pre></figure></p>
<p>So far so good? As you can see, we have two arrays, each of which contains some
numbers (one odd and the other even numbers) and we associate them with the SectionEvenNumbers and SectionOddNumbers keys that we declared before in the implementation file of our view controller. Now let’s go ahead and instantiate our table view:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)viewDidLoad {
[super viewDidLoad];</p>
<p>[self constructDictionaryOfNumbers];
self.barButtonAction =
[[UIBarButtonItem alloc] initWithTitle:@"Delete Odd Numbers"
style:UIBarButtonItemStylePlain
target:self action:@selector(deleteOddNumbersSection:)];
[self.navigationItem setRightBarButtonItem:self.barButtonAction animated:NO];
self.tableViewNumbers = [[UITableView alloc]
initWithFrame:self.view.frame
style:UITableViewStyleGrouped];
self.tableViewNumbers.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.tableViewNumbers];
self.tableViewNumbers.delegate = self;
self.tableViewNumbers.dataSource = self;
}</code></pre></figure></p>
<p>The next thing we need to do is to populate our table view with data inside our data source dictionary:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#pragma mark - Table View Data Source
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
NSInteger result = 0;
result = [[self.dictionaryOfNumbers allKeys] count];
return result;
}</p>
<ul>
<li><p>(NSInteger) tableView:(UITableView <em>)tableView
numberOfRowsInSection:(NSInteger)section {
NSInteger result = 0;
NSString </em>sectionNameInDictionary = [[self.dictionaryOfNumbers allKeys] objectAtIndex:section];
NSArray *sectionArray = [self.dictionaryOfNumbers objectForKey: sectionNameInDictionary];
result = [sectionArray count];
return result;
}</p></li>
<li><p>(UITableViewCell <em>) tableView:(UITableView </em>)tableView cellForRowAtIndexPath:(NSIndexPath <em>)indexPath {
UITableViewCell </em>result = nil;
static NSString *CellIdentifier = @"NumbersCellIdentifier";
result = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];</p>
<p> if (result == nil) {
result = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}</p>
<p> NSString <em>sectionNameInDictionary = [[self.dictionaryOfNumbers allKeys] objectAtIndex:indexPath.section];
NSArray </em>sectionArray = [self.dictionaryOfNumbers objectForKey: sectionNameInDictionary];
NSNumber *number = [sectionArray objectAtIndex:indexPath.row];
result.textLabel.text = [NSString stringWithFormat:@"%lu", (unsigned long)[number unsignedIntegerValue]];</p>
<p> return result;
}</p></li>
<li><p>(NSString <em>) tableView:(UITableView </em>)tableView titleForHeaderInSection:(NSInteger)section {
NSString *result = nil;
result = [[self.dictionaryOfNumbers allKeys] objectAtIndex:section];
return result;
}</code></pre></figure></p></li>
</ul>
<p>Our navigation button is linked to the deleteOddNumbersSection: selector. This is a method we are going to code now. The purpose of this method, as its name implies, is to find the section that corresponds to all odd numbers in our data source and the table view, and remove that section from both of these. Here is how we will do it:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void) deleteOddNumbersSection:(id)paramSender {
/<em> First remove the section from our data source </em>/
NSString *key = SectionOddNumbers;
NSInteger indexForKey = [[self.dictionaryOfNumbers allKeys]
indexOfObject:key];
if (indexForKey == NSNotFound) {
NSLog(@"Could not find the section in the data source.");
return;
}</p>
<pre><code>[self.dictionaryOfNumbers removeObjectForKey:key];
/* Then delete the section from the table view */
NSIndexSet *sectionToDelete = [NSIndexSet indexSetWithIndex:indexForKey];
[self.tableViewNumbers deleteSections:sectionToDelete
withRowAnimation:UITableViewRowAnimationAutomatic];
/* Finally, remove the button from the navigation baras it is not useful any longer */
[self.navigationItem setRightBarButtonItem:nil animated:YES];
</code></pre>
<p>}</code></pre></figure></p>
<p>Simple enough. Now, when the user presses the navigation bar button, the Odd Num- bers section will disappear from the table view. You can note that there is an animation that gets committed on the table view while the section is being deleted. This is because of the <em>UITableViewRowAnimationAutomatic</em> animation type that we are passing to the <em>withRowAnimation:</em> parameter of the <em>deleteSections:withRowAnimation:</em> method of our table view. Now run the app in iOS Simulator and select Debug → Toggle Slow Ani- mations. Then attempt to press the navigation bar button and see what happens. You can see the deletion animation in slow motion. It’s neat, isn’t it? After the deletion is completed, our app will look similar to <em>Figure 4-17</em>.</p>
<p><img src="/assets/img/ios/TableView-17.png" alt="TableView-17" /></p>
<p><em>Figure 4-17. The section containing odd numbers is removed from the table view</em></p>
<p>We now know how to delete sections from table views. Let’s move to deleting cells. We are going to change the functionality of our navigation bar button so that when it is pressed, it will delete all cells in all sections of our table view with a numerical value greater than 2. That includes all odd and even numbers greater than 2. So let’s change our navigation bar button item in the viewDidLoad method of our view controller:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)viewDidLoad {
[super viewDidLoad];</p>
<pre><code>[self constructDictionaryOfNumbers];
self.barButtonAction =
[[UIBarButtonItem alloc] initWithTitle:@"Delete Numbers &gt; 2" style:UIBarButtonItemStylePlain target:self action:@selector(deleteNumbersGreaterThan2:)];
[self.navigationItem setRightBarButtonItem:self.barButtonAction animated:NO];
self.tableViewNumbers = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStyleGrouped];
self.tableViewNumbers.autoresizingMask = UIViewAutoresizingFlexibleWidth |UIViewAutoresizingFlexibleHeight;
self.tableViewNumbers.delegate = self;
self.tableViewNumbers.dataSource = self;
[self.view addSubview:self.tableViewNumbers];
</code></pre>
<p>}</code></pre></figure></p>
<p><em>Figure 4-18</em> shows the results of our app running in iPhone Simulator.</p>
<p><img src="/assets/img/ios/TableView-18.png" alt="TableView-18" /></p>
<p><em>Figure 4-18. A button that will delete all cells containing a number greater than 2</em></p>
<p>The navigation bar button is now connected to the deleteNumbersGreaterThan2: selec- tor. This is a method that we have to implement in our view controller, but before jumping into coding it straightaway, let’s first define what this method should do:</p>
<ol>
<li>Find both arrays of odd and even numbers in our data source and grab the index paths (of type NSIndexPath) of those numbers that are greater than 2. We will use these index paths to later delete the corresponding cells from the table view.</li>
<li>Delete all the numbers greater than 2 from our data source, in both the even and odd number dictionaries.</li>
<li>Delete the relevant cells from the table view. We collected the index paths of these cells in the first step.</li>
<li>Remove the navigation bar button, since it won’t be of any use after the relevant cells have been deleted from the data source and the table view. Alternatively, if you want, you could just disable this button—but I think removing that button provides a better user experience, since a disabled button is really of no use to the user.</li>
</ol>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void) deleteNumbersGreaterThan2:(id)paramSender {
NSMutableArray <em>arrayOfIndexPathsToDelete = [[NSMutableArray alloc] init];
NSMutableArray </em>arrayOfNumberObjectsToDelete = [[NSMutableArray alloc] init];</p>
<pre><code>/* Step 1: gather the objects we have to delete from our data source and their index paths */
__block NSUInteger keyIndex = 0;
[self.dictionaryOfNumbers enumerateKeysAndObjectsUsingBlock:
^(NSString *key, NSMutableArray *object, BOOL *stop) {
[object enumerateObjectsUsingBlock:
^(NSNumber *number, NSUInteger numberIndex, BOOL *stop) {
if ([number unsignedIntegerValue] &gt; 2) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:numberIndex inSection:keyIndex];
[arrayOfIndexPathsToDelete addObject:indexPath];
[arrayOfNumberObjectsToDelete addObject:number];
}
}];
keyIndex++;
}];
/* Step 2: delete the objects from the data source */
if ([arrayOfNumberObjectsToDelete count] &gt; 0) {
NSMutableArray *arrayOfOddNumbers = [self.dictionaryOfNumbers objectForKey:SectionOddNumbers];
NSMutableArray *arrayOfEvenNumbers = [self.dictionaryOfNumbers objectForKey:SectionEvenNumbers];
[arrayOfNumberObjectsToDelete enumerateObjectsUsingBlock:
^(NSNumber *numberToDelete, NSUInteger idx, BOOL *stop) {
if ([arrayOfOddNumbers indexOfObject:numberToDelete] != NSNotFound) {
[arrayOfOddNumbers removeObject:numberToDelete];
}
if ([arrayOfEvenNumbers indexOfObject:numberToDelete]
!= NSNotFound) {
[arrayOfEvenNumbers removeObject:numberToDelete];
}
[arrayOfEvenNumbers removeObject:numberToDelete];
}];
}
/* Step 3: delete the cells that correspond to the objects */
NSArray *arrayOfPaths = [[NSArray alloc] initWithArray:arrayOfIndexPathsToDelete];
[self.tableViewNumbers deleteRowsAtIndexPaths:arrayOfPaths withRowAnimation:UITableViewRowAnimationAutomatic];
[self.navigationItem setRightBarButtonItem:nil animated:YES];
</code></pre>
<p>}</code></pre></figure></p>
<p>After the user presses the button on the navigation bar, all cells containing a number greater than 2 will be deleted from our data source, and the table view and our app will look like <em>Figure 4-19</em>.</p>
<p><img src="/assets/img/ios/TableView-19.png" alt="TableView-19" /></p>
<p><em>Figure 4-19. We have deleted all cells with a value greater than 2</em></p>
TableView-112015-05-17T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/17/tableview-11<h2>Moving Cells and Sections in Table Views</h2>
<hr />
<h3>Problem</h3>
<hr />
<p>You want to move and shuffle cells and sections inside a table view, with smooth and intuitive animations.</p>
<h3>Solution</h3>
<hr />
<p>Use the moveSection:toSection: method of the table view to move a section to a new position. You can also use the moveRowAtIndexPath:toIndexPath: method to move a table view cell from its current place to a new place.</p>
<h3>Discussion</h3>
<hr />
<p>Moving table view cells and sections differs from exchanging them. Let’s have a look at an example that will make this easier to understand. Let’s say you have three sections in your table view: Sections A, B, and C. If you move Section A to Section C, the table view will notice this move and will then shift Section B to the previous position of Section A, and will move Section B to the previous position of Section B. However, if Section B is moved to Section C, the table view will not have to move Section A at all, as it is sitting on top and doesn’t interfere with the repositioning of Section B and C. In this case, Section B will be moved to Section C and Section C to Section B. The same logic will be used by the table view when moving cells.</p>
<p>To demonstrate this, let’s create a table view and preload it with three sections, each of which contains three cells of its own. Let’s start with the header file of our view controller:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import <UIKit/UIKit.h></p>
<p>@interface TableViewController : UIViewController <UITableViewDataSource, UITableViewDelegate></p>
<p>@property (nonatomic, strong) UITableView <em>myTableView;
/</em> Each section is an array on its own, containing objects of type NSString <em>/
@property (nonatomic, strong) NSMutableArray </em>arrayOfSections;
@end</code></pre></figure></p>
<p>Our view controller will become the data source of the table view. The table view has sections and each section has rows. We will keep an array of arrays; the first array is our array of sections, which will itself contain other arrays that contain our cells. The arrayOfSections defined in the header file of our view controller will bear that respon- sibility. Let’s go ahead and populate this array in the implementation of our view con- troller:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (NSMutableArray <em>) newSectionWithIndex:(NSUInteger)paramIndex
withCellCount:(NSUInteger)paramCellCount {
NSMutableArray </em>result = [[NSMutableArray alloc] init];
NSUInteger counter = 0;</p>
<pre><code>for (counter = 0;
counter &lt; paramCellCount;
counter++) {
[result addObject:[[NSString alloc] initWithFormat:@"Section %lu Cell %lu", (unsigned long)paramIndex, (unsigned long)counter+1]];
}
return result;
</code></pre>
<p>}</code></pre></figure></p>
<p>We shall then instantiate our table view and implement the necessary methods in the UITableViewDataSource protocol to populate our table view with data:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
NSInteger result = 0;</p>
<pre><code>if ([tableView isEqual:self.myTableView]) {
result = (NSInteger)[self.arrayOfSections count];
}
return result;
</code></pre>
<p>}</p>
<ul>
<li><p>(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger result = 0;</p>
<p> if ([tableView isEqual:self.myTableView]) {
if ([self.arrayOfSections count] > section) {
NSMutableArray *sectionArray = [self.arrayOfSections objectAtIndex:section];
result = (NSInteger)[sectionArray count];
}
}</p>
<p> return result;
}</p></li>
<li><p>(UITableViewCell <em>)tableView:(UITableView </em>)tableView cellForRowAtIndexPath:(NSIndexPath <em>)indexPath {
UITableViewCell </em>result = nil;</p>
<p> if ([tableView isEqual:self.myTableView]) {
static NSString *CellIdentifier = @"CellIdentifier";
result = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (result == nil) {
result = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}</p>
<pre><code> NSMutableArray *sectionArray = [self.arrayOfSections
objectAtIndex:indexPath.section];
result.textLabel.text = [sectionArray objectAtIndex:indexPath.row];
</code></pre>
<p> }</p>
<p> return result;
}</p></li>
<li><p>(void)viewDidLoad {
[super viewDidLoad];</p>
<p> <em>arrayOfSections = [[NSMutableArray alloc] init];
NSMutableArray <em>section1 = [self newSectionWithIndex:1 withCellCount:3];
NSMutableArray </em>section2 = [self newSectionWithIndex:2 withCellCount:3];
NSMutableArray *section3 = [self newSectionWithIndex:3 withCellCount:3];
[</em>arrayOfSections addObject:section1];
[<em>arrayOfSections addObject:section2];
[</em>arrayOfSections addObject:section3];</p>
<p> self.myTableView =
[[UITableView alloc] initWithFrame:self.view.bounds
style:UITableViewStyleGrouped];
self.myTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
[self.view addSubview:self.myTableView];
}</code></pre></figure></p></li>
</ul>
<p>Show time! Shall we first have a look at how sections can be moved to a new position? Let’s write a method that will move Section 1 to 3:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void) moveSection1ToSection3 {
NSMutableArray *section1 = [self.arrayOfSections objectAtIndex:0];
[self.arrayOfSections removeObject:section1];
[self.arrayOfSections addObject:section1];
[self.myTableView moveSection:0 toSection:2];
}</code></pre></figure></p>
<p>I will leave it up to you to decide when you would like to invoke this method, as we don’t have a button on our UI at the moment. You can simply create a navigation controller, place a navigation button on it, and then invoke this method.
Once you run the app normally, you will see the sections lined up from 1 to 3, as in <em>Figure 4-13</em>.</p>
<p><img src="/assets/img/ios/TableView-13.png" alt="TableView-13" /></p>
<p><em>Figure 4-13. A table view with three sections, each containing three cells</em></p>
<p>After you invoke the moveSection1ToSection3 method, you will see that Section 1 gets moved to Section 3, Section 3 moves to Section 2’s previous position, and finally Section 2 moves to Section 1’s previous position (<em>Figure 4-14</em>).</p>
<p><img src="/assets/img/ios/TableView-14.png" alt="TableView-14" /></p>
<p><em>Figure 4-14. Section 1 is moved to Section 3, and other sections are subsequently moved as well</em></p>
<p>Moving cells is very similar to moving sections. To move cells, all we have to do is to use the <strong>moveRowAtIndexPath:toIndexPath:</strong> method. Remember that you can move a cell from one section to the same section, or to a new section. Let’s make it easy and move Cell 1 in Section 1 to Cell 2 in the same section and see what happens:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void) moveCell1InSection1ToCell2InSection1{
NSMutableArray <em>section1 = [self.arrayOfSections objectAtIndex:0];
NSString </em>cell1InSection1 = [section1 objectAtIndex:0];
[section1 removeObject:cell1InSection1];
[section1 insertObject:cell1InSection1 atIndex:1];
NSIndexPath <em>sourceIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
NSIndexPath </em>destinationIndexPath = [NSIndexPath indexPathForRow:1 inSection:0];
[self.myTableView moveRowAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath];
}</code></pre></figure></p>
<p>So what is going on in this code? Well, we need to make sure our data source holds the correct data that needs to be displayed in our table view after we have moved the cells around, so we remove Cell 1 in Section 1 first. That moves Cell 2 to Cell 1, and Cell 3 to Cell 2, with a total of 2 cells in the array. Then we will insert Cell 1 into Index 1 (second object) of the array. That will make our array contain Cell 2, Cell 1, and then Cell 3. After that is done, we have actually moved the cells in our table view.</p>
<p>Let’s make this a bit more difficult. How about moving Cell 2 in Section 1 to Cell 1 in Section 2?</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void) moveCell2InSection1ToCell1InSection2{
NSMutableArray <em>section1 = [self.arrayOfSections objectAtIndex:0];
NSMutableArray </em>section2 = [self.arrayOfSections objectAtIndex:1];
NSString <em>cell2InSection1 = [section1 objectAtIndex:1];
[section1 removeObject:cell2InSection1];
[section2 insertObject:cell2InSection1 atIndex:0];
NSIndexPath </em>sourceIndexPath = [NSIndexPath indexPathForRow:1 inSection:0];
NSIndexPath *destinationIndexPath = [NSIndexPath indexPathForRow:0 inSection:1];
[self.myTableView moveRowAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath];
}</code></pre></figure></p>
<p>The results of this transition are shown in <em>Figure 4-15</em>.</p>
<p><img src="/assets/img/ios/TableView-15.png" alt="TableView-15" /></p>
<p><em>Figure 4-15. Cell 2 in Section 1 is moved to Cell 1 in Section 2</em></p>
TableView-102015-05-16T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/16/tableview-10<h2>Displaying Context Menus on Table View Cells</h2>
<hr />
<h3>Problem</h3>
<hr />
<p>You want to give your users the ability to use copy/paste options among other operations that they can choose, by holding down one of their fingers on a table view cell in your app.</p>
<h3>Solution</h3>
<hr />
<p>Implement the following three methods of the UITableViewDelegate protocol in the delegate object of your table view:</p>
<ul>
<li>tableView:shouldShowMenuForRowAtIndexPath:</li>
</ul>
<p>The return value of this method is of type BOOL. If you return YES from this method, iOS will display the context menu for the table view cell whose index gets passed to you through the shouldShowMenuForRowAtIndexPath parameter.</p>
<ul>
<li>tableView:canPerformAction:forRowAtIndexPath:withSender:</li>
</ul>
<p>The return value of this method is also of type BOOL. Once you allow iOS to display a context menu for a table view cell, iOS will call this method multiple times and pass you the selector of the action that you can choose to display in the context menu or not. So, if iOS wants to ask you whether you would like to show the Copy menu to be displayed to the user, this method will get called in your table view’s delegate object and the canPerformAction parameter of this method will be equal to @selector(copy:). We will read more information about this in this recipe’s Discussion.</p>
<ul>
<li>tableView:performAction:forRowAtIndexPath:withSender:</li>
</ul>
<p>Once you allow a certain action to be displayed in the context menu of a table view cell, when the user picks that action from the menu, this method will get called in your table view’s delegate object. In here, you must do whatever needs to be done to satisfy the user’s request. For instance, if it is the Copy menu that the user has selected, you will need to use a pasteboard to place the chosen table view cell’s content into the pasteboard.</p>
<h3>Discussion</h3>
<hr />
<p>A table view can give a yes/no answer to iOS, allowing or disallowing the display of available system menu items for a table view cell. iOS attempts to display a context menu on a table view cell when the user has held down his finger on the cell for a certain period of time, roughly about one second. iOS then asks the table view whose cell was the source of the trigger for the menu. If the table view gives a yes answer, iOS will then tell the table view what options can be displayed in the context menu, and the table view will be able to say yes or no to any of those items. If there are five menu items available, for instance, and the table view says yes to only two of them, then only those two items will be displayed.</p>
<p>After the menu items are displayed to the user, the user can either tap on one of the items or tap outside the context menu to cancel it. Once the user taps on one of the menu items, iOS will send a delegate message to the table view informing it of the menu item that the user has picked. Based on this information, the table view can make a decision as to what to do with the selected action.</p>
<p>I suggest that we first see what actions are actually available for a context menu on a table view cell, so let’s create our table view and then display a few cells inside it:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 3;
}</p>
<ul>
<li><p>(UITableViewCell <em>) tableView:(UITableView </em>)tableView cellForRowAtIndexPath:(NSIndexPath <em>)indexPath {
UITableViewCell </em>result = nil;
static NSString *CellIdentifier = @"CellIdentifier";
result = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];</p>
<p> if (result == nil) {
result = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}</p>
<p> result.textLabel.text = [[NSString alloc]
initWithFormat:@"Section %ld Cell %ld",
(long)indexPath.section,
(long)indexPath.row];</p>
<p> return result;
}</p></li>
<li><p>(void)viewDidLoad {
[super viewDidLoad];</p>
<p> self.view.backgroundColor = [UIColor whiteColor];
self.myTableView = [[UITableView alloc]
initWithFrame:self.view.bounds
style:UITableViewStylePlain];
self.myTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
self.myTableView.dataSource = self;
self.myTableView.delegate = self;
[self.view addSubview:self.myTableView];
}</code></pre></figure></p></li>
</ul>
<p>Now we will implement the three aforementioned methods defined in the UITableView Delegate protocol and simply convert the available actions (of type SEL) to strings and print them out to the console:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (BOOL) tableView:(UITableView <em>)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath </em>)indexPath {
/<em> Allow the context menu to be displayed on every cell </em>/
return YES;
}</p>
<ul>
<li><p>(BOOL) tableView:(UITableView <em>) tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath </em>)indexPath withSender:(id)sender {
NSLog(@"%@", NSStringFromSelector(action));</p>
<p> return NO;
}</p></li>
<li><p>(void) tableView:(UITableView <em>)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath </em>)indexPath withSender:(id)sender {
/<em> Empty for now </em>/
}</code></pre></figure></p></li>
</ul>
<p>Now run your app in the simulator or on the device. You will then see three cells loaded into the table view. Hold down your finger (if on a device) or your pointer (if using iOS Simulator) on one of the cells and observe what gets printed out to the console window:</p>
<ul>
<li>cut:</li>
<li>copy:</li>
<li>select:</li>
<li>selectAll:</li>
<li>paste:</li>
<li>delete:</li>
<li>_promptForReplace:</li>
<li>_showTextStyleOptions:</li>
<li>_define:</li>
<li>_accessibilitySpeak:</li>
<li>_accessibilityPauseSpeaking:</li>
<li>makeTextWritingDirectionRightToLeft:</li>
<li>makeTextWritingDirectionLeftToRight:</li>
</ul>
<p>These are all the actions that iOS will allow you to show your users, should you need them. So for instance, if you would like to allow your users to have the Copy option, in the tableView:canPerformAction:forRowAtIndexPath:withSender: method, simply find out which action iOS is asking your permission for before displaying it, and either return YES or NO:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (BOOL) tableView:(UITableView <em>) tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath </em>)indexPath withSender:(id)sender {
if (action == @selector(copy:)) {
return YES;
}</p>
<pre><code>return NO;
</code></pre>
<p>}</code></pre></figure></p>
<p>The next step is to intercept what menu item the user actually selected from the context menu. Based on this information, we can then take appropriate action. For instance, if the user selected the Copy item in the context menu (see <em>Figure 4-12</em>), then we can use UIPasteBoard to copy that cell into the pasteboard for later use:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void) tableView:(UITableView <em>)tableView performAction:(SEL)action
forRowAtIndexPath:(NSIndexPath </em>)indexPath withSender:(id)sender{
if (action == @selector(copy:)) {
UITableViewCell <em>cell = [tableView cellForRowAtIndexPath:indexPath];
UIPasteboard </em>pasteBoard = [UIPasteboard generalPasteboard];
[pasteBoard setString:cell.textLabel.text];
}
}</code></pre></figure></p>
<p><img src="/assets/img/ios/TableView-12.png" alt="TableView-12" /></p>
<p><em>Figure 4-12. The Copy action displayed inside a context menu on a table view cell</em></p>
构造使用 TableView-92015-05-15T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/15/tableview-9<h2>在TableView中构建页眉和页脚</h2>
<hr />
<h3>问题</h3>
<hr />
<p>要在一个TableView中创建一个页眉或页脚。</p>
<h3>方案</h3>
<hr />
<p>创建一个视图(可以是一个标签,图片等,任何可以直接或者间接的视图子类),然后把这个视图分配到TableView的1个Section的页眉或页脚中。也可以指定某个页眉或页脚的高度。</p>
<h3>讨论</h3>
<hr />
<p>TableView可以有多个页眉页脚。一个TableView的每个Section都可有它自己的页眉和页脚,如果在一个TableView中有3个Section,你就最多有3个页眉和3个页脚。你不必给这些Section的每个部分提供页眉页脚。告诉TableView是否在一个Section要页眉或页脚,通过其委托你是否把这些视图传递到TableView中,你想在TableView中为Section(多个)提供页眉(多的)、页脚(多个),这些都取决于你。TableView中的页眉页脚成了TableView的一部分,这就意味着当TableView的内容滚动时,TableView中的页眉页脚也同样会滚动。</p>
<p><img src="/assets/img/ios/TableView-7.png" alt="TableView-7" /></p>
<p>图4-7. TableView中页眉和页脚</p>
<blockquote><p>通过UITableViewDataSource定义的方法可以确定一个Section内页眉和页脚的高度。</p>
<p>通过UITableViewDelegate协议中定义的方法可以确定为一个表视图中一个Section的页眉、页脚展示的实际视图。</p></blockquote>
<p>创建一个带有一个TableView的简单APP。随后提供2个标签,他们属于UILabel类,一个做页眉,另一个做页脚。仅用3个cell填充这个TableView。在页眉中放入文字"Section 1 Header"。在页脚标签上放入文字"Section 1 Footer",以根视图控制器的头文件开始,我们定义一个TableView。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import <UIKit/UIKit.h></p>
<p>@interface TableViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) UITableView *myTableView;
@end</code></pre></figure></p>
<p>创建一个分组的表视图,并给它加载三个单元格。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (UITableViewCell <em>) tableView:(UITableView </em>)tableView cellForRowAtIndexPath:(NSIndexPath <em>)indexPath {
UITableViewCell </em>result = nil;
static NSString *CellIdentifier = @"CellIdentifier";
result = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];</p>
<pre><code>if (result == nil) {
result = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
result.textLabel.text = [[NSString alloc] initWithFormat:@"Cell %ld",
(long)indexPath.row];
return result;
</code></pre>
<p>}</p>
<ul>
<li><p>(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}</p></li>
<li><p>(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 3;
}</p></li>
<li><p>(void)viewDidLoad {
[super viewDidLoad];
self.myTableView =
[[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
self.myTableView.dataSource = self;
self.myTableView.delegate = self;
self.myTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.myTableView];
}</p></li>
<li><p>(BOOL)shouldAutorotateToInterfaceOrientation :(UIInterfaceOrientation)interfaceOrientation {
return YES;
}</code></pre></figure></p></li>
</ul>
<p>这个部分令人振奋。现在可以使用UITableViewDelegate定义的2个重要方法来提供一个页眉标签和另一个页脚标签。这两个方法分别是:</p>
<ul>
<li>tableView:viewForHeaderInSection:</li>
</ul>
<blockquote><p>此方法返回一个UIView类型的值。此方法返回的视图将显示为viewForHeaderInSection参数指定部分的页眉。</p></blockquote>
<ul>
<li>tableView:viewForFooterInSection:</li>
</ul>
<blockquote><p>此方法返回一个UIView类型的值。此方法返回的视图将显示为viewForFooterInSection参数指定部分的页脚。</p></blockquote>
<p>实现方法,并从其中返回一个UILabel的实例。按照计划,在页眉标签放入"Section 1 Header",在页脚标签放入文字"Section 1 Footer"。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (UIView <em>) tableView:(UITableView </em>)tableView viewForHeaderInSection:(NSInteger)section {
UILabel *result = nil;</p>
<pre><code>if ([tableView isEqual:self.myTableView]) {
result = [[UILabel alloc] initWithFrame:CGRectZero];
result.text = @"Section 1 Header";
result.backgroundColor = [UIColor clearColor];
[result sizeToFit];
}
return result;
</code></pre>
<p>}</p>
<ul>
<li><p>(UIView <em>) tableView:(UITableView </em>)tableView viewForFooterInSection:(NSInteger)section {
UILabel *result = nil;</p>
<p> if ([tableView isEqual:self.myTableView]) {
result = [[UILabel alloc] initWithFrame:CGRectZero];
result.text = @"Section 1 Footer";
result.backgroundColor = [UIColor clearColor];
[result sizeToFit];
}</p>
<p> return result;
}</code></pre></figure></p></li>
</ul>
<p>如果你现在在iPhone模拟器上运行你的APP,你肯定会看到一些奇怪的事件。如图4-8所示:</p>
<blockquote><p>IOS 7 viewForHeaderInSection 的section从1开始而不是从0开始</p></blockquote>
<p><img src="/assets/img/ios/TableView-8.png" alt="TableView-8" /></p>
<p>图4-8. TableView中的页眉和页脚没有正确对齐</p>
<p>The reason for this misalignment of the labels is because the table view doesn’t really know the height of these views. To specify the height of the header and footer views, we need to use the following two methods which are defined in the UITableViewDele gate protocol:</p>
<ul>
<li>tableView:heightForHeaderInSection:</li>
</ul>
<p>The return value of this method is of type CGFloat, and it specifies the height of the header for a section in a table view. The section’s index is passed through the heightForHeaderInSection parameter.</p>
<ul>
<li>tableView:heightForFooterInSection:</li>
</ul>
<p>The return value of this method is of type CGFloat, and it specifies the height of the footer for a section in a table view. The section’s index is passed through the heightForHeaderInSection parameter.</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
CGFloat result = 0.0f;</p>
<pre><code>if ([tableView isEqual:self.myTableView] &amp;&amp; section == 0) {
result = 30.0f;
}
return result;
</code></pre>
<p>}</p>
<ul>
<li><p>(CGFloat) tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
CGFloat result = 0.0f;</p>
<p> if ([tableView isEqual:self.myTableView] && section == 0) {
result = 30.0f;
}</p>
<p> return result;
}</code></pre></figure></p></li>
</ul>
<p><img src="/assets/img/ios/TableView-9.png" alt="TableView-9" /></p>
<ul>
<li>tableView:titleForHeaderInSection:</li>
</ul>
<p>The return value of this method is of type NSString. This string will automatically be placed inside a label by the table view and will be displayed as the header of the section, which is specified in the titleForHeaderInSection parameter.</p>
<ul>
<li>tableView:titleForFooterInSection:</li>
</ul>
<p>The return value of this method is of type NSString. This string will automatically be placed inside a label by the table view and will be displayed as the footer of the section, which is specified in the titleForFooterInSection parameter.</p>
构造使用 TableView-82015-05-15T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/15/tableview-8<h2>启用TableViewCell的滑动删除</h2>
<hr />
<h3>问题</h3>
<hr />
<p>想让APP用户能从TableView中轻松删除行。</p>
<h3>方案</h3>
<hr />
<p>在TableView的delegate中实现tableView:editingStyleForRowAtIndexPath:方法。</p>
<p>在dataSource中实现tableView:commitEditingStyle:forRowAtIndexPath:方法。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">/<em> TableViewController.h </em>/</p>
<h1>import <UIKit/UIKit.h></h1>
<p>@interface TableViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) UITableView <em>myTableView;
@property (nonatomic, strong) NSMutableArray </em>arrayOfRows;
@end</p>
<p>/<em> TableViewController.m </em>/
- (void)viewDidLoad {
[super viewDidLoad];
self.myTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
self.myTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.myTableView.dataSource = self;
self.myTableView.delegate = self;
[self.view addSubview:self.myTableView];</p>
<pre><code>self.arrayOfRows = [@[@"abc", @"def", @"hig"] mutableCopy];
</code></pre>
<p>}</p>
<ul>
<li><p>(UITableViewCellEditingStyle)tableView:(UITableView <em>)tableView editingStyleForRowAtIndexPath:(NSIndexPath </em>)indexPath {
UITableViewCellEditingStyle result = UITableViewCellEditingStyleNone;</p>
<p> if ([tableView isEqual:self.myTableView]) {
result = UITableViewCellEditingStyleDelete;
}</p>
<p> return result;
}</p></li>
<li><p>(void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
[self.myTableView setEditing:editing animated:animated];
}</p></li>
<li><p>(void)tableView:(UITableView <em>)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath </em>)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
if (indexPath.row < [self.arrayOfRows count]) {
[self.arrayOfRows removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}
}
}</code></pre></figure></p></li>
</ul>
<p>tableView:editingStyleForRowAtIndexPath:方法能够启动删除功能,它被tableView调用,同时它的返回值决定了tableView允许用户做什么(插入,删除等)。</p>
<p>tableView:commitEditingStyle:forRowAtIndexPath:方法实现用户要求的删除。后一种方法在委托中定义,但是它的功能有点重载:不只使用这方法删除数据,也必须要从表中删除行。</p>
<h3>讨论</h3>
<hr />
<p>TableView通过在目标行右侧显示一个按钮来响应滑动(图4-6)。正如你所看到的,TableView不在编辑模式下,但按钮允许用户删除行。</p>
<p><img src="/assets/img/ios/TableView-6.png" alt="TableView-6" /></p>
<p>图4-6. 删除键出现在TableView单元格中</p>
<p>通过执行tableView:editingStyleForRowAtIndexPath:(在UITableViewDelegate声明)方法能够启动这个模式,它的返回值说明了表中是否应该允许同时插入与删除或者同时不。通过实现TableView的数据源中的tableView:commitEditingStyle:forRowAtIndexPath:方法,你将会得到通知(如果用户完成了插入或者删除操作的话)。</p>
<p>deleteRowsAtIndexPaths:withRowAnimation:方法的第二个参数允许你指定一个动画方法,当行从TableView中删除时这个动画方法会被执行。我们的示例说明了当行被删除时它在从右到左的移动过程中消失。</p>
构造使用 TableView-72015-05-15T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/15/tableview-7<h2>在TableView中展示分层数据</h2>
<hr />
<h3>问题</h3>
<hr />
<p>在一个TableView中显示分层数据。</p>
<h3>方案</h3>
<hr />
<p>使用TableViewCell的缩进功能:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (UITableViewCell <em>)tableView:(UITableView </em>)tableView cellForRowAtIndexPath:(NSIndexPath <em>)indexPath {
UITableViewCell</em> result = nil;
static NSString *MyCellIdentifier = @"SimpleCells";
result = [tableView dequeueReusableCellWithIdentifier:MyCellIdentifier];</p>
<pre><code>if (result == nil) {
result = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyCellIdentifier];
}
result.textLabel.text = [NSString stringWithFormat:@"Section %ld, Cell %ld", (long )indexPath.section, (long)indexPath.row];
result.indentationLevel = indexPath.row;
result.indentationWidth = 10.0f;
return result;
</code></pre>
<p>}</code></pre></figure></p>
<p>为了给每个cell的内容视图一个边距,用缩进等级与缩进宽度简单的相乘。图4-5描述这些cell在TableView展示时的外观。</p>
<p><img src="/assets/img/ios/TableView-5.png" alt="TableView-5" /></p>
<p>图4-5. 带缩进的TableView单元格</p>
<h3>讨论</h3>
<hr />
<p>虽然可能很少发现很有用,但是你可以在iOS SDK的TableViewCell应用缩进。每个cell有2个想着属性。缩进等级和缩进宽度。缩进等级与缩进宽度简单相乘,所得的结果就是偏移量,根据这个值TableViewCell的内容会向左右两侧偏移。</p>
<p>例如,如果一个cell的缩进等级设置为2,缩进宽度设置为3,那么相乘的结果为6。这就意味着当呈现在TableView中时cell内容视图向右移动6个像素。</p>
<blockquote><p>缩进级别定义为有符号的整数值,使你可以将给它的值为负。这显示将使单元格的内容视图向左偏移。</p>
<p>TableView的缩进级别允许程序员呈现分层数据,并由程序员来确定每个cell缩进级和缩进宽度。</p></blockquote>
构造使用 TableView-62015-05-15T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/15/tableview-6<h2>创建自定义TableView单元格附件</h2>
<hr />
<h3>问题</h3>
<hr />
<p>iOS SDK提供的附件不满意,想要创建自己的附件。</p>
<h3>方案</h3>
<hr />
<p>把类型UIView的一个实例分配到任何UITableViewCell类的实例的accessoryView属性。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (UITableViewCell <em>)tableView:(UITableView </em>)tableView cellForRowAtIndexPath:(NSIndexPath <em>)indexPath {
UITableViewCell</em> result = nil;</p>
<pre><code>if ([tableView isEqual:self.myTableView]) {
static NSString *MyCellIdentifier = @"SimpleCell";
result = [tableView dequeueReusableCellWithIdentifier:MyCellIdentifier];
if (result == nil) {
result = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyCellIdentifier];
}
result.textLabel.text = [NSString stringWithFormat:@"Section %ld, Cell %ld",
(long)indexPath.section,
(long)indexPath.row];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(0.0f, 0.0f, 150.0f, 25.0f);
[button setTitle:@"Expand" forState:UIControlStateNormal];
[button addTarget:self action:@selector(performExpand:) forControlEvents:UIControlEventTouchUpInside];
result.accessoryView = button;
}
return result;
</code></pre>
<p>}</code></pre></figure></p>
<p>这段代码使用performExpand:方法叙谈每个按键的选择器。下面是这个方法的定义:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)performExpand:(id)paramSender {
NSLog(@"id = %@", paramSender);
}</code></pre></figure></p>
<p><img src="/assets/img/ios/TableView-4.png" alt="TableView-4" /></p>
<p>图4-4. 带有自定义附件视图的TableView单元格</p>
<h3>讨论</h3>
<hr />
<p>UITableViewCell类型对象保留一个名为accessoryView属性。</p>
<p>如果对内置iOS SDK TableViewCell的附件不完全满意的话,可以把别的视图赋予这个属性。属性设置之后,Cocoa Touch将忽略accessoryType属性的值,同时把分配到accessoryView属性的值作为分配的附件用到单元格中。</p>
<p>当点击任何单元的按钮,performExpand:方法就会被调用。如何决定发送按钮属于哪一个单元格?现在,我们要以某种方式把我们的与其所属的cell连接起来。</p>
<p>处理这个问题的一个办法是检索触发事件的按钮的superView。TableViewCell的辅助视图作为他们的子视图添加到cell的配件视图,因为检索按钮的superView将返回TableViewCell,返回的这个拥有配套的视图按钮的cell:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)performExpand:(UIButton <em>)paramSender {
UITableViewCell </em>ownerCell = (UITableViewCell *)paramSender.superview;</p>
<pre><code>if (ownerCell != nil) {
NSIndexPath *ownerCellIndexPath = [self.myTableView indexPathForCell:ownerCell];
NSLog(@"Accessory in index path is tapped. Index path = %@", ownerCellIndexPath);
if (ownerCellIndexPath.section == 0 &amp;&amp; ownerCellIndexPath.row == 1) {
/* This is the second row in the first section */
}
}
</code></pre>
<p>}</code></pre></figure></p>
构造使用 TableView-52015-05-15T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/15/tableview-5<h2>在TableView中使用不同各类的附件</h2>
<hr />
<h3>问题</h3>
<hr />
<p>通过展示不同附件(accessories),抓住用户对一个TableView的关注,或者提供不同的方式使用户在你的TableView中和每个cells进行互动。</p>
<h3>方案</h3>
<hr />
<p>使用UITableViewCell类中的accessoryType,在tableView的数据源对象中进行想着的实例化。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (UITableViewCell <em>)tableView:(UITableView </em>)tableView cellForRowAtIndexPath:(NSIndexPath <em>)indexPath {
UITableViewCell</em> result = nil;</p>
<pre><code>if ([tableView isEqual:self.myTableView]) {
static NSString *MyCellIdentifier = @"SimpleCell";
result = [tableView dequeueReusableCellWithIdentifier:MyCellIdentifier];
if (result == nil) {
result = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyCellIdentifier];
}
result.textLabel.text = [NSString stringWithFormat:@"Section %ld, Cell %ld",
(long)indexPath.section,
(long)indexPath.row];
result.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
return result;
</code></pre>
<p>}</code></pre></figure></p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">typedef NS_ENUM(NSInteger, UITableViewCellAccessoryType) {
UITableViewCellAccessoryNone, // don't show any accessory view
UITableViewCellAccessoryDisclosureIndicator, // regular chevron. doesn't track
UITableViewCellAccessoryDetailDisclosureButton, // info button w/ chevron. tracks
UITableViewCellAccessoryCheckmark, // checkmark. doesn't track
UITableViewCellAccessoryDetailButton NS_ENUM_AVAILABLE_IOS(7_0) // info button. tracks
};</code></pre></figure></p>
<h3>讨论</h3>
<hr />
<p>可以把UITableViewCellAccessoryType枚举中定义的任何值分配到一个UITableViewCell类的实例中accessoryType的属性。</p>
<p>discloure indicator 和 detail discloure按钮是两个非常有用的附件。它们都通过V形乘着向用户指示,如果他们在关联的TableView单元格进行点击,将显示新的视图或视图控制器。</p>
<p>换句话说,用户将看到一个有更多关于当前所选项信息的屏幕。这两个附件的区别在于discloure indicator不产生事件,而detail discloure按钮在被点击时会向委托触发一个事件。</p>
<p>也就是说,点击cell上的按键会有不同的效果。因此detail discloure按钮允许用户在同一行上执行两个独立但想着的操作。</p>
<p>图4-3显示在一个TableView中展示了这两个不财附件,第一个是discloure indicator,第二个是detail discloure按钮。</p>
<p><img src="/assets/img/ios/TableView-3.png" alt="TableView-3" /></p>
<p>图4-3. 2个不同附件的TableViewCells.</p>
<p>如果你点击任何一个分配到TableViewCells中的detail discloure按钮,你会马上意识到它事实上是一个按钮。现在问题来了:当用户点击这个按钮的时候TableView如何知道呢?</p>
<p>TableView在其委托对象上产生一个事件,一个TableViewCell的detail discloure按钮也会产生一个被TableView的委托对象捕获的事件:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void) tableView:(UITableView <em>)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath </em>)indexPath{
NSLog(@"Accessory button is tapped for cell at index path = %@", indexPath);
UITableViewCell *ownerCell = [tableView cellForRowAtIndexPath:indexPath];
NSLog(@"Cell Title = %@", ownerCell.textLabel.text);
}</code></pre></figure></p>
<p>此代码查找哪个TableViewCell的detail discloure按钮触发,并将该cell的文本标签的内容打印到控制台屏幕。</p>
<blockquote><p>提醒:可以通过选择Xcode中的Run --> Console来显示控制台屏幕。</p></blockquote>
构造使用 TableView-42015-05-14T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/14/tableview-4<h2>接收和处理TableView事件</h2>
<hr />
<h3>问题</h3>
<hr />
<p>响应TableView产生的各种事件。</p>
<h3>方案</h3>
<p>给TableView提供一个委托对象。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import <UIKit/UIKit.h></p>
<p>@interface TableViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) UITableView *myTableView;
@end</code></pre></figure></p>
<p>同一个视图控制器的.m文件中实现UITableViewDelegate协议中定义的一个方法。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)viewDidLoad {
[super viewDidLoad];</p>
<pre><code>self.myTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
self.myTableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.myTableView.dataSource = self;
self.myTableView.delegate = self;
[self.view addSubview:self.myTableView];
</code></pre>
<p>}</p>
<ul>
<li>(void) tableView:(UITableView <em>)tableView didSelectRowAtIndexPath:(NSIndexPath </em>)indexPath {
if ([tableView isEqual:self.myTableView]) {
NSLog(@"%@", [NSString stringWithFormat:@"Cell %ld in Section %ld is selected", (long)indexPath.row, (long)indexPath.section]);
}
}</code></pre></figure></li>
</ul>
<h3>讨论</h3>
<hr />
<p>当一个数据源负责给TableView提供数据时,无论何时事件发生TableView都将咨询委托,或者在完成一个任务之前如果TableView请求更多信息,它要调用一个委托的方法:</p>
<ul>
<li>当一个cell被选中或者取消选中之前</li>
<li>当一个TableView需要找出每个cell的高度时</li>
<li>当一个TableView需要构造每个section静养和页脚时</li>
</ul>
<p>当对象被设置为TableView的delegate属性。Delegate实现了tableView:didSelectRowAtIndexPath:,这样当用户选择了tableView中的某个cell或者row时,就会等到通知。SDK中的UITableViewDelegate相关文档介绍了所有可以定义的方法。</p>
构造使用 TableView-32015-05-13T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/13/tableview-3<h2>向 TableView 填充数据</h2>
<hr />
<h3>问题</h3>
<hr />
<p>想要在 TableView 中填充数据。</p>
<h3>方案</h3>
<hr />
<p>创建一个遵循 UITableViewDataSource 协议的对象并把它分配到一个 TableView 实例中,然后通过响应数据源消息给你的 TableView 提供信息。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import <UIKit/UIKit.h></p>
<p>@Interface TableViewController : UIViewController <UITableViewDataSource>
@property (nonatomic, strong) UITableView *myTableView;
@end</code></pre></figure></p>
<p>在视图控制器中用 viewDidLoad 方法可以创建 TableView ,然后将 viewController 设置为 tableView 的数据源。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.myTableView = [[UITableView alloc] initWithFrame: self.view.bounds
style: UITableViewStylePlain];
self.myTableView.dataSource = self;</p>
<pre><code>/* Make sure tableView resize correctly */
self.myTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview: self.myTableView];
</code></pre>
<p>}</code></pre></figure></p>
<p>现在需要确定我们的表视图响应 UITableViewDataSource 协议的 @required 方法,按住键盘上的 command 键,在视图控制器的头文件 UITableViewDataSource 上单击,这将会展示这个协议 @required 修饰的方法。</p>
<p>UITableView 类定义了一个名为 dataSource 的属性,这个非固定类型的对象必须遵循 UITableViewDataSource 协议,每次刷新表格视图,并使用 reloadData 方法重新加载时,这个 TableView 会从其数据源调用各种方法以找出对其填充的数据,一个 TableView 的数据源能够实现三个重要方法,其中2个对每个数据源都要强制执行。</p>
<ul>
<li><p>numberOfSectionsInTableView:
此方法允许数据源告知加载到 TableView 中的表的 Section 数。</p></li>
<li><p>tableView:numberOfRowsInSection:
此方法告诉视图控制器有多少单元格或者行要加载到每个 Section, Section 个数传递给数据源中的 numberOfRowsInSection 作参数,这个方法在数据源对象中要强制执行。</p></li>
<li><p>tableView:cellForRowAtIndexPath:
此方法负责返回作为 TableView 行的 UITableViewCell 类静态实例。这个方法在数据源对象的执行中也是强制性的。</p></li>
</ul>
<p>首先,告诉 UITableView 显示3个Section.</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSInteger result = 0;</p>
<pre><code> if ([tableView isEqual:self.myTableView]) {
result = 3;
}
return result;
</code></pre>
<p>}</code></pre></figure></p>
<p>然后,告诉 UITableView 需要在每个 Section 显示多少行。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger result = 0;</p>
<pre><code>if ([tableView isEqual:self.myTableView]) {
switch (section) {
case 0: {
result = 3;
break;
}
case 1: {
result = 5;
break;
}
case 2: {
result = 8;
break;
}
}
}
return result;
</code></pre>
<p>}</code></pre></figure></p>
<p>最后,呈现 Cell。</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (UITableViewCell <em>)tableView:(UITableView </em>)tableView cellForRowAtIndexPath:(NSIndexPath <em>)indexPath {
UITableViewCell </em>result = nil;</p>
<pre><code>if ([tableView isEqual:self.myTableView]) {
static NSString *TableViewCellIdentifier = @"MyCells";
result = [tableView dequeueReusableCellWithIdentifier:TableViewCellIdentifier];
if (result == nil) {
result = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TableViewCellIdentifier];
}
result.textLabel.text = [NSString stringWithFormat:@"Section %ld, Cell %ld", (long)indexPath.section, (long)indexPath.row];
}
return result;
</code></pre>
<p>}</code></pre></figure></p>
<p><img src="/assets/img/ios/TableView-2.png" alt="TableView-2" /></p>
<p>图4-2. 一个包含3个Section空白的TableView</p>
<p>当重新加载或刷新TableView,要通过UITableViewDataSource协议查询数据源,请求许多的信息。</p>
<p>TableView首先请求Section的个数,每个Section来负责持有行或cells。数据源确认了Section数目之后,TableView会为每个Section请求行数,数据源获取每个Section的从零开始的索引,并基于这一点,可以决定多少cells已加载到每个Section。</p>
<p>确定了每个Section的cells数目之后,TableView继续请求数据源以展示每个Section的每个cells,可以分配UITableViewCell类的实例并把其返回TableView。当然,每个cells可以设置的属性有很多,属性中包括标题,子标题,cells的颜色以及更多。</p>
构造使用 TableView-22015-05-12T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/12/tableview-2<h2>给TableView设置一个Delegate</h2>
<hr />
<h3>问题</h3>
<p>决定要把一个委托设置给TableView。</p>
<h3>方案</h3>
<p>将遵循UITableViewDelegate协议的对象指定为TableView的delegate属性:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">@implementation ViewController</p>
<ul>
<li><p>(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.</p>
<p> CGRect tableViewFrame = self.view.bounds;
self.myTableView = [[UITableView alloc] initWithFrame:tableViewFrame style:UITableViewStylePlain];
self.myTableView.delegate = self;</p>
<p> [self.view addSubview:self.myTableView];
}</p></li>
</ul>
<p>@end</code></pre></figure></p>
<p>上面的代码将当前对象指定为TableView的委托。myTableView是属于调用视图控制器的UITableView中的一个属性。该语句嵌入在viewDidLoad方法中,因为调用对象是一个UIViewController的实例,并且这个方法是放置这段代码的正确位置,这样可以确保语句只被调用一次。</p>
<h3>讨论</h3>
<p>UITableView类定义了一个名为delegate的属性,TableView可以把这个属性分配给一个遵循UITableViewDelegate协议的对象,换句话说,这个委托必须保证回应些协议中定义的消息,这些消息通过TableView本身发送到委托对象。把TableView的委托看作是收听由TableView发送各种事件的对象,例如当一个单元格被选中,或者当一个TableView要指出它的单元格的高度。某种程序上,我们也可以使用Interface Builder来修正TableView及其单元格的视图外观。只要打开Interface Builder,选择一个你之前创建的TableView,然后选择Tools --> Size Inspector,在Size Inspector 板,可以通过TableView单元格的高度值就可以修改TableView的视觉外观。</p>
<p>为了使你选择的TableView的委托对象符合UITableViewDelegate协议,你需要通过下面的方法把协议增加到对象的接口声明中:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import <UIKit/UIKit.h></p>
<p>@interface ViewController : UIViewController <UITableViewDelegate>
@property (strong, nonatomic) UITableView *myTableView;
@end</code></pre></figure></p>
<blockquote><p>对于那些UITableViewDelegate协议还有@required标记的消息要强制委托对象回应。可以选择性的回应其它消息,但是委托必须回应任何一条你想影响TableView的消息。</p></blockquote>
<p>发送到一个TableView的委托对象的消息将拾一个参数,这个参数会告诉委托对象哪个TableView发出的消息,注意到这一点非常重要,因为在特定情况下你需要在一个对象(通常是视图)中放置超过一张TableView。因为这一点,极力推荐你做出的决定是基于事实上向你委托对象发送确切消息的TableView,比如:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (CGFloat)tableView:(UITableView <em>)tableView heightForRowAtIndexPath:(NSIndexPath </em>)indexPath {
CGFloat result = 20.0f;</p>
<pre><code>if ([tableView isEqual:self.myTableView]) {
result = 40.0f;
}
return result;
</code></pre>
<p>}</code></pre></figure></p>
<p>值得注意的是TableView中单元格的位置是由其indexPath表示的。一个索引路径是section和row索引的组合,section索引是从零开始的并指定每个单元格属于哪个分组或Section,而section中中的单元格索引又是从零开始的。</p>
构造使用 TableView-12015-05-11T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/11/tableview-1<h2>介绍</h2>
<hr />
<p>简而言之,TableView是一个被分成不同部分的滚动视图,每部分又进一步被分成行。每行是一个UITableViewCell类的实例。通过些类的子类,可以创建自定义TableView的行。</p>
<p>使用TableView是一个向用户项目列表的理想方法。你可以把图片、文本和其他任何东西嵌入TableView单元格,可以自定义它们的高度、形状、分组、或更多TableView结构的简易性使用其高度可自定义。</p>
<p>一个TableView可以输入TableView数据源的数据,你会接收到各种事件并用TableView委托对象来控制其物理外观。这些分别在UITableViewDataSource和UITableViewDelegate的协议来定义。</p>
<p>尽管UITableView实例将UIScrollView子类化,TableView只能垂直滚动,这更大程序上是一个特点而非局限。</p>
<h2>实例化TableView</h2>
<hr />
<h3>问题</h3>
<p>在你的UI中放置一个TableView.</p>
<h3>方案</h3>
<p>实例化一个UITableView类的对象并把它一个子视图添加到你的视图中。</p>
<h3>讨论</h3>
<p>有2个办法可以实例化TableView:</p>
<ol>
<li>通过代码</li>
<li>使用界面生成器(Interface Builder)</li>
</ol>
<p>如果你使用界面生成器,创建一个TableView就是简单的把一个TableView从对象库拖拽到你的.xib文件中。如果你使用代码创建组建更顺手的话,也没有问题。你必须做的事情就是把UITableView类的一个对象实例化,让我们通过在视图控制器的头文件中定义我们的TableView开始:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import <UIKit/UIKit.h>
@interface Instantiating_a_TableView_ViewController : UIViewController
@property (nonatomic, strong) UITableView *myTableView;
@end</code></pre></figure></p>
<p>创建视图控制器就像分配和初始化UITableView的实例一样简单:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">- (void)viewDidLoad {
[super viewDidLoad];</p>
<pre><code>self.view.backgroundColor = [UIColor whiteColor];
self.myTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
[self.view addSubView:self.myTableView];
</code></pre>
<p>}</code></pre></figure></p>
<p>视图控制器中初始值设定项inti偗e:style:的样式参数允许我们需要哪种类型的TableView.有2种风格我们可以选择。</p>
<ul>
<li>UITableViewStylePlain</li>
</ul>
<p>创建一个没有背影图片的空白TableView</p>
<ul>
<li>UITableViewStyleGrouped</li>
</ul>
<p>创建一个有背影图片和圆角组边框的TableView,类似于Settings app。</p>
<p>如果你现在在iPhone模拟器上运行APP,将看到没有填充TableView单元格的TableView放在那里。</p>
<p><img src="/assets/img/ios/TableView-1.png" alt="TableView-1" /></p>
<p>图4-1. 一个没有内容的空白Table View</p>
自动布局和Visual Format Language2015-05-10T00:00:00+00:00http://jiaxianhua.github.io/autolayout/2015/05/10/autolayout<p>iOS 6 Programming Cookbook</p>
<h2>介绍</h2>
<hr />
<p>对程序员来说,使 UI 组件排列整齐总是一件很头疼的事。在复杂的 iOS 程序里的大部 分视图控制器都包含了大量的代码仅仅是设置屏幕上 UI 框架的,校准组件的水平或垂直位 置,确保组件在不同版本的 iOS 里都能得到合理的布局显示。除此之外,一些程序员甚至想 在不同的设备(如 iPhone 和 iPad)上使用相同的视图控制器。这就给代码添加了更多的复 杂性。苹果在 iOS 6 中使这些事情变得更简单了。它实现了从 OS X 到 iOS 的自动布局。我 们紧接着将要讨论自动布局的详细内容,但是首先让我为它做一个简要的介绍和解释它的用 途。</p>
<p> 假设有一个按钮,你想把它放置在屏幕的中央。视图中心和按钮中心的相对位置可以简
单地定义成如下:</p>
<ul>
<li>按钮的 center.x 相当于视图中心的 center.x</li>
<li>按钮的 center.y 相当于视图中心的 center.y</li>
</ul>
<p>苹果发现很多的 UI 组件的位置可以使用一个简单的方程等式得到解决:</p>
<p>Object1.property1=(object2.property2*multiplier)+constant value</p>
<p>例如:使用这个方程式,我们可以很容易地将一个按钮放置到他的父视图中,如下所 示:</p>
<pre><code>Button.center.x=(button.superview.center.x*1)+0
Button.center.y=(button.superview.center.y*1)+0
</code></pre>
<p>使用这个方程式,你可以在进行 UI 开发是做一些很有意思的事情,而不用像以前那样 麻烦了。前面涉及到的公式被封装到了 iOS SDK 中的 NSLayoutConstraint 类中。你创建的 每个约束条件(如这个类的一个实例)都只代表唯一的一个约束。例如,如果你想将按钮放 在其容器的中间,你必须将按钮的 x 和 y 方向的位置都设置为中间。这意味着你需要设置两 个约束条件。不能只设置一个约束条件就能将其放置到中央的位置。但是,本章后面将学习 Visual Format Language(可视化语言),这是 iOS 语言的拓展并简化了 UI 的布局。</p>
<p>限制条件可以通过 cross view 产生。例如,如果在一个视图上两个按钮,并且想使它 们之间的垂直距离是 100 个像素,那么就需要使用这种原则来产生限制条件将其添加到这两 个按钮的公共父控件(有可能此父控件同时拥有这两个按钮。根据的原则如下:</p>
<ul>
<li>如果限制条件位于一个公共的父视图的两个视图之间的,意味着这些视图具有相同的
父视图,那么就将限制条件添加到父视图中。</li>
<li>限制条件是在一个视图和其的父视图之间的,那么将限制条件添加到其父视图中。
·如果限制条件是两个不共享同一个父视图的视图之间的,那么将限制条件添加到这两
个视图的公共父视图中。</li>
</ul>
<p>图 3-1 介绍了这些限制条件是如何工作的。</p>
<p><img src="/assets/img/ios/ios6-cookbook-3-1.png" alt="ios6-cookbook-3-1" /></p>
<p>图 3-1.限制条件和它们要添加到的视图之前的关系图</p>
构造和显示具有样式的 Text2015-05-09T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/09/nsmutableattributedstring<h2>问题</h2>
<hr />
<p>你希望能够在 UI 组建中直接能够显示富文本,而不是针对每一种格式创建一个单独的
UI组建。例如,你想要在一个 UILabel 中显示一个句子,该句子中只有一个单词是粗体。</p>
<h2>方案</h2>
<hr />
<p>构建一个 NSAttributedString 实例对象,或者可修改的 NSMutableAttributedString。可以 通过设置 UI 组建(如 UILabel)具体的某个字符串属性,或简单的使用字符串属性内置方 法来在 canvas 上绘制 text。</p>
<h2>讨论</h2>
<hr />
<p>富文本是很好的东西!许多程序员需要在 UI 组建中的一行文本内显示各种样式的字符 串。例如,在文本中的一行之内,你可能需要同时显示竖直的和斜体字,即一个单词是斜 体,剩余的是正规文本。或者你希望一句话中的某个单词具有下划线。要实现这样的功能,我们中的有些程序员需要使用 Web View,但是这没有优化的方案,因为 Web View 渲染内容 非常的慢,这就影响了程序的执行效率。而现在,在 iOS 6 中,我们可以使用属性字符串 了。不知道为什么到现在苹果才将这个功能实现到 iOS 中,而在 Mac 开发中,很早以前就 可以使用属性字符串了。</p>
<p>在开始之前,我先用图 2-93 所示向你展示一下什么是属性字符串。然后我将通过编 程来实现这样的效果。</p>
<p><img src="/assets/img/ios/ios6-cookbook-2-93.png" alt="ios6-cookbook-2-93" /></p>
<p><strong>图 29-3 显示在 label 中的一个属性字符串</strong></p>
<blockquote><p>注意:为了更好的介绍,这里的文本是渲染在 UILabel 实例对象中的。</p></blockquote>
<p>OK,从上图中,我们看到什么呢?如下所列: 具有如下属性的“iOS”:</p>
<ul>
<li>60points 大小的粗体</li>
<li>背景色为黑色</li>
<li>前景色为红色</li>
</ul>
<p>具有如下属性的“SDK”:</p>
<ul>
<li>60points 大小的粗体</li>
<li>白色的文本</li>
<li>亮灰色的阴影</li>
</ul>
<p>构造属性字符串的最好方法就是使用 NSMutableAttributedString 类的 initWithString:方 法,同时传递一个 NSString 到这个方法中。这将会创建一个没有任何属性的属性字符串。 然后,通过 NSMutableAttributedString 类的 setAttributes:range:方法来给字符串中的不同部分 设置属性。这个方法有两个参数:</p>
<p><strong>setAttributes</strong></p>
<p>是一个字典,字典左右的 key 都是字符属性,每个 key 的值依赖于 key 本身。下面 是字典中可以设置的一些重要 key:</p>
<p><em>NSFontAttributeName</em></p>
<p>这个 key 的值是 UIFont 的一个实例,用来定义指定字符串范围的字体。</p>
<p><em>NSForegroundColorAttributeName</em></p>
<p> 这个 key 的值是 UIColor 类型,用来定义指定字符串范围的颜色。</p>
<p><em>NSBackgroundColorAttributeName</em></p>
<p> 这个 key 的值是 UIColor 类型,用来定义指定字符串范围的背景颜色。</p>
<p><em>NSShadowAttributeName</em></p>
<p> 这个 key 的值必须是 NSShadow 的一个实例,用来定义指定字符串范围的阴影。</p>
<p><strong>range</strong></p>
<p>是 NSRange 类型,用来指定属性应用在字符串的起点和长度。</p>
<blockquote><p>注意:要想查看上面这个方法可以传递的所有不同 key,可以浏览苹果在线文档关于 NSMutableAttributedstring 类介绍。由于相关的 URL 苹果可能会随时改变,我就不把 URL 直接放在这里了,不过很容易搜索到。</p></blockquote>
<p>在下面的例子中我将实例分为两个属性字典。“iOS”属性字典可以通过下面的代码来 构造:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">NSDictionary *attributesForFirstWord = @{
NSFontAttributeName : [UIFont boldSystemFontOfSize:60.0f],
NSForegroundColorAttributeName : [UIColor redColor],
NSBackgroundColorAttributeName : [UIColor blackColor]
};</code></pre></figure></p>
<p>而“SDK”则使用下面的属性:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">NSShadow <em>shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor darkGrayColor];
shadow.shadowOffset = CGSizeMake(4.0f, 4.0f);
NSDictionary </em>attributesForSecondWord = @{ NSFontAttributeName : [UIFont boldSystemFontOfSize:60.0f],
NSForegroundColorAttributeName : [UIColor whiteColor],
NSBackgroundColorAttributeName : [UIColor redColor],
NSShadowAttributeName : shadow
};</code></pre></figure></p>
<p>然后将它们放在一起。通过下面的代码,不仅创建了 label,还设置好了文本属性:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">#import "ViewController.h"
@interface ViewController ()</p>
<p>@property (nonatomic, strong) UILabel *label;
@end</p>
<p>@implementation ViewController
- (NSAttributedString <em>) attributedText{
NSString </em>string = @"iOS SDK";
NSMutableAttributedString <em>result = [[NSMutableAttributedString alloc] initWithString:string];
NSDictionary </em>attributesForFirstWord = @{
NSFontAttributeName : [UIFont boldSystemFontOfSize:60.0f],
NSForegroundColorAttributeName : [UIColor redColor],
NSBackgroundColorAttributeName : [UIColor blackColor]
};
NSShadow <em>shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor darkGrayColor];

shadow.shadowOffset = CGSizeMake(4.0f, 4.0f);
NSDictionary </em>attributesForSecondWord = @{
NSFontAttributeName : [UIFont boldSystemFontOfSize:60.0f],
NSForegroundColorAttributeName : [UIColor whiteColor],
NSBackgroundColorAttributeName : [UIColor redColor],
NSShadowAttributeName : shadow
};
/<em> Find the string "iOS" in the whole string and sets its attribute </em>/
[result setAttributes:attributesForFirstWord
range:[string rangeOfString:@"iOS"]];
/<em> Do the same thing for the string "SDK" </em>/
[result setAttributes:attributesForSecondWord
range:[string rangeOfString:@"SDK"]];
return [[NSAttributedString alloc] initWithAttributedString:result];
}</p>
<ul>
<li>(void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.label = [[UILabel alloc] init];
self.label.backgroundColor = [UIColor clearColor];
self.label.attributedText = [self attributedText];
[self.label sizeToFit];
self.label.center = self.view.center;
[self.view addSubview:self.label];
}</code></pre></figure></li>
</ul>
iOS 内存管理2015-05-08T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/08/ios-memory-management<p>软件运行时会分配和使用设备的内存资源,因此,在软件开发的过程中,需要进行内存管理,以保证高效、快速的分配内存,并且在适当的时候释放和回收内存资源。</p>
<h2>一、Objective-C内存管理的对象</h2>
<hr />
<p>IOS开发中,内存中的对象主要有两类,一类是值类型,比如int、float、struct等基本数据类型,另一类是引用类型,也就是继承自NSObject类的所有的OC对象。前一种值类型不需要我们管理,后一种引用类型是需要我们管理内存的,一旦管理不好,就会产生非常糟糕的后果。</p>
<h3>为什么值类型不需要管理,而引用类型需要管理呢?那是因为他们分配内存方式不一样。</h3>
<p>值类型会被放入栈中,他们依次紧密排列,在内存中占有一块连续的内存空间,遵循先进后出的原则。引用类型会被放到堆中,当给对象分配内存空间时,会随机的从内存当中开辟空间,对象与对象之间可能会留有不确定大小的空白空间,因此会产生很多内存碎片,需要我们管理。</p>
<p>栈内存与堆内存从性能上比较,栈内存要优于堆内存,这是因为栈遵循先进后出的原则,因此当数据量过大时,存入栈会明显的降低性能。因此,我们会把大量的数据存入堆中,然后栈中存放堆的地址,当需要调用数据时,就可以快速的通过栈内的地址找到堆中的数据。</p>
<p>值类型和引用类型之间是可以相互转化的,把值类型转化为引用类型的过程叫做装箱,比如把int包装为NSNumber,这个过程会增加程序的运行时间,降低性能。而把引用类型转为值类型的过程叫做拆箱,比如把NSNumer转为float,在拆箱的过程中,我们一定要注意数据原有的类型,如果类型错误,可能导致拆箱失败,因此会存在安全性的问题。手动的拆箱和装箱,都会增加程序的运行时间,降低代码可读性,影响性能。</p>
<p>在IOS开发过程中,栈内存中的值类型系统会自动管理,堆内存中的引用类型是需要我们管理的。每个OC对象内部都专门有四个字节来存储引用计数器,它是一个整数,表示对象被引用的次数,通过它可以判断对象是否被回收,如果引用计数为0,对象回收,不为0不回收。当对象执行alloc、new或者retain时,引用计数加1,release时,引用计数减1。</p>
<h2>二、Objective-C管理内存的方式</h2>
<hr />
<p>Objective-c中提供了两种内存管理机制MRC(Mannul Reference Counting)和ARC(Automatic Reference Counting),分别提供对内存的手动和自动管理,来满足不同的需求。MRC与ARC区别如下图所示。</p>
<p><img src="/assets/img/ios/memory-1.png" alt="memory-1" /></p>
<ul>
<li>MRC(人工引用计数),手动管理内存。</li>
</ul>
<p>MRC模式下,所有的对象都需要手动的添加retain、release代码来管理内存。使用MRC,需要遵守谁创建,谁回收的原则。也就是谁alloc,谁release;谁retain,谁release。</p>
<p>当引用计数为0的时候,必须回收,引用计数不为0,不能回收,如果引用计数为0,但是没有回收,会造成内存泄露。如果引用计数为0,继续释放,会造成野指针。为了避免出现野指针,我们在释放的时候,会先让指针=nil。</p>
<ul>
<li>ARC(自动引用计数),自动管理内存。</li>
</ul>
<p>ARC是IOS5推出的新功能,通过ARC,可以自动的管理内存。在ARC模式下,只要没有强指针(强引用)指向对象,对象就会被释放。在ARC模式下,不允许使用retain、release、retainCount等方法。并且,如果使用dealloc方法时,不允许调用[super dealloc]方法。</p>
<p>ARC模式下的property变量修饰词为strong、weak,相当于MRC模式下的retain、assign。strong :代替retain,缺省关键词,代表强引用。weak:代替assign,声明了一个可以自动设置nil的弱引用,但是比assign多一个功能,指针指向的地址被释放之后,指针本身也会自动被释放。</p>
<h2>三、与内存有关的修饰符</h2>
<hr />
<p><strong>strong</strong> :强引用,ARC中使用,与MRC中retain类似,使用之后,计数器+1。</p>
<p><strong>weak</strong> :弱引用 ,ARC中使用,如果只想的对象被释放了,其指向nil,可以有效的避免野指针,其引用计数为1。</p>
<p><strong>readwrite</strong> : 可读可写特性,需要生成getter方法和setter方法时使用。</p>
<p><strong>readonly</strong> : 只读特性,只会生成getter方法 不会生成setter方法,不希望属性在类外改变。</p>
<p><strong>assign</strong> :赋值特性,不涉及引用计数,弱引用,setter方法将传入参数赋值给实例变量,仅设置变量时使用。</p>
<p><strong>retain</strong> :表示持有特性,setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1。</p>
<p><strong>copy</strong> :表示拷贝特性,setter方法将传入对象复制一份,需要完全一份新的变量时。</p>
<p><strong>nonatomic</strong> :非原子操作,不加同步,多线程访问可提高性能,但是线程不安全的。决定编译器生成的setter getter是否是原子操作。</p>
<p><strong>atomic</strong> :原子操作,同步的,表示多线程安全,与nonatomic相反。</p>
<h2>四、MRC与ARC混编</h2>
<hr />
<p>MRC与ARC理论上是不能兼容的,也就是你如果创建的项目是ARC模式的,在你的代码中是不能使用release,否则会出现内存问题。现在大部分程序都会选择ARC的方式,但是很多第三方的框架是MRC模式,如果想把这些第三方的文件加到自己项目中,需要进行标识,否则编译的时候会出现错误。</p>
<p>在ARC的项目中,对MRC的文件可以添加编译选项-fno-objc-arc的标识;在MRC的项目中,对ARC的文件可以添加编译选项 -fobjc-arc的标识。 步骤如下图所示。</p>
<p><img src="/assets/img/ios/memory-2.png" alt="memory-2" /></p>
<p>把MRC文件转为ARC,实际上是去掉文件中的retain、release,因此也通过下图中方式完成。</p>
<p><img src="/assets/img/ios/memory-3.png" alt="memory-3" /></p>
<blockquote><p>原文:<a href="http://www.code4blog.com/archives/678">http://www.code4blog.com/archives/678</a></p></blockquote>
Alamofire2015-05-07T00:00:00+00:00http://jiaxianhua.github.io/swift/2015/05/07/alamofire<p><img src="https://raw.githubusercontent.com/Alamofire/Alamofire/assets/alamofire.png" alt="Alamofire: Elegant Networking in Swift" /></p>
<p>Alamofire is an HTTP networking library written in Swift.</p>
<h2>Features</h2>
<ul>
<li>[x] Chainable Request / Response methods</li>
<li>[x] URL / JSON / plist Parameter Encoding</li>
<li>[x] Upload File / Data / Stream</li>
<li>[x] Download using Request or Resume data</li>
<li>[x] Authentication with NSURLCredential</li>
<li>[x] HTTP Response Validation</li>
<li>[x] Progress Closure & NSProgress</li>
<li>[x] cURL Debug Output</li>
<li>[x] Comprehensive Unit Test Coverage</li>
<li>[x] Complete Documentation</li>
</ul>
<h2>Requirements</h2>
<ul>
<li>iOS 7.0+ / Mac OS X 10.9+</li>
<li>Xcode 6.3</li>
</ul>
<h2>Communication</h2>
<ul>
<li>If you <strong>need help</strong>, use <a href="http://stackoverflow.com/questions/tagged/alamofire">Stack Overflow</a>. (Tag 'alamofire')</li>
<li>If you'd like to <strong>ask a general question</strong>, use <a href="http://stackoverflow.com/questions/tagged/alamofire">Stack Overflow</a>.</li>
<li>If you <strong>found a bug</strong>, open an issue.</li>
<li>If you <strong>have a feature request</strong>, open an issue.</li>
<li>If you <strong>want to contribute</strong>, submit a pull request.</li>
</ul>
<h2>Installation</h2>
<blockquote><p><strong>Embedded frameworks require a minimum deployment target of iOS 8 or OS X Mavericks.</strong></p>
<p>To use Alamofire with a project targeting iOS 7, you must include the <code>Alamofire.swift</code> source file directly in your project. See the <a href="#source-file">'Source File'</a> section for instructions.</p></blockquote>
<h3>CocoaPods</h3>
<p><a href="http://cocoapods.org">CocoaPods</a> is a dependency manager for Cocoa projects.</p>
<p>CocoaPods 0.36 adds supports for Swift and embedded frameworks. You can install it with the following command:</p>
<pre><code class="bash">$ gem install cocoapods
</code></pre>
<p>To integrate Alamofire into your Xcode project using CocoaPods, specify it in your <code>Podfile</code>:</p>
<pre><code class="ruby">source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
pod 'Alamofire', '~> 1.2'
</code></pre>
<p>Then, run the following command:</p>
<pre><code class="bash">$ pod install
</code></pre>
<h3>Carthage</h3>
<p>Carthage is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application.</p>
<p>You can install Carthage with <a href="http://brew.sh/">Homebrew</a> using the following command:</p>
<pre><code class="bash">$ brew update
$ brew install carthage
</code></pre>
<p>To integrate Alamofire into your Xcode project using Carthage, specify it in your <code>Cartfile</code>:</p>
<pre><code class="ogdl">github "Alamofire/Alamofire" >= 1.2
</code></pre>
<h3>Manually</h3>
<p>If you prefer not to use either of the aforementioned dependency managers, you can integrate Alamofire into your project manually.</p>
<h4>Embedded Framework</h4>
<ul>
<li>Add Alamofire as a <a href="http://git-scm.com/docs/git-submodule">submodule</a> by opening the Terminal, <code>cd</code>-ing into your top-level project directory, and entering the following command:</li>
</ul>
<pre><code class="bash">$ git submodule add https://github.com/Alamofire/Alamofire.git
</code></pre>
<ul>
<li><p>Open the new <code>Alamofire</code> folder, and drag the <code>Alamofire.xcodeproj</code> into the Project Navigator of your application's Xcode project.</p>
<blockquote><p>It should appear nested underneath your application's blue project icon. Whether it is above or below all the other Xcode groups does not matter.</p></blockquote></li>
<li><p>Select the <code>Alamofire.xcodeproj</code> in the Project Navigator and verify the deployment target matches that of your application target.</p></li>
<li>Next, select your application project in the Project Navigator (blue project icon) to navigate to the target configuration window and select the application target under the "Targets" heading in the sidebar.</li>
<li>In the tab bar at the top of that window, open the "General" panel.</li>
<li>Click on the <code>+</code> button under the "Embedded Binaries" section.</li>
<li><p>You will see two different <code>Alamofire.xcodeproj</code> folders each with two different versions of the <code>Alamofire.framework</code> nested inside a <code>Products</code> folder.</p>
<blockquote><p>It does not matter which <code>Products</code> folder you choose from, but it does matter whether you choose the top or bottom <code>Alamofire.framework</code>.</p></blockquote></li>
<li><p>Select the top <code>Alamofire.framework</code> for iOS and the bottom one for OS X.</p>
<blockquote><p>You can verify which one you selected by inspecting the build log for your project. The build target for <code>Alamofire</code> will be listed as either <code>Alamofire iOS</code> or <code>Alamofire OSX</code>.</p></blockquote></li>
<li><p>And that's it!</p></li>
</ul>
<blockquote><p>The <code>Alamofire.framework</code> is automagically added as a target dependency, linked framework and embedded framework in a copy files build phase which is all you need to build on the simulator and a device.</p></blockquote>
<h4>Source File</h4>
<p>For application targets that do not support embedded frameworks, such as iOS 7, Alamofire can be integrated by adding the <code>Alamofire.swift</code> source file directly into your project. Note that you will no longer need to <code>import Alamofire</code> since you are not actually loading a framework. Additionally, any of the calling conventions described in the <a href="#usage">'Usage'</a> section with the <code>Alamofire</code> prefix would instead omit it (for example, <code>Alamofire.request</code> becomes <code>request</code>), since this functionality is incorporated into the top-level namespace.</p>
<hr />
<h2>Usage</h2>
<h3>Making a Request</h3>
<pre><code class="swift">import Alamofire
Alamofire.request(.GET, "http://httpbin.org/get")
</code></pre>
<h3>Response Handling</h3>
<pre><code class="swift">Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
.response { (request, response, data, error) in
println(request)
println(response)
println(error)
}
</code></pre>
<blockquote><p>Networking in Alamofire is done <em>asynchronously</em>. Asynchronous programming may be a source of frustration to programmers unfamiliar with the concept, but there are <a href="https://developer.apple.com/library/ios/qa/qa1693/_index.html">very good reasons</a> for doing it this way.</p>
<p>Rather than blocking execution to wait for a response from the server, a <a href="http://en.wikipedia.org/wiki/Callback_%28computer_programming%29">callback</a> is specified to handle the response once it's received. The result of a request is only available inside the scope of a response handler. Any execution contingent on the response or data received from the server must be done within a handler.</p></blockquote>
<h3>Response Serialization</h3>
<p><strong>Built-in Response Methods</strong></p>
<ul>
<li><code>response()</code></li>
<li><code>responseString(encoding: NSStringEncoding)</code></li>
<li><code>responseJSON(options: NSJSONReadingOptions)</code></li>
<li><code>responsePropertyList(options: NSPropertyListReadOptions)</code></li>
</ul>
<h4>Response String Handler</h4>
<pre><code class="swift">Alamofire.request(.GET, "http://httpbin.org/get")
.responseString { (_, _, string, _) in
println(string)
}
</code></pre>
<h4>Response JSON Handler</h4>
<pre><code class="swift">Alamofire.request(.GET, "http://httpbin.org/get")
.responseJSON { (_, _, JSON, _) in
println(JSON)
}
</code></pre>
<h4>Chained Response Handlers</h4>
<p>Response handlers can even be chained:</p>
<pre><code class="swift">Alamofire.request(.GET, "http://httpbin.org/get")
.responseString { (_, _, string, _) in
println(string)
}
.responseJSON { (_, _, JSON, _) in
println(JSON)
}
</code></pre>
<h3>HTTP Methods</h3>
<p><code>Alamofire.Method</code> lists the HTTP methods defined in <a href="http://tools.ietf.org/html/rfc7231#section-4.3">RFC 7231 §4.3</a>:</p>
<pre><code class="swift">public enum Method: String {
case OPTIONS = "OPTIONS"
case GET = "GET"
case HEAD = "HEAD"
case POST = "POST"
case PUT = "PUT"
case PATCH = "PATCH"
case DELETE = "DELETE"
case TRACE = "TRACE"
case CONNECT = "CONNECT"
}
</code></pre>
<p>These values can be passed as the first argument of the <code>Alamofire.request</code> method:</p>
<pre><code class="swift">Alamofire.request(.POST, "http://httpbin.org/post")
Alamofire.request(.PUT, "http://httpbin.org/put")
Alamofire.request(.DELETE, "http://httpbin.org/delete")
</code></pre>
<h3>Parameters</h3>
<h4>GET Request With URL-Encoded Parameters</h4>
<pre><code class="swift">Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
// http://httpbin.org/get?foo=bar
</code></pre>
<h4>POST Request With URL-Encoded Parameters</h4>
<pre><code class="swift">let parameters = [
"foo": "bar",
"baz": ["a", 1],
"qux": [
"x": 1,
"y": 2,
"z": 3
]
]
Alamofire.request(.POST, "http://httpbin.org/post", parameters: parameters)
// HTTP body: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3
</code></pre>
<h3>Parameter Encoding</h3>
<p>Parameters can also be encoded as JSON, Property List, or any custom format, using the <code>ParameterEncoding</code> enum:</p>
<pre><code class="swift">enum ParameterEncoding {
case URL
case JSON
case PropertyList(format: NSPropertyListFormat,
options: NSPropertyListWriteOptions)
func encode(request: NSURLRequest,
parameters: [String: AnyObject]?) ->
(NSURLRequest, NSError?)
{ ... }
}
</code></pre>
<ul>
<li><code>URL</code>: A query string to be set as or appended to any existing URL query for <code>GET</code>, <code>HEAD</code>, and <code>DELETE</code> requests, or set as the body for requests with any other HTTP method. The <code>Content-Type</code> HTTP header field of an encoded request with HTTP body is set to <code>application/x-www-form-urlencoded</code>. <em>Since there is no published specification for how to encode collection types, Alamofire follows the convention of appending <code>[]</code> to the key for array values (<code>foo[]=1&foo[]=2</code>), and appending the key surrounded by square brackets for nested dictionary values (<code>foo[bar]=baz</code>).</em></li>
<li><code>JSON</code>: Uses <code>NSJSONSerialization</code> to create a JSON representation of the parameters object, which is set as the body of the request. The <code>Content-Type</code> HTTP header field of an encoded request is set to <code>application/json</code>.</li>
<li><code>PropertyList</code>: Uses <code>NSPropertyListSerialization</code> to create a plist representation of the parameters object, according to the associated format and write options values, which is set as the body of the request. The <code>Content-Type</code> HTTP header field of an encoded request is set to <code>application/x-plist</code>.</li>
<li><code>Custom</code>: Uses the associated closure value to construct a new request given an existing request and parameters.</li>
</ul>
<h4>Manual Parameter Encoding of an NSURLRequest</h4>
<pre><code class="swift">let URL = NSURL(string: "http://httpbin.org/get")!
var request = NSURLRequest(URL: URL)
let parameters = ["foo": "bar"]
let encoding = Alamofire.ParameterEncoding.URL
(request, _) = encoding.encode(request, parameters: parameters)
</code></pre>
<h4>POST Request with JSON-encoded Parameters</h4>
<pre><code class="swift">let parameters = [
"foo": [1,2,3],
"bar": [
"baz": "qux"
]
]
Alamofire.request(.POST, "http://httpbin.org/post", parameters: parameters, encoding: .JSON)
// HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}}
</code></pre>
<h3>Caching</h3>
<p>Caching is handled on the system framework level by <a href="https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLCache_Class/Reference/Reference.html#//apple_ref/occ/cl/NSURLCache"><code>NSURLCache</code></a>.</p>
<h3>Uploading</h3>
<p><strong>Supported Upload Types</strong></p>
<ul>
<li>File</li>
<li>Data</li>
<li>Stream</li>
</ul>
<h4>Uploading a File</h4>
<pre><code class="swift">let fileURL = NSBundle.mainBundle()
.URLForResource("Default",
withExtension: "png")
Alamofire.upload(.POST, "http://httpbin.org/post", file: fileURL)
</code></pre>
<h4>Uploading w/Progress</h4>
<pre><code class="swift">Alamofire.upload(.POST, "http://httpbin.org/post", file: fileURL)
.progress { (bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) in
println(totalBytesWritten)
}
.responseJSON { (request, response, JSON, error) in
println(JSON)
}
</code></pre>
<h3>Downloading</h3>
<p><strong>Supported Download Types</strong></p>
<ul>
<li>Request</li>
<li>Resume Data</li>
</ul>
<h4>Downloading a File</h4>
<pre><code class="swift">Alamofire.download(.GET, "http://httpbin.org/stream/100", destination: { (temporaryURL, response) in
if let directoryURL = NSFileManager.defaultManager()
.URLsForDirectory(.DocumentDirectory,
inDomains: .UserDomainMask)[0]
as? NSURL {
let pathComponent = response.suggestedFilename
return directoryURL.URLByAppendingPathComponent(pathComponent!)
}
return temporaryURL
})
</code></pre>
<h4>Using the Default Download Destination</h4>
<pre><code class="swift">let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)
Alamofire.download(.GET, "http://httpbin.org/stream/100", destination: destination)
</code></pre>
<h4>Downloading a File w/Progress</h4>
<pre><code class="swift">Alamofire.download(.GET, "http://httpbin.org/stream/100", destination: destination)
.progress { (bytesRead, totalBytesRead, totalBytesExpectedToRead) in
println(totalBytesRead)
}
.response { (request, response, _, error) in
println(response)
}
</code></pre>
<h3>Authentication</h3>
<p>Authentication is handled on the system framework level by <a href="https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLAuthenticationChallenge_Class/Reference/Reference.html"><code>NSURLCredential</code> and <code>NSURLAuthenticationChallenge</code></a>.</p>
<p><strong>Supported Authentication Schemes</strong></p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Basic_access_authentication">HTTP Basic</a></li>
<li><a href="http://en.wikipedia.org/wiki/Digest_access_authentication">HTTP Digest</a></li>
<li><a href="http://en.wikipedia.org/wiki/Kerberos_%28protocol%29">Kerberos</a></li>
<li><a href="http://en.wikipedia.org/wiki/NT_LAN_Manager">NTLM</a></li>
</ul>
<h4>HTTP Basic Authentication</h4>
<pre><code class="swift">let user = "user"
let password = "password"
Alamofire.request(.GET, "https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(user: user, password: password)
.response {(request, response, _, error) in
println(response)
}
</code></pre>
<h4>Authentication with NSURLCredential</h4>
<pre><code class="swift">let user = "user"
let password = "password"
let credential = NSURLCredential(user: user, password: password, persistence: .ForSession)
Alamofire.request(.GET, "https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(usingCredential: credential)
.response {(request, response, _, error) in
println(response)
}
</code></pre>
<h3>Validation</h3>
<p>By default, Alamofire treats any completed request to be successful, regardless of the content of the response. Calling <code>validate</code> before a response handler causes an error to be generated if the response had an unacceptable status code or MIME type.</p>
<h4>Manual Validation</h4>
<pre><code class="swift">Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.response { (_, _, _, error) in
println(error)
}
</code></pre>
<h4>Automatic Validation</h4>
<p>Automatically validates status code within <code>200...299</code> range, and that the <code>Content-Type</code> header of the response matches the <code>Accept</code> header of the request, if one is provided.</p>
<pre><code class="swift">Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
.validate()
.response { (_, _, _, error) in
println(error)
}
</code></pre>
<h3>Printable</h3>
<pre><code class="swift">let request = Alamofire.request(.GET, "http://httpbin.org/ip")
println(request)
// GET http://httpbin.org/ip (200)
</code></pre>
<h3>DebugPrintable</h3>
<pre><code class="swift">let request = Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
debugPrintln(request)
</code></pre>
<h4>Output (cURL)</h4>
<pre><code class="bash">$ curl -i \
-H "User-Agent: Alamofire" \
-H "Accept-Encoding: Accept-Encoding: gzip;q=1.0,compress;q=0.5" \
-H "Accept-Language: en;q=1.0,fr;q=0.9,de;q=0.8,zh-Hans;q=0.7,zh-Hant;q=0.6,ja;q=0.5" \
"http://httpbin.org/get?foo=bar"
</code></pre>
<hr />
<h2>Advanced Usage</h2>
<blockquote><p>Alamofire is built on <code>NSURLSession</code> and the Foundation URL Loading System. To make the most of
this framework, it is recommended that you be familiar with the concepts and capabilities of the underlying networking stack.</p></blockquote>
<p><strong>Recommended Reading</strong></p>
<ul>
<li><a href="https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html">URL Loading System Programming Guide</a></li>
<li><a href="https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSURLSession_class/Introduction/Introduction.html#//apple_ref/occ/cl/NSURLSession">NSURLSession Class Reference</a></li>
<li><a href="https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLCache_Class/Reference/Reference.html#//apple_ref/occ/cl/NSURLCache">NSURLCache Class Reference</a></li>
<li><a href="https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLAuthenticationChallenge_Class/Reference/Reference.html">NSURLAuthenticationChallenge Class Reference</a></li>
</ul>
<h3>Manager</h3>
<p>Top-level convenience methods like <code>Alamofire.request</code> use a shared instance of <code>Alamofire.Manager</code>, which is configured with the default <code>NSURLSessionConfiguration</code>.</p>
<p>As such, the following two statements are equivalent:</p>
<pre><code class="swift">Alamofire.request(.GET, "http://httpbin.org/get")
</code></pre>
<pre><code class="swift">let manager = Alamofire.Manager.sharedInstance
manager.request(NSURLRequest(URL: NSURL(string: "http://httpbin.org/get")))
</code></pre>
<p>Applications can create managers for background and ephemeral sessions, as well as new managers that customize the default session configuration, such as for default headers (<code>HTTPAdditionalHeaders</code>) or timeout interval (<code>timeoutIntervalForRequest</code>).</p>
<h4>Creating a Manager with Default Configuration</h4>
<pre><code class="swift">let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
let manager = Alamofire.Manager(configuration: configuration)
</code></pre>
<h4>Creating a Manager with Background Configuration</h4>
<pre><code class="swift">let configuration = NSURLSessionConfiguration.backgroundSessionConfiguration("com.example.app.background")
let manager = Alamofire.Manager(configuration: configuration)
</code></pre>
<h4>Creating a Manager with Ephemeral Configuration</h4>
<pre><code class="swift">let configuration = NSURLSessionConfiguration.ephemeralSessionConfiguration()
let manager = Alamofire.Manager(configuration: configuration)
</code></pre>
<h4>Modifying Session Configuration</h4>
<pre><code class="swift">var defaultHeaders = Alamofire.Manager.sharedInstance.session.configuration.HTTPAdditionalHeaders ?? [:]
defaultHeaders["DNT"] = "1 (Do Not Track Enabled)"
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPAdditionalHeaders = defaultHeaders
let manager = Alamofire.Manager(configuration: configuration)
</code></pre>
<blockquote><p>This is <strong>not</strong> recommended for <code>Authorization</code> or <code>Content-Type</code> headers. Instead, use <code>URLRequestConvertible</code> and <code>ParameterEncoding</code>, respectively.</p></blockquote>
<h3>Request</h3>
<p>The result of a <code>request</code>, <code>upload</code>, or <code>download</code> method is an instance of <code>Alamofire.Request</code>. A request is always created using a constructor method from an owning manager, and never initialized directly.</p>
<p>Methods like <code>authenticate</code>, <code>validate</code>, and <code>response</code> return the caller in order to facilitate chaining.</p>
<p>Requests can be suspended, resumed, and cancelled:</p>
<ul>
<li><code>suspend()</code>: Suspends the underlying task and dispatch queue</li>
<li><code>resume()</code>: Resumes the underlying task and dispatch queue. If the owning manager does not have <code>startRequestsImmediately</code> set to <code>true</code>, the request must call <code>resume()</code> in order to start.</li>
<li><code>cancel()</code>: Cancels the underlying task, producing an error that is passed to any registered response handlers.</li>
</ul>
<h3>Response Serialization</h3>
<h4>Creating a Custom Response Serializer</h4>
<p>Alamofire provides built-in response serialization for strings, JSON, and property lists, but others can be added in extensions on <code>Alamofire.Request</code>.</p>
<p>For example, here's how a response handler using <a href="https://github.com/mattt/Ono">Ono</a> might be implemented:</p>
<pre><code class="swift">extension Request {
class func XMLResponseSerializer() -> Serializer {
return { (request, response, data) in
if data == nil {
return (nil, nil)
}
var XMLSerializationError: NSError?
let XML = ONOXMLDocument(data: data, &XMLSerializationError)
return (XML, XMLSerializationError)
}
}
func responseXMLDocument(completionHandler: (NSURLRequest, NSHTTPURLResponse?, ONOXMLDocument?, NSError?) -> Void) -> Self {
return response(serializer: Request.XMLResponseSerializer(), completionHandler: { (request, response, XML, error) in
completionHandler(request, response, XML as? ONOXMLDocument, error)
})
}
}
</code></pre>
<h4>Generic Response Object Serialization</h4>
<p>Generics can be used to provide automatic, type-safe response object serialization.</p>
<pre><code class="swift">@objc public protocol ResponseObjectSerializable {
init?(response: NSHTTPURLResponse, representation: AnyObject)
}
extension Alamofire.Request {
public func responseObject<T: ResponseObjectSerializable>(completionHandler: (NSURLRequest, NSHTTPURLResponse?, T?, NSError?) -> Void) -> Self {
let serializer: Serializer = { (request, response, data) in
let JSONSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
let (JSON: AnyObject?, serializationError) = JSONSerializer(request, response, data)
if response != nil && JSON != nil {
return (T(response: response!, representation: JSON!), nil)
} else {
return (nil, serializationError)
}
}
return response(serializer: serializer, completionHandler: { (request, response, object, error) in
completionHandler(request, response, object as? T, error)
})
}
}
</code></pre>
<pre><code class="swift">final class User: ResponseObjectSerializable {
let username: String
let name: String
required init?(response: NSHTTPURLResponse, representation: AnyObject) {
self.username = response.URL!.lastPathComponent
self.name = representation.valueForKeyPath("name") as String
}
}
</code></pre>
<pre><code class="swift">Alamofire.request(.GET, "http://example.com/users/mattt")
.responseObject { (_, _, user: User?, _) in
println(user)
}
</code></pre>
<p>The same approach can also be used to handle endpoints that return a representation of a collection of objects:</p>
<pre><code class="swift">@objc public protocol ResponseCollectionSerializable {
class func collection(#response: NSHTTPURLResponse, representation: AnyObject) -> [Self]
}
extension Alamofire.Request {
public func responseCollection<T: ResponseCollectionSerializable>(completionHandler: (NSURLRequest, NSHTTPURLResponse?, [T]?, NSError?) -> Void) -> Self {
let serializer: Serializer = { (request, response, data) in
let JSONSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
let (JSON: AnyObject?, serializationError) = JSONSerializer(request, response, data)
if response != nil && JSON != nil {
return (T.collection(response: response!, representation: JSON!), nil)
} else {
return (nil, serializationError)
}
}
return response(serializer: serializer, completionHandler: { (request, response, object, error) in
completionHandler(request, response, object as? [T], error)
})
}
}
</code></pre>
<h3>URLStringConvertible</h3>
<p>Types adopting the <code>URLStringConvertible</code> protocol can be used to construct URL strings, which are then used to construct URL requests. <code>NSString</code>, <code>NSURL</code>, <code>NSURLComponents</code>, and <code>NSURLRequest</code> conform to <code>URLStringConvertible</code> by default, allowing any of them to be passed as <code>URLString</code> parameters to the <code>request</code>, <code>upload</code>, and <code>download</code> methods:</p>
<pre><code class="swift">let string = NSString(string: "http://httpbin.org/post")
Alamofire.request(.POST, string)
let URL = NSURL(string: string)!
Alamofire.request(.POST, URL)
let URLRequest = NSURLRequest(URL: URL)
Alamofire.request(.POST, URLRequest) // overrides `HTTPMethod` of `URLRequest`
let URLComponents = NSURLComponents(URL: URL, resolvingAgainstBaseURL: true)
Alamofire.request(.POST, URLComponents)
</code></pre>
<p>Applications interacting with web applications in a significant manner are encouraged to have custom types conform to <code>URLStringConvertible</code> as a convenient way to map domain-specific models to server resources.</p>
<h4>Type-Safe Routing</h4>
<pre><code class="swift">extension User: URLStringConvertible {
static let baseURLString = "http://example.com"
var URLString: String {
return User.baseURLString + "/users/\(username)/"
}
}
</code></pre>
<pre><code class="swift">let user = User(username: "mattt")
Alamofire.request(.GET, user) // http://example.com/users/mattt
</code></pre>
<h3>URLRequestConvertible</h3>
<p>Types adopting the <code>URLRequestConvertible</code> protocol can be used to construct URL requests. <code>NSURLRequest</code> conforms to <code>URLRequestConvertible</code> by default, allowing it to be passed into <code>request</code>, <code>upload</code>, and <code>download</code> methods directly (this is the recommended way to specify custom HTTP header fields or HTTP body for individual requests):</p>
<pre><code class="swift">let URL = NSURL(string: "http://httpbin.org/post")!
let mutableURLRequest = NSMutableURLRequest(URL: URL)
mutableURLRequest.HTTPMethod = "POST"
let parameters = ["foo": "bar"]
var JSONSerializationError: NSError? = nil
mutableURLRequest.HTTPBody = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: &JSONSerializationError)
mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
Alamofire.request(mutableURLRequest)
</code></pre>
<p>Applications interacting with web applications in a significant manner are encouraged to have custom types conform to <code>URLRequestConvertible</code> as a way to ensure consistency of requested endpoints. Such an approach can be used to abstract away server-side inconsistencies and provide type-safe routing, as well as manage authentication credentials and other state.</p>
<h4>API Parameter Abstraction</h4>
<pre><code class="swift">enum Router: URLRequestConvertible {
static let baseURLString = "http://example.com"
static let perPage = 50
case Search(query: String, page: Int)
// MARK: URLRequestConvertible
var URLRequest: NSURLRequest {
let (path: String, parameters: [String: AnyObject]?) = {
switch self {
case .Search(let query, let page) where page > 1:
return ("/search", ["q": query, "offset": Router.perPage * page])
case .Search(let query, _):
return ("/search", ["q": query])
}
}()
let URL = NSURL(string: Router.baseURLString)!
let URLRequest = NSURLRequest(URL: URL.URLByAppendingPathComponent(path))
let encoding = Alamofire.ParameterEncoding.URL
return encoding.encode(URLRequest, parameters: parameters).0
}
}
</code></pre>
<pre><code class="swift">Alamofire.request(Router.Search(query: "foo bar", page: 1)) // ?q=foo+bar&offset=50
</code></pre>
<h4>CRUD & Authorization</h4>
<pre><code class="swift">enum Router: URLRequestConvertible {
static let baseURLString = "http://example.com"
static var OAuthToken: String?
case CreateUser([String: AnyObject])
case ReadUser(String)
case UpdateUser(String, [String: AnyObject])
case DestroyUser(String)
var method: Alamofire.Method {
switch self {
case .CreateUser:
return .POST
case .ReadUser:
return .GET
case .UpdateUser:
return .PUT
case .DestroyUser:
return .DELETE
}
}
var path: String {
switch self {
case .CreateUser:
return "/users"
case .ReadUser(let username):
return "/users/\(username)"
case .UpdateUser(let username, _):
return "/users/\(username)"
case .DestroyUser(let username):
return "/users/\(username)"
}
}
// MARK: URLRequestConvertible
var URLRequest: NSURLRequest {
let URL = NSURL(string: Router.baseURLString)!
let mutableURLRequest = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(path))
mutableURLRequest.HTTPMethod = method.rawValue
if let token = Router.OAuthToken {
mutableURLRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
}
switch self {
case .CreateUser(let parameters):
return Alamofire.ParameterEncoding.JSON.encode(mutableURLRequest, parameters: parameters).0
case .UpdateUser(_, let parameters):
return Alamofire.ParameterEncoding.URL.encode(mutableURLRequest, parameters: parameters).0
default:
return mutableURLRequest
}
}
}
</code></pre>
<pre><code class="swift">Alamofire.request(Router.ReadUser("mattt")) // GET /users/mattt
</code></pre>
<hr />
<h2>FAQ</h2>
<h3>When should I use Alamofire?</h3>
<p>If you're starting a new project in Swift, and want to take full advantage of its conventions and language features, Alamofire is a great choice. Although not as fully-featured as AFNetworking, Alamofire is much nicer to work with, and should satisfy the vast majority of networking use cases.</p>
<blockquote><p>It's important to note that two libraries aren't mutually exclusive: AFNetworking and Alamofire can peacefully exist in the same code base.</p></blockquote>
<h3>When should I use AFNetworking?</h3>
<p>AFNetworking remains the premiere networking library available for OS X and iOS, and can easily be used in Swift, just like any other Objective-C code. AFNetworking is stable and reliable, and isn't going anywhere.</p>
<p>Use AFNetworking for any of the following:</p>
<ul>
<li>UIKit extensions, such as asynchronously loading images to <code>UIImageView</code></li>
<li>TLS verification, using <code>AFSecurityManager</code></li>
<li>Situations requiring <code>NSOperation</code> or <code>NSURLConnection</code>, using <code>AFURLConnectionOperation</code></li>
<li>Network reachability monitoring, using <code>AFNetworkReachabilityManager</code></li>
<li>Multipart HTTP request construction, using <code>AFHTTPRequestSerializer</code></li>
</ul>
<h3>What's the origin of the name Alamofire?</h3>
<p>Alamofire is named after the <a href="https://aggie-horticulture.tamu.edu/wildseed/alamofire.html">Alamo Fire flower</a>, a hybrid variant of the Bluebonnet, the official state flower of Texas.</p>
<hr />
<h2>Credits</h2>
<p>Alamofire is owned and maintained by the <a href="http://alamofire.org">Alamofire Software Foundation</a>.</p>
<h2>License</h2>
<p>Alamofire is released under the MIT license. See LICENSE for details.</p>
3天200个开源项目,Swift编程语言资料大合集2015-05-06T00:00:00+00:00http://jiaxianhua.github.io/swift/2015/05/06/3-days-swift<p>在本周二凌晨召开的苹果年度开发者大会WWDC上,苹果公司推出了全新的编程语言Swift。Swift 基于C和Objective-C,是供iOS和OS X应用编程的全新语言,更加高效、现代、安全,可以提升应用性能,同时降低开发难度。</p>
<p><img src="/assets/img/ios/swift.jpg" alt="swift" /></p>
<p>据称,Swift仍然处于beta测试的阶段,会在iOS 8发布的时一同推出市场,用来取代现有的Objective-C语言。Swift推出之后,苹果公司也不会停止对Objective-C的支持,开发工具会同时支持两种语言。</p>
<p>WWDC刚刚结束,在不到24小时的时间内,已经有开发者使用 Switf 编程语言完成了一款克隆 Flappy Bird 的小游戏,并<a href="https://github.com/fullstackio/FlappySwift">开源在Github上</a>。刚刚,2048游戏也来了:<a href="https://github.com/austinzheng/swift-2048">Github地址</a>。</p>
<p>目前,这个项目已经收获了3000多个称赞和800多个派生。</p>
<p>继Flappy Bird之后,越来越多的开源应用迅速的完成了向Swift语言的转换, 这里是Github上使用Swift语言的开源项目列表,目测已经有200多个了!</p>
<p><strong>下面是我们收集的一些Swift编程语言的相关资料:</strong></p>
<ul>
<li><a href="https://itunes.apple.com/us/book/the-swift-programming-language/id881256329?mt=11">苹果官方Swift文档《The Swift Programming Language》</a></li>
<li><a href="https://developer.apple.com/swift/">苹果开发者Swift文档及介绍</a></li>
<li><a href="http://gashero.iteye.com/blog/2075324">网友整理的Swift中文文档《 Apple Swift编程语言入门教程》</a></li>
<li><a href="https://github.com/numbbbbb/the-swift-programming-language-in-chinese">中文版Apple官方Swift教程(Github协作翻译中)</a></li>
<li><a href="http://swiftlang.com.cn/start/">Apple Swift编程语言入门教程 </a></li>
<li><a href="http://www.jikexueyuan.com/course/92.html">极客学院《一小时学会Swift语言》课程</a></li>
<li><a href="http://www.cocoachina.com/newbie/basic/2014/0604/8667.html">Cocoachina翻译的Swift官方文档</a></li>
<li><a href="http://blog.jobbole.com/69948/#comment-50349">Swift语言概览中文版</a></li>
<li><a href="https://code.csdn.net/theSalt/swift">Swift英文文档markdown版</a></li>
<li><a href="http://edu.51cto.com/course/course_id-1387.html#6720061-tsina-1-89686-beb47058ad4ce5a6b43f97ee91cac13a">Swift开发视频教程【入门篇】</a></li>
<li><a href="http://letsswift.com/">letsswift 编译的Swift中文教程</a></li>
<li><a href="https://github.com/search?l=Swift&p=1&q=swift&ref=cmdform&type=Repositories">Github上的Swift开源项目列表</a></li>
<li><a href="http://swift.sh/">国内第一个Swift社区:http://swift.sh/</a></li>
</ul>
<p><strong>相关文章:</strong></p>
<ul>
<li><a href="http://www.csdn.net/article/2014-06-03/2820045-WWDC-2014-Xcode-6-Beta">Swift横空出世,Xcode 6 Beta有了哪些新变化?</a></li>
<li><a href="http://tech.sina.com.cn/it/apple/2014-06-03/15219414757.shtml">苹果编程语言Swift解析:将推动应用开发巨变</a></li>
</ul>
<blockquote><p>原文:<a href="http://code.csdn.net/news/2820075">http://code.csdn.net/news/2820075</a></p></blockquote>
浅谈 iOS 版本号2015-05-05T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/05/ios-version<p>但是在使用中我们发现好多开发者对怎么更好的用版本号来标示应用很陌生. 这是篇基础文章, 简单介绍 iOS 的版本号.</p>
<p><img src="/assets/img/ios/version-1.jpg" alt="version-1" /></p>
<h2>名词解释</h2>
<hr />
<ul>
<li>Version, 通常说的版本号, 是应用向用户宣传说明时候用到的标识. 一般有2段或者3段式, 如:<code>2.1</code>,<code>8.1.2</code></li>
</ul>
<p><code>Version</code> 一般由产品部门确定, 完全迥异的更新需要改变主版本号, 比如 <code>QQ 4.0</code> 的变化非常大, 主版本的变化会更加吸引用户的眼球,所以有的应用会频繁的更新主版本号, 比如 <code>FireFox 20.0</code>. 两段式的副版本号既包含小功能更新也会包含 bug 修复等,三段式副版本基本都是新功能添加和大问题修复,第三段则表示稳定版本基本都是修复 bug</p>
<ul>
<li>Build , 编译号指一次唯一编译标识, 通常是一个递增整数(安卓强制为数字, iOS 可以是字符串)</li>
</ul>
<p><code>Build</code> 都是给内部使用, 用来确定一个唯一版本. 与前面提到的 Version 不会有太大联系.</p>
<h3>iOS 开发中,这个2个号码都可以任意字符串或数字.</h3>
<p>我们目前遇到的情况有:</p>
<ul>
<li>忽略了 Version 或 Build. 这两个号中的一个常年的不会发生变化.</li>
<li>颠倒了 Version 和 Build.</li>
</ul>
<p><img src="/assets/img/ios/version-2.jpg" alt="version-2" /></p>
<p>获取方法也很简单:</p>
<p><figure class="highlight"><pre><code class="language-objective-c" data-lang="objective-c">NSDictionary *info= [[NSBundle mainBundle] infoDictionary];</p>
<p>info[@"CFBundleShortVersionString"]; //Version
info[@"CFBundleVersion"]; // Build</code></pre></figure></p>
<h2>为什么使用版本号</h2>
<hr />
<ul>
<li>方便标示和沟通</li>
</ul>
<p>前面提到 版本号更新会给推广产生一定的积极作用. 所以版本号不要太长, 如果像这样 "我们隆重推出了 某某某 1.7.14.19257 !", 这个会让用户感觉很乏味很像电视购物,而且也不利于传播. 如果是 "某某 3.0, 大有不同 !"可能就会产生更好的沟通效果.</p>
<ul>
<li>方便追踪 Bug</li>
</ul>
<p>一个应用有 Bug 是肯定的, 但是很快的定位解决问题却体现出团队和程序员的能力. 我们经常遇到有开发者说我提交一个版本, 但是下载下来有还是旧的. 我们帮他解决问题的时候,他自己都搞不清哪个是哪个了, 如果能在"关于"之类的地方显示当前的版本, 就会容易找到问题.</p>
<p>或者是测试团队的同事, 可能手里同时有几个不同分支的版本在测试, 他们需要精确的描述一个测试版本.</p>
<h2>自动改变 Build 号</h2>
<hr />
<p>前面提到, Version 是不需要自动变化的, 根据产品或者市场部门的需求,适时的手动改一下就好.</p>
<ul>
<li><code>agvtool (Apple-generic versioning tool)</code></li>
</ul>
<p>agvtool, 是苹果的命令行工具, 也是集成在 Xcode 中最方便的工具. 我们在自动编译 SDK 的脚本中用的就是这个方法. 其实就用了一行(其他的高级用法可以参考前面的链接):</p>
<p>agvtool next-version
使用前需要在 Xcode 里简单配置一下, 如图:</p>
<p>version-3.jpg</p>
<ul>
<li>基于SCM的版本控制号</li>
</ul>
<p>SCM 现在常用的有 Git 和 SVN, 还有一些相对小众的比如 hg 这里就不多做介绍了.
如果用 Git/SVN 来管理代码(相信已经没有人不用了) 我们可以用代码的提交次数来代替Build号.</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">Git
<span class="nv">REV</span><span class="o">=</span><span class="sb"><code></span>git rev-list HEAD | wc -l | awk <span class="s1">'{print (}'</span><span class="sb"></code></span></code></pre></figure></p>
<p>其中 <em>HEAD</em> 是分支名, 代表当前分支, 可以直接替换成其他分支名, 比如<em>master</em>, <em>dev</em>.
这个脚本放到</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">SVN
<span class="nv">REV</span><span class="o">=</span><span class="sb"><code></span>svnversion -nc | sed -e <span class="s1">'s/^[^:]*://;s/[A-Za-z]//'</span><span class="sb"></code></span></code></pre></figure></p>
<p>后面都是一样的:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">/usr/libexec/PlistBuddy -c <span class="s2">"Set :CFBundleVersion </span><span class="k">${</span><span class="nv">REV</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">TARGET_BUILD_DIR</span><span class="k">}</span><span class="s2">"</span>/<span class="k">${</span><span class="nv">INFOPLIST_PATH</span><span class="k">}</span></code></pre></figure></p>
<p>这样每次编译app的时候就自动把版本号加到Info.plist的<em>CFBundleVersion</em>键值下</p>
<p>把上面2行代码 加到 "Build Phase > Run Script"就可以了:</p>
<p><img src="/assets/img/ios/version-4.jpg" alt="version-4" /></p>
<ul>
<li>基于日期时间</li>
</ul>
<p>用发布日期作为版本好也是许多应用常用的方式, 因为好记好理解. 这里直接附上代码:</p>
<p>REV=<code>date +%y%m%d</code> #输出格式141120的六位日期格式,可以根据自己喜欢改变格式
后面都是一样的:</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">/usr/libexec/PlistBuddy -c <span class="s2">"Set :CFBundleVersion </span><span class="k">${</span><span class="nv">REV</span><span class="k">}</span><span class="s2">"</span> <span class="s2">"</span><span class="k">${</span><span class="nv">TARGET_BUILD_DIR</span><span class="k">}</span><span class="s2">"</span>/<span class="k">${</span><span class="nv">INFOPLIST_PATH</span><span class="k">}</span></code></pre></figure></p>
<p>使用方法同上.</p>
<h2>怎么使用</h2>
<hr />
<p>只要配置好了版本号, 其他的事情就不需要人工干预了, 这里介绍2种使用场景.</p>
<ul>
<li>Crash 收集</li>
</ul>
<p>收集 Crash 是应用开发必要的环节, 通过分析和修复 Crash 信息可以大大提高应用的稳定性而不会让更多的用户失望甚至删除应用.
目前有很多收集工具, 比如 FIR.im 旗下的BugHD, Crashlytics等.</p>
<p><img src="/assets/img/ios/version-5.jpg" alt="version-5" /></p>
<ul>
<li>用户反馈</li>
</ul>
<p>能主动反馈问题的用户都是极品用户, 不管要求是不是合理我们都要认真对待.
不管是用各种 SDK 还是用 Email 都要尽量的带上版本号, 系统信息, 方便确认用户需求.最不济也要在"关于"里面能让用户找到相关的版本信息以便描述问题.</p>
<blockquote><p>原文:<a href="http://segmentfault.com/a/1190000002423661">http://segmentfault.com/a/1190000002423661</a></p></blockquote>
ios size2015-05-04T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/05/04/ios-size<h2>app icon图标命名规范</h2>
<hr />
<p>ios app icon 尺寸
<img src="/assets/img/ios/app-icon.png" alt="app-icon" /></p>
<p>比如@huang这样的命名规范也能让程序与你产生好感,更为乐意地与你合作,这里就是以UCD(用户思维,程序在这里也是你的用户哦)思考的同理心啦~</p>
<h2>iPhone、iPad分辨率和显示屏规格</h2>
<hr />
<p><img src="/assets/img/ios/ios_1.png" alt="ios_1" /></p>
<h2>ICON规格</h2>
<hr />
<p><img src="/assets/img/ios/ios_2.png" alt="ios_2" /></p>
<h2>iPhone分辨率和显示屏规格</h2>
<hr />
<p><img src="/assets/img/ios/1.png" alt="1" /></p>
<h2>iPad分辨率和显示屏规格</h2>
<hr />
<p><img src="/assets/img/ios/2.png" alt="2" /></p>
<h2>iPhone6+缩减像素取样</h2>
<hr />
<p>在iOS上渲染像素和物理像素(physical pixels)是等同的,只有一个例外:iPhone 6 Plus的Retina HD显示屏。</p>
<p>由于它屏幕的像素分辨率要低于一个常规的@3x分辨率,所以被渲染内容会自动调整为原始尺寸的87%(从2208*1242像素来适应为1920*1082像素的显示屏分辨率)</p>
<p>所以我们设计6p的时候采用2208*1242像素来设计,比较好。</p>
<p><img src="/assets/img/ios/3.png" alt="3" /></p>
<h2>iPhone ICON规格</h2>
<hr />
<p><img src="/assets/img/ios/4.png" alt="4" />
<img src="/assets/img/ios/5.png" alt="5" /></p>
<h2>iPad ICON规格</h2>
<hr />
<p><img src="/assets/img/ios/6.png" alt="6" /></p>
<h2>附录:</h2>
<hr />
<ol>
<li>iPhone 5S, 6 以及6+显示屏区别的详细信息可参:The Ultimate Guide To iPhone Resolutions</li>
<li>圆角ICON:非官方的模板</li>
<li>iOS版本中默认字体:完整的预置字体列表</li>
</ol>
<hr />
<p>参考:<a href="http://ubuuk.com/ios.html">http://ubuuk.com/ios.html</a></p>
<p><a href="http://hao.xueui.cn/size/ios_size.html">http://hao.xueui.cn/size/ios_size.html</a></p>
Pharo by Example 142015-05-03T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/05/03/pharo-by-example-14<h2>Reflection</h2>
<hr />
<p>Smalltalk is a reflective programming language. In a nutshell, this means that programs are able to “reflect” on their own execution and structure. More technically, this means that the metaobjects of the runtime system can be reified as ordinary objects, which can be queried and inspected. The metaobjects in Smalltalk are classes, metaclasses, method dictionaries, compiled methods, the run-time stack, and so on. This form of reflection is also called introspection (反省), and is supported by many modern programming languages.</p>
<p><img src="/assets/img/smalltalk/Figure%2014.1.png" alt="Figure 14.1" /></p>
<p>Reification and reflection.</p>
<p>Conversely, it is possible in Smalltalk to modify reified metaobjects and <em>reflect</em> these changes back to the runtime system. This is also called <em>intercession</em>, and is supported mainly by dynamic programming languages, and only to a very limited degree by static languages.</p>
<p>A program that manipulates other programs (or even itself) is a <em>metaprogram</em>. For a programming language to be reflective, it should support both introspection and intercession. Introspection is the ability to <em>examine</em> the data structures that define the language, such as objects, classes, methods and the execution stack. Intercession is the ability to <em>modify</em> these structures, in other words to change the language semantics and the behavior of a program from within the program itself. <em>Structural reflection</em> is about examining and modifying the structures of the run-time system, and <em>behavioural reflection</em> is about modifying the interpretation of these structures.</p>
<p>In this chapter we will focus mainly on structural reflection. We will explore many practical examples illustrating how Smalltalk supports introspection and metaprogramming.</p>
<h3>Introspection</h3>
<hr />
<p>Using the inspector, you can look at an object, change the values of its instance variables, and even send messages to it.</p>
<blockquote><p>Evaluate the following code in a workspace:</p></blockquote>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">w</span> <span class="o">:=</span> <span class="nc">Workspace</span> <span class="nb">new</span><span class="p">.</span>
<span class="nv">w</span> <span class="nf">openLabel:</span> <span class="s">'My Workspace'</span><span class="p">.</span>
<span class="nv">w</span> <span class="nf">inspect</span></code></pre></figure></p>
<p>This will open a second workspace and an inspector. The inspector shows the internal state of this new workspace, listing its instance variables in the left part (dependents, contents, bindings...) and the value of the selected instance variable in the right part. The contents instance variable represents whatever the workspace is displaying in its text area, so if you select it, the right part will show an empty string.</p>
<blockquote><p>Now type 'hello' in place of that empty string, then accept it.</p></blockquote>
<p>The value of the contents variable will change, but the workspace window will not notice it, so it does not redisplay itself. To trigger the window refresh, evaluate self contentsChanged in the lower part of the inspector</p>
<h4>Accessing instance variables</h4>
<p><img src="/assets/img/smalltalk/Figure%2014.2.png" alt="Figure 14.2" /></p>
<p>Inspecting a Workspace.</p>
<p>by numeric indices. The inspector uses methods defined by the Object class to access them: instVarAt: index and instVarNamed: aString can be used to get the value of the instance variable at position index or identified by aString, respectively; to assign new values to these instance variables, it uses instVarAt:put: and instVarNamed:put:.</p>
<p>For instance, you can change the value of the w binding of the first workspace by evaluating:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">w</span> <span class="nf">instVarNamed:</span> <span class="s">'contents'</span> <span class="nf">put:</span> <span class="err">'</span><span class="nv">howdy</span><span class="err">’;</span> <span class="nv">contentsChanged</span></code></pre></figure></p>
<blockquote><p><em>Caveat</em>: Although these methods are useful for building development tools, using them to develop conventional applications is a bad idea: these reflective methods break the encapsulation boundary of your objects and can therefore make your code much harder to understand and maintain.</p></blockquote>
<p>Both instVarAt: and instVarAt:put: are primitive methods, meaning that they are implemented as primitive operations of the Pharo virtual machine. If you consult the code of these methods, you will see the special pragma syntax <primitive: N> where N is an integer.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Object</span> <span class="nf">>></span>
<span class="nf">instVarAt:</span> <span class="nv">index</span>
<span class="c">"Primitive. Answer a fixed variable in an object. ..."</span>
<span class="p"><</span><span class="k">primitive:</span> 73<span class="p">></span>
<span class="c">"Access beyond fixed variables."</span>
<span class="o">^</span> <span class="bp">self</span> <span class="nf">basicAt:</span> <span class="nv">index</span> <span class="err">−</span> <span class="bp">self</span> <span class="nf">class</span> <span class="nf">instSize</span></code></pre></figure></p>
<p>Typically, the code after the primitive invocation is not executed. It is executed only if the primitive fails. In this specific case, if we try to access a variable that does not exist, then the code following the primitive will be tried. This also allows the debugger to be started on primitive methods. Although it is possible to modify the code of primitive methods, beware that this can be risky business for the stability of your Pharo system.</p>
<p><img src="/assets/img/smalltalk/Figure%2014.3.png" alt="Figure 14.3" /></p>
<p>Displaying all instance variables of a Workspace.</p>
<p>shows how to display the values of the instance variables of an arbitrary instance (w) of class Workspace. The method allInstVarNames returns all the names of the instance variables of a given class.</p>
<p>In the same spirit, it is possible to gather instances that have specific properties. For instance, to get all instances of class SketchMorph whose instance variable owner is set to the world morph (i.e., images currently displayed), try this expression:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">SketchMorph</span> <span class="nf">allInstances</span> <span class="nf">select:</span> <span class="p">[</span><span class="o">:</span><span class="nv">c</span> <span class="p">|</span> <span class="p">(</span><span class="nv">c</span> <span class="nf">instVarNamed:</span> <span class="s">'owner'</span><span class="p">)</span> <span class="nf">isWorldMorph</span><span class="p">]</span></code></pre></figure></p>
<h4>Iterating over instance variables</h4>
<p>Let us consider the message instanceVariableValues, which returns a collection of all values of instance variables defined by this class, excluding the inherited instance variables. For instance:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="p">(</span><span class="m">1</span><span class="nf">@</span><span class="m">2</span><span class="p">)</span> <span class="nf">instanceVariableValues</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">OrderedCollection</span><span class="p">(</span><span class="m">1</span> <span class="m">2</span><span class="p">)</span></code></pre></figure></p>
<p>The method is implemented in Object as follows:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Object</span> <span class="nf">>></span>
<span class="nf">instanceVariableValues</span>
<span class="c">"Answer a collection whose elements are the values of those instance variables of the receiver which were added by the receiver's class."</span>
<span class="p">|</span><span class="nv">c</span><span class="p">|</span>
<span class="nv">c</span> <span class="o">:=</span> <span class="nc">OrderedCollection</span> <span class="nb">new</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">class</span> <span class="nf">superclass</span> <span class="nf">instSize</span> <span class="nf">+</span> <span class="m">1</span>
<span class="nf">to:</span> <span class="bp">self</span> <span class="nf">class</span> <span class="nf">instSize</span>
<span class="nf">do:</span> <span class="p">[</span> <span class="o">:</span><span class="nv">i</span> <span class="p">|</span> <span class="nv">c</span> <span class="nf">add:</span> <span class="p">(</span><span class="bp">self</span> <span class="nf">instVarAt:</span> <span class="nv">i</span><span class="p">)].</span>
<span class="o">^</span> <span class="nv">c</span></code></pre></figure></p>
<p>This method iterates over the indices of instance variables that the class defines, starting just after the last index used by the superclasses. (The method instSize returns the number of all named instance variables that a class defines.)</p>
<h4>Querying classes and interfaces</h4>
<p>The development tools in Pharo (code browser, debugger, inspector...) all use the reflective features we have seen so far.</p>
<p>Here are a few other messages that might be useful to build development tools:</p>
<p><em>isKindOf</em>: aClass returns true if the receiver is instance of aClass or of one of its superclasses. For instance:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="m">1.5</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">Float</span>
<span class="m">1.5</span> <span class="nf">isKindOf:</span> <span class="nc">Number</span> <span class="nf">---></span> <span class="bp">true</span>
<span class="m">1.5</span> <span class="nf">isKindOf:</span> <span class="nc">Integer</span> <span class="nf">---></span> <span class="bp">false</span></code></pre></figure></p>
<p><em>respondsTo</em>: aSymbol returns true if the receiver has a method whose selector is aSymbol. For instance:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="m">1.5</span> <span class="nf">respondsTo:</span> <span class="ss">#floor</span> <span class="nf">---></span> <span class="bp">true</span> <span class="c">"since Number implements floor"</span>
<span class="m">1.5</span> <span class="nf">floor</span> <span class="nf">---></span> <span class="m">1</span>
<span class="nf">Exception</span> <span class="nf">respondsTo:</span> <span class="ss">#,</span> <span class="nf">---></span> <span class="bp">true</span> <span class="c">"exception classes can be grouped"</span></code></pre></figure></p>
<blockquote><p>Caveat: Although these features are especially useful for defining development tools, they are normally not appropriate for typical applications. Asking an object for its class, or querying it to discover which messages it understands, are typical signs of design problems, since they violate the principle of encapsulation. Development tools, however, are not normal applications, since their domain is that of software itself. As such these tools have a right to dig deep into the internal details of code.</p></blockquote>
<h4>Code metrics</h4>
<p>Let’s see how we can use Smalltalk’s introspection features to quickly extract some code metrics. Code metrics measure such aspects as the depth of the inheritance hierarchy, the number of direct or indirect subclasses, the number of methods or of instance variables in each class, or the number of locally defined methods or instance variables. Here are a few metrics for the class Morph, which is the superclass of all graphical objects in Pharo, revealing that it is a huge class, and that it is at the root of a huge hierarchy. Maybe it needs some refactoring!</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Morph</span> <span class="nf">allSuperclasses</span> <span class="nf">size</span><span class="p">.</span> <span class="err">---></span> <span class="m">2</span> <span class="c">"inheritance depth"</span>
<span class="nf">Morph</span> <span class="nf">allSelectors</span> <span class="nf">size</span><span class="p">.</span> <span class="err">---></span> <span class="m">1378</span> <span class="c">"number of methods"</span>
<span class="nf">Morph</span> <span class="nf">allInstVarNames</span> <span class="nf">size</span><span class="p">.</span> <span class="err">---></span> <span class="m">6</span> <span class="c">"number of instance variables"</span>
<span class="nf">Morph</span> <span class="nf">selectors</span> <span class="nf">size</span><span class="p">.</span> <span class="err">---></span> <span class="m">998</span> <span class="c">"number of new methods"</span>
<span class="nf">Morph</span> <span class="nf">instVarNames</span> <span class="nf">size</span><span class="p">.</span> <span class="err">---></span> <span class="m">6</span> <span class="c">"number of new variables"</span>
<span class="nf">Morph</span> <span class="nf">subclasses</span> <span class="nf">size</span><span class="p">.</span> <span class="err">---></span> <span class="m">45</span> <span class="c">"direct subclasses"</span>
<span class="nf">Morph</span> <span class="nf">allSubclasses</span> <span class="nf">size</span><span class="p">.</span> <span class="err">---></span> <span class="m">326</span> <span class="c">"total subclasses"</span>
<span class="nf">Morph</span> <span class="nf">linesOfCode</span><span class="p">.</span> <span class="err">---></span> <span class="m">5968</span> <span class="c">"total lines of code!"</span></code></pre></figure></p>
<p>One of the most interesting metrics in the domain of object-oriented languages is the number of methods that extend methods inherited from the superclass. This informs us about the relation between the class and its superclasses. In the next sections we will see how to exploit our knowledge of the runtime structure to answer such questions.</p>
<h3>Browsing code</h3>
<hr />
<p>In Smalltalk, everything is an object. In particular, classes are objects that provide useful features for navigating through their instances. Most of the messages we will look at now are implemented in Behavior, so they are under- stood by all classes.</p>
<p>As we saw previously, you can obtain an instance of a given class by sending it the message #someInstance.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Point</span> <span class="nf">someInstance</span> <span class="nf">---></span> <span class="m">0</span><span class="nf">@</span><span class="m">0</span></code></pre></figure></p>
<p>You can also gather all the instances with #allInstances, or the number of alive instances in memory with #instanceCount.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">ByteString</span> <span class="nf">allInstances</span> <span class="nf">---></span> <span class="ss">#(</span><span class="s">'collection'</span> <span class="s">'position'</span> <span class="p">.</span><span class="err">..</span><span class="ss">)</span>
<span class="nf">ByteString</span> <span class="nf">instanceCount</span> <span class="nf">---></span> <span class="m">104565</span>
<span class="nf">String</span> <span class="nf">allSubInstances</span> <span class="nf">size</span> <span class="nf">---></span> <span class="m">101675</span></code></pre></figure></p>
<p>These features can be very useful when debugging an application, because you can ask a class to enumerate those of its methods exhibiting specific properties.</p>
<ul>
<li>whichSelectorsAccess: returns the list of all selectors of methods that read or write the instance variable named by the argument</li>
<li>whichSelectorsStoreInto: returns the selectors of methods that modify the value of an instance variable</li>
<li>whichSelectorsReferTo: returns the selectors of methods that send a given message</li>
<li>crossReferenceassociateseachmessagewiththesetofmethodsthatsend it.</li>
</ul>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Point</span> <span class="nf">whichSelectorsAccess:</span> <span class="s">'x'</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">IdentitySet</span><span class="p">(</span><span class="err">#</span><span class="s">'\'</span> <span class="ss">#=</span> <span class="ss">#scaleBy:</span> <span class="p">.</span><span class="err">..</span><span class="p">)</span>
<span class="nf">Point</span> <span class="nf">whichSelectorsStoreInto:</span> <span class="s">'x'</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">IdentitySet</span><span class="p">(</span><span class="ss">#setX:setY:</span> <span class="p">.</span><span class="err">..</span><span class="p">)</span>
<span class="nf">Point</span> <span class="nf">whichSelectorsReferTo:</span> <span class="ss">#+</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">IdentitySet</span><span class="p">(</span><span class="ss">#rotateBy:about:</span> <span class="p">.</span><span class="err">..</span><span class="p">)</span>
<span class="nf">Point</span> <span class="nf">crossReference</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">Array</span><span class="p">(</span>
<span class="nv">an</span> <span class="nf">Array</span><span class="p">(</span><span class="s">'*'</span> <span class="nf">an</span> <span class="nf">IdentitySet</span><span class="p">(</span><span class="ss">#rotateBy:about:</span> <span class="p">.</span><span class="err">..</span><span class="p">))</span>
<span class="nf">an</span> <span class="nf">Array</span><span class="p">(</span><span class="s">'+'</span> <span class="nf">an</span> <span class="nf">IdentitySet</span><span class="p">(</span><span class="ss">#rotateBy:about:</span> <span class="p">.</span><span class="err">..</span><span class="p">))</span> <span class="p">.</span><span class="err">..</span><span class="p">)</span></code></pre></figure></p>
<p>The following messages take inheritance into account:</p>
<ul>
<li>whichClassIncludesSelector: returns the superclass that implements the given message</li>
<li>unreferencedInstanceVariables returns the list of instance variables that are neither used in the receiver class nor any of its subclasses</li>
</ul>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Rectangle</span> <span class="nf">whichClassIncludesSelector:</span> <span class="ss">#inspect</span> <span class="nf">---></span> <span class="nc">Object</span> <span class="nf">Rectangle</span> <span class="nf">unreferencedInstanceVariables</span> <span class="nf">---></span> <span class="ss">#()</span></code></pre></figure></p>
<p>SystemNavigation is a facade that supports various useful methods for query- ing and browsing the source code of the system. SystemNavigation default re- turns an instance you can use to navigate the system. For example:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">SystemNavigation</span> <span class="nf">default</span> <span class="nf">allClassesImplementing:</span> <span class="ss">#yourself</span> <span class="nf">---></span> <span class="p">{</span><span class="nc">Object</span><span class="p">}</span></code></pre></figure></p>
<p>The following messages should also be self-explanatory:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">SystemNavigation</span> <span class="nf">default</span> <span class="nf">allSentMessages</span> <span class="nf">size</span> <span class="nf">---></span> <span class="m">24930</span>
<span class="nf">SystemNavigation</span> <span class="nf">default</span> <span class="nf">allUnsentMessages</span> <span class="nf">size</span> <span class="nf">---></span> <span class="m">6431</span>
<span class="nf">SystemNavigation</span> <span class="nf">default</span> <span class="nf">allUnimplementedCalls</span> <span class="nf">size</span> <span class="nf">---></span> <span class="m">270</span></code></pre></figure></p>
<p>Note that messages implemented but not sent are not necessarily useless, since they may be sent implicitly (e.g., using perform:). Messages sent but not implemented, however, are more problematic, because the methods sending these messages will fail at runtime. They may be a sign of unfinished implementation, obsolete APIs, or missing libraries.</p>
<p>SystemNavigation default allCallsOn: #Point returns all messages sent explicitly to Point as a receiver.</p>
<p>All these features are integrated in the programming environment of Pharo, in particular in the code browsers. As you are surely already aware, there are convenient keyboard shortcuts for browsing all implementors (CMD–m) and senders (CMD–n) of a given message. What is perhaps not so well known is that there are many such pre-packaged queries implemented as methods of the SystemNavigation class in the browsing protocol. For example, you can programmatically browse all implementors of the message ifTrue: by evaluating:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">SystemNavigation</span> <span class="nf">default</span> <span class="nf">browseAllImplementorsOf:</span> <span class="ss">#ifTrue:</span></code></pre></figure></p>
<p><img src="/assets/img/smalltalk/Figure%2014.4.png" alt="Figure 14.4" /></p>
<p>Browse all implementations of #ifTrue:.</p>
<p>Particularly useful are the methods browseAllSelect: and browseMethodsWith- SourceString:. Here are two different ways to browse all methods in the system that perform super sends (the first way is rather brute force; the second way is better and eliminates some false positives):
</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">SystemNavigation</span> <span class="nf">default</span> <span class="nf">browseMethodsWithSourceString:</span> <span class="s">'super'</span><span class="p">.</span> <span class="nc">SystemNavigation</span> <span class="nf">default</span> <span class="nf">browseAllSelect:</span> <span class="p">[</span><span class="o">:</span><span class="nv">method</span> <span class="p">|</span> <span class="nv">method</span> <span class="nf">sendsToSuper</span> <span class="p">].</span></code></pre></figure></p>
<p></p>
<h3>Classes, method dictionaries and methods</h3>
<hr />
<p>Since classes are objects, we can inspect or explore them just like any other object.</p>
<blockquote><p>Evaluate Point explore.</p></blockquote>
<p>the explorer shows the structure of class Point. You can see that the class stores its methods in a dictionary, indexing them by their selector. The selector #* points to the decompiled bytecode of Point >> *.</p>
<p><img src="/assets/img/smalltalk/Figure%2014.5.png" alt="Figure 14.5" /></p>
<p>Explorer class Point and the bytecode of its #* method.</p>
<p><img src="/assets/img/smalltalk/Figure%2014.6.png" alt="Figure 14.6" /></p>
<p>Classes, method dictionaries and compiled methods</p>
<h3>Browsing environments</h3>
<hr />
<p>Although SystemNavigation offers some useful ways to programmatically query and browse system code, there is a better way. The Refactoring Browser, which is integrated into Pharo, provides both interactive and programmatic ways to pose complex queries.</p>
<p>Suppose we are interested to discover which methods in the Collection hier- archy send a message to super which is different from the method’s selector. This is normally considered to be a bad code smell, since such a super-send should normally be replaced by a self-send. (Think about it — you only need super to extend a method you are overriding; all other inherited methods can be accessed by sending to self!)</p>
<p>The refactoring browser provides us with an elegant way to restrict our query to just the classes and methods we are interested in.</p>
<blockquote><p>Open a browser on the class Collection. action-click on the class name and select refactoring <em>scope>subclasses with</em>. This will open a new Browser Environment on just the Collection hierarchy. Within this re- stricted scope select <em>refactoringscope>super-sends</em> to open a new environ- ment with all methods that perform super-sends within the Collectuon hierar- chy. Now click on any method and select <em>refactor>code critics</em> . Navigate to <em>Lint checks>Possible bugs>Sends different super message</em>. and action-click to select <em>browse</em>.</p></blockquote>
<p><img src="/assets/img/smalltalk/Figure%2014.7.png" alt="Figure 14.7" /></p>
<p>Finding methods that send a different super message.</p>
<h3>Accessing the run-time context</h3>
<hr />
<p>We have seen how Smalltalk’s reflective capabilities let us query and explore objects, classes and methods. But what about the run-time environment?</p>
<h4>Method contexts</h4>
<p>In fact, the run-time context of an executing method is in the virtual machine — it is not in the image at all! On the other hand, the debugger obviously has access to this information, and we can happily explore the run-time context, just like any other object. How is this possible?</p>
<p>Actually, there is nothing magical about the debugger. The secret is the pseudo-variable thisContext, which we have encountered only in passing before. Whenever thisContext is referred to in a running method, the entire run-time context of that method is reified and made available to the image as a series of chained MethodContext objects.</p>
<p>We can easily experiment with this mechanism ourselves.</p>
<p><img src="/assets/img/smalltalk/Figure%2014.8.png" alt="Figure 14.8" /></p>
<p>Exploring thisContext.</p>
<h3>Intercepting messages not understood</h3>
<hr />
<p>So far we have used the reflective features of Smalltalk mainly to query and explore objects, classes, methods and the run-time stack. Now we will look at how to use our knowledge of the Smalltalk system structure to intercept messages and modify behaviour at run-time.</p>
<p>When an object receives a message, it first looks in the method dictionary of its class for a corresponding method to respond to the message. If no such method exists, it will continue looking up the class hierarchy, until it reaches Object. If still no method is found for that message, the object will send itself the message doesNotUnderstand: with the message selector as its argument. The process then starts all over again, until Object»doesNotUnderstand: is found, and the debugger is launched.</p>
<p>But what if doesNotUnderstand: is overridden by one of the subclasses of Object in the lookup path? As it turns out, this is a convenient way of realizing certain kinds of very dynamic behaviour. An object that does not understand a message can, by overriding doesNotUnderstand:, fall back to an alternative strategy for responding to that message.</p>
<p>Two very common applications of this technique are (1) to implement lightweight proxies for objects, and (2) to dynamically compile or load missing code.</p>
<h4>Lightweight proxies</h4>
<p>In the first case, we introduce a “minimal object” to act as a proxy for an existing object. Since the proxy will implement virtually no methods of its own, any message sent to it will be trapped by doesNotUnderstand:. By implementing this message, the proxy can then take special action before delegating the message to the real subject it is the proxy for.</p>
<h4>Generating missing methods</h4>
<p>The other most common application of intercepting not understood messages is to dynamically load or generate the missing methods. Consider a very large library of classes with many methods. Instead of loading the entire library, we could load a stub for each class in the library. The stubs know where to find the source code of all their methods. The stubs simply trap all messages not understood, and dynamically load the missing methods on-demand. At some point, this behaviour can be deactivated, and the loaded code can be saved as the minimal necessary subset for the client application.</p>
<p><img src="/assets/img/smalltalk/Figure%2014.9.png" alt="Figure 14.9" /></p>
<p>Dynamically creating accessors.</p>
<h3>Objects as method wrappers</h3>
<hr />
<p>We have already seen that compiled methods are ordinary objects in Smalltalk, and they support a number of methods that allow the programmer to query the run-time system. What is perhaps a bit more surprising, is that any object can play the role of a compiled method. All it has to do is respond to the method run:with:in: and a few other important messages.</p>
<blockquote><p>Define an empty class Demo. Evaluate Demo new answer42 and notice how the usual “Message Not Understood” error is raised.</p></blockquote>
<p>Now we will install a plain Smalltalk object in the method dictionary of our Demo class.</p>
<h4>Using methods wrappers to perform test coverage</h4>
<p>Method wrappers are a well-known technique for intercepting messages3. In the original implementation4, a method wrapper is an instance of a subclass of CompiledMethod. When installed, a method wrapper can perform special actions before or after invoking the original method. When uninstalled, the original method is returned to its rightful position in the method dictionary.</p>
<h3>Pragmas</h3>
<hr />
<p>A pragma is an annotation that specifies data about a program, but is not involved in the execution of the program. Pragmas have no direct effect on the operation of the method they annotate. Pragmas have a number of uses, among them:</p>
<ul>
<li>Information for the compiler: pragmas can be used by the compiler to make a method call a primitive function. This function has to be defined by the virtual machine or by an external plugging.</li>
<li>Runtime processing: Some pragmas are available to be examined at runtime.</li>
</ul>
<h2>Chapter summary</h2>
<hr />
<p>Reflection refers to the ability to query, examine and even modify the metaob- jects of the run-time system as ordinary objects.</p>
<ul>
<li>The Inspector uses instVarAt: and related methods to query and modify “private” instance variables of objects.</li>
<li>Send Behavior»allInstances to query instances of a class.</li>
<li>The messages class, isKindOf:, respondsTo: etc. are useful for gathering metrics or building development tools, but they should be avoided in regular applications: they violate the encapsulation of objects and make your code harder to understand and maintain.</li>
<li>SystemNavigation is a utility class holding many useful queries for navi- gation and browsing the lass hierarhy. For example, use SystemNavigation default browseMethodsWithSourceString: 'pharo'. to find and browse all meth- ods with a given source string. (Slow, but thorough!)</li>
<li>Every Smalltalk class points to an instance of MethodDictionary which maps selectors to instances of CompiledMethod. A compiled method knows its class, closing the loop.</li>
<li>MethodReference is a leightweight proxy for a compiled method, pro- viding additional convenience methods, and used by many Smalltalk tools.</li>
<li>BrowserEnvironment, part of the Refactoring Browser infrastructure, offers a more refined interface than SystemNavigation for querying the system, since the result of a query can be used as a the scope of a new query. Both GUI and programmatic interfaces are available.</li>
<li>thisContext is a pseudo-variable that reifies the run-time stack of the virtual machine. It is mainly used by the debugger to dynamically construct an interactive view of the stack. It is also especially useful for dynamically determining the sender of a message.</li>
<li>Intelligent breakpoints can be set using haltIf:, taking a method selector as its argument. haltIf: halts only if the named method occurs as a sender in the run-time stack.</li>
<li>A common way to intercept messages sent to a given target is to use a “minimal object” as a proxy for that target. The proxy implements as few methods as possible, and traps all message sends by implementing doesNotunderstand:. It can then perform some additional action and then
forward the message to the original target.</li>
<li>Send become: to swap the references of two objects, such as a proxy and its target.</li>
<li>Beware, some messages, like class and yourself are never really sent, but are interpreted by the VM. Others, like +, − and ifTrue: may be directly interpreted or inlined by the VM depending on the receiver.</li>
<li>Another typical use for overriding doesNotUnderstand: is to lazily load or compile missing methods.</li>
<li>doesNotUnderstand: cannot trap self-sends.</li>
<li>A more rigorous way to intercept messages is to use an object as a method wrapper. Such an object is installed in a method dictionary in place of a compiled method. It should implement run:with:in: which is sent by the VM when it detects an ordinary object instead of a compiled method in the method dictionary. This technique is used by the SUnit Test Runner to collect coverage data.</li>
</ul>
Pharo by Example 132015-05-03T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/05/03/pharo-by-example-13<h2>Classes and metaclasses</h2>
<hr />
<ol>
<li>Rule 1. Everything is an object.</li>
<li>Rule 2. Every object is an instance of a class.</li>
<li>Rule 3. Every class has a superclass.</li>
<li>Rule 4. Everything happens by sending messages.</li>
<li>Rule 5. Method lookup follows the inheritance chain.</li>
<li>Rule 6. Every class is an instance of a metaclass.</li>
<li>Rule 7. The metaclass hierarchy parallels the class hierarchy.</li>
<li>Rule 8. Every metaclass inherits from Class and Behavior.</li>
<li>Rule 9. Every metaclass is an instance of Metaclass.</li>
<li>Rule 10. The metaclass of Metaclass is an instance of Metaclass.</li>
</ol>
<h3>Revisiting the Smalltalk object model</h3>
<hr />
<p>Since everything is an object, the color blue in Smalltalk is also an object.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Color</span> <span class="nf">blue</span> <span class="nf">---></span> <span class="nc">Color</span> <span class="nf">blue</span></code></pre></figure></p>
<p>Every object is an instance of a class. The class of the color blue is the class Color:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Color</span> <span class="nf">blue</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">Color</span></code></pre></figure></p>
<p>Interestingly, if we set the alpha value of a color, we get an instance of a different class, namely TranslucentColor:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="p">(</span><span class="nc">Color</span> <span class="nf">blue</span> <span class="nf">alpha:</span> <span class="m">0.4</span><span class="p">)</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">TranslucentColor</span></code></pre></figure></p>
<p>We can create a morph and set its color to this translucent color:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">EllipseMorph</span> <span class="nb">new</span> <span class="nf">color:</span> <span class="p">(</span><span class="nc">Color</span> <span class="nf">blue</span> <span class="nf">alpha:</span> <span class="m">0.4</span><span class="p">);</span> <span class="nf">openInWorld</span></code></pre></figure></p>
<p><img src="/assets/img/smalltalk/Figure%2013.1.png" alt="Figure 13.1" /></p>
<p>A translucent ellipse</p>
<p>By Rule 3, every class has a superclass. The superclass of TranslucentColor
is Color, and the superclass of Color is Object:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">TranslucentColor</span> <span class="nf">superclass</span> <span class="nf">---></span> <span class="nc">Color</span>
<span class="nf">Color</span> <span class="nf">superclass</span> <span class="nf">---></span> <span class="nc">Object</span></code></pre></figure></p>
<p>Everything happens by sending messagess (Rule 4), so we can deduce that blue is a message to Color, class and alpha: are messages to the color blue, openInWorld is a message to an ellipse morph, and superclass is a message to TranslucentColor and Color. The receiver in each case is an object, since everything is an object, but some of these objects are also classes.</p>
<p>Method lookup follows the inheritance chain (Rule 5), so when we send the message class to the result of Color blue alpha: 0.4, the message is handled when the corresponding method is found in the class Object.</p>
<p><img src="/assets/img/smalltalk/Figure%2013.2.png" alt="Figure 13.2" /></p>
<p>Sending a message to a translucent color</p>
<p>The figure captures the essence of the is-a relationship. Our translucent blue object is a TranslucentColor instance, but we can also say that it is a Color and that it is an Object, since it responds to the messages defined in all of these classes. In fact, there is a message, isKindOf:, that you can send to any object to find out if it is in an is a relationship with a given class:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">translucentBlue</span> <span class="o">:=</span> <span class="nc">Color</span> <span class="nf">blue</span> <span class="nf">alpha:</span> <span class="m">0.4</span><span class="p">.</span>
<span class="nv">translucentBlue</span> <span class="nf">isKindOf:</span> <span class="nc">TranslucentColor</span> <span class="nf">---></span> <span class="bp">true</span>
<span class="nf">translucentBlue</span> <span class="nf">isKindOf:</span> <span class="nc">Color</span> <span class="nf">---></span> <span class="bp">true</span>
<span class="nf">translucentBlue</span> <span class="nf">isKindOf:</span> <span class="nc">Object</span> <span class="nf">---></span> <span class="bp">true</span></code></pre></figure></p>
<h3>Every class is an instance of a metaclass</h3>
<hr />
<p>classes whose instances are themselves classes are called metaclasses.</p>
<p><strong>Metaclasses are implicit</strong>. Metaclasses are automatically created when you define a class. We say that they are implicit since as a programmer you never have to worry about them. An implicit metaclass is created for each class you create, so each metaclass has only a single instance.</p>
<p>Whereas ordinary classes are named by global variables, metaclasses are anonymous. However, we can always refer to them through the class that is their instance. The class of Color, for instance, is Color class, and the class of Object is Object class:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Color</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">Color</span> <span class="nf">class</span>
<span class="nf">Object</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">Object</span> <span class="nf">class</span></code></pre></figure></p>
<p><img src="/assets/img/smalltalk/Figure%2013.3.png" alt="Figure 13.3" /></p>
<p>The metaclasses of Translucent and its superclasses</p>
<p>The fact that classes are also objects makes it easy for us to query them by sending messages. Let’s have a look:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Color</span> <span class="nf">subclasses</span> <span class="nf">---></span> <span class="p">{</span><span class="nc">TranslucentColor</span><span class="p">}</span>
<span class="nf">TranslucentColor</span> <span class="nf">subclasses</span> <span class="nf">---></span> <span class="ss">#()</span>
<span class="nf">TranslucentColor</span> <span class="nf">allSuperclasses</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">OrderedCollection</span><span class="p">(</span><span class="nc">Color</span> <span class="nf">Object</span> <span class="nf">ProtoObject</span><span class="p">)</span>
<span class="nf">TranslucentColor</span> <span class="nf">instVarNames</span> <span class="nf">---></span> <span class="ss">#(</span><span class="s">'alpha'</span><span class="ss">)</span>
<span class="nf">TranslucentColor</span> <span class="nf">allInstVarNames</span> <span class="nf">---></span> <span class="ss">#(</span><span class="s">'rgb'</span> <span class="s">'cachedDepth'</span> <span class="s">'cachedBitPattern'</span> <span class="s">' alpha'</span><span class="ss">)</span>
<span class="nf">TranslucentColor</span> <span class="nf">selectors</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">IdentitySet</span><span class="p">(</span><span class="ss">#pixelValueForDepth:</span> <span class="ss">#pixelWord32</span> <span class="ss">#convertToCurrentVersion:refStream:</span> <span class="ss">#isTransparent</span> <span class="ss">#scaledPixelValue32</span> <span class="ss">#bitPatternForDepth:</span> <span class="ss">#storeArrayValuesOn:</span> <span class="ss">#setRgb:alpha:</span> <span class="ss">#alpha</span> <span class="ss">#isOpaque</span> <span class="ss">#pixelWordForDepth:</span> <span class="ss">#isTranslucentColor</span> <span class="ss">#hash</span> <span class="ss">#isTranslucent</span> <span class="ss">#alpha:</span> <span class="ss">#storeOn:</span> <span class="ss">#asNontranslucentColor</span> <span class="ss">#privateAlpha</span> <span class="ss">#balancedPatternForDepth:</span><span class="p">)</span></code></pre></figure></p>
<h3>The metaclass hierarchy parallels the class hierarchy</h3>
<hr />
<p>Rule 7 says that the superclass of a metaclass cannot be an arbitrary class: it is constrained to be the metaclass of the superclass of the metaclass’s unique instance.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">TranslucentColor</span> <span class="nf">class</span> <span class="nf">superclass</span> <span class="nf">---></span> <span class="nc">Color</span> <span class="nf">class</span> <span class="nf">TranslucentColor</span>
<span class="nf">superclass</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">Color</span> <span class="nf">class</span></code></pre></figure></p>
<p>This is what we mean by the metaclass hierarchy being parallel to the class hierarchy; Figure 13.4 shows how this works in the TranslucentColor hierarchy.</p>
<p><img src="/assets/img/smalltalk/Figure%2013.4.png" alt="Figure 13.4" /></p>
<p>The metaclass hierarchy parallels the class hierarchy.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">TranslucentColor</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">TranslucentColor</span> <span class="nf">class</span>
<span class="nf">TranslucentColor</span> <span class="nf">class</span> <span class="nf">superclass</span> <span class="nf">---></span> <span class="nc">Color</span> <span class="nf">class</span>
<span class="nf">TranslucentColor</span> <span class="nf">class</span> <span class="nf">superclass</span> <span class="nf">superclass</span> <span class="nf">---></span> <span class="nc">Object</span> <span class="nf">class</span></code></pre></figure></p>
<p><strong>Uniformity between Classes and Objects.</strong>
It is interesting to step back a moment and realize that there is no difference between sending a message to an object and to a class. In both cases the search for the corresponding method starts in the class of the receiver, and proceeds up the inheritance chain.</p>
<p>Thus, messages sent to classes must follow the metaclass inheritance chain. Consider, for example, the method blue, which is implemented on the class side of Color. If we send the message blue to TranslucentColor, then it will be looked-up the same way as any other message. The lookup starts in TranslucentColor class, and proceeds up the metaclass hierarchy until it is found in Color class (see Figure 13.5).</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">TranslucentColor</span> <span class="nf">blue</span> <span class="nf">---></span> <span class="nc">Color</span> <span class="nf">blue</span></code></pre></figure></p>
<p>Note that we get as a result an ordinary Color blue, and not a translucent one — there is no magic!</p>
<p><img src="/assets/img/smalltalk/Figure%2013.5.png" alt="Figure 13.5" /></p>
<p>Message lookup for classes is the same as for ordinary objects.</p>
<p>Thus we see that there is one uniform kind of method lookup in Smalltalk. Classes are just objects, and behave like any other objects. Classes have the power to create new instances only because classes happen to respond to the message new, and because the method for new knows how to create new instances. Normally, non-class objects do not understand this message, but if you have a good reason to do so, there is nothing stopping you from adding a new method to a non-metaclass.</p>
<p>Since classes are objects, we can also inspect them.</p>
<blockquote><p>Inspect Color blue and Color.</p></blockquote>
<p>Notice that in one case you are inspecting an instance of Color and in the other case the Color class itself. This can be a bit confusing, because the title bar of the inspector names the class of the object being inspected.</p>
<p>The inspector on Color allows you to see the superclass, instance variables, method dictionary, and so on, of the Color class.</p>
<p><img src="/assets/img/smalltalk/Figure%2013.6.png" alt="Figure 13.6" /></p>
<p>Classes are objects too.</p>
<h3>Every metaclass Inherits from Class and Behavior</h3>
<hr />
<p>Every metaclass is-a class, hence inherits from Class. Class in turn inherits from its superclasses, ClassDescription and Behavior. Since everything in Smalltalk is-an object, these classes all inherit eventually from Object.</p>
<p><strong>Where is new defined?</strong> To understand the importance of the fact that metaclasses inherit from Class and Behavior, it helps to ask where new is defined and how it is found. When the message new is sent to a class it is looked up in its metaclass chain and ultimately in its superclasses Class, ClassDescription and Behavior.</p>
<p><img src="/assets/img/smalltalk/Figure%2013.7.png" alt="Figure 13.7" /></p>
<p>Metaclasses inherit from Class and Behavior</p>
<p>The question <em>“Where new is defined?”</em> is crucial. <strong>new</strong> is first defined in the class <strong>Behavior</strong>, and it can be redefined in its subclasses, including any of the metaclass of the classes we define, when this is necessary. Now when a message new is sent to a class it is looked up, as usual, in the metaclass of this class, continuing up the superclass chain right up to the class Behavior, if it has not been redefined along the way.</p>
<p>Note that the result of sending TranslucentColor new is an instance of TranslucentColor and not of Behavior, even though the method is looked-up in the class Behavior! new always returns an instance of self, the class that receives the message, even if it is implemented in another class.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">TranslucentColor</span> <span class="nb">new</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">TranslucentColor</span> <span class="c">"not Behavior"</span></code></pre></figure></p>
<p>A common mistake is to look for new in the superclass of the receiving class. The same holds for new:, the standard message to create an object of a given size. For example, <strong>Array new: 4</strong> creates an array of 4 elements. You will not find this method defined in Array or any of its superclasses. Instead you should look in Array class and its superclasses, since that is where the lookup will start.</p>
<p><img src="/assets/img/smalltalk/Figure%2013.8.png" alt="Figure 13.8" /></p>
<p>new is an ordinary message looked up in the metaclass chain.</p>
<p><strong>Responsibilities of Behavior, ClassDescription and Class.</strong> Behavior provides the minimum state necessary for objects that have instances: this includes a superclass link, a method dictionary, and a description of the instances (i.e., representation and number). Behavior inherits from Object,so it,and all of its subclasses, can behave like objects.</p>
<p>Behavior is also the basic interface to the compiler. It provides methods for creating a method dictionary, compiling methods, creating instances (i.e., new, basicNew, new:, and basicNew:), manipulating the class hierarchy (i.e., superclass:, addSubclass:), accessing methods (i.e., selectors, allSelectors, compiledMethodAt:), accessing instances and variables (i.e., allInstances, instVarNames . . . ), accessing the class hierarchy (i.e., superclass, subclasses) and querying (i.e., hasMethods, includesSelector, canUnderstand:, inheritsFrom:, isVariable).</p>
<p>ClassDescription is an abstract class that provides facilities needed by its two direct subclasses, Class and Metaclass. ClassDescription adds a number of facilities to the basis provided by Behavior: named instance variables, the categorization of methods into protocols, the notion of a name (abstract), the maintenance of change sets and the logging of changes, and most of the mechanisms needed for filing-out changes.</p>
<p>Class represents the common behaviour of all classes. It provides a class name, compilation methods, method storage, and instance variables. It provides a concrete representation for class variable names and shared pool variables (addClassVarName:, addSharedPool:, initialize). Class knows how to create instances, so all metaclasses should inherit ultimately from Class.</p>
<p><img src="/assets/img/smalltalk/Figure%2013.9.png" alt="Figure 13.9" /></p>
<p>Every metaclass is a Metaclass.</p>
<h3>Every metaclass is an instance of Metaclass</h3>
<hr />
<p>Metaclasses are objects too; they are instances of the class Metaclass. The instances of class Metaclass are the anonymous metaclasses, each of which has exactly one instance, which is a class.</p>
<p>Metaclass represents common metaclass behaviour. It provides methods for instance creation (subclassOf:) creating initialized instances of the metaclass’s sole instance, initialization of class variables, metaclass instance, method compilation, and class information (inheritance links, instance variables, etc.).</p>
<h3>The metaclass of Metaclass is an Instance of Metaclass</h3>
<hr />
<p>The final question to be answered is: <em>what is the class of Metaclass class?</em></p>
<p>The answer is simple: it is a metaclass, so it must be an instance of Metaclass, just like all the other metaclasses in the system.</p>
<p>The figure shows how all metaclasses are instances of Metaclass, including the metaclass of Metaclass itself.</p>
<p><img src="/assets/img/smalltalk/Figure%2013.10.png" alt="Figure 13.10" /></p>
<p>All metaclasses are instances of the class Metaclass, even the metaclass of Metaclass.</p>
<p>will see how the metaclass hierarchy perfectly mirrors the class hierarchy, all the way up to Object class.</p>
<p>The following examples show us how we can query the class hierarchy to demonstrate that Figure 13.10 is correct. (Actually, you will see that we told a white lie — Object class superclass ≠æ ProtoObject class, not Class. In Pharo, we must go one superclass higher to reach Class.)</p>
<p>Example 13.1: The class hierarchy TranslucentColor superclass ≠æ Color</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Color</span> <span class="nf">superclass</span> <span class="nf">---></span> <span class="nc">Object</span></code></pre></figure></p>
<p>Example 13.2: The parallel metaclass hierarchy</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">TranslucentColor</span> <span class="nf">class</span> <span class="nf">superclass</span> <span class="nf">---></span> <span class="nc">Color</span> <span class="nf">class</span>
<span class="nf">Color</span> <span class="nf">class</span> <span class="nf">superclass</span> <span class="nf">---></span> <span class="nc">Object</span> <span class="nf">class</span>
<span class="nf">Object</span> <span class="nf">class</span> <span class="nf">superclass</span> <span class="nf">superclass</span> <span class="nf">---></span> <span class="nc">Class</span> <span class="c">"NB: skip ProtoObject class"</span>
<span class="nf">Class</span> <span class="nf">superclass</span> <span class="nf">---></span> <span class="nc">ClassDescription</span>
<span class="nf">ClassDescription</span> <span class="nf">superclass</span> <span class="nf">---></span> <span class="nc">Behavior</span>
<span class="nf">Behavior</span> <span class="nf">superclass</span> <span class="nf">---></span> <span class="nc">Object</span></code></pre></figure></p>
<p>Example 13.3: Instances of Metaclass</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">TranslucentColor</span> <span class="nf">class</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">Metaclass</span>
<span class="nf">Color</span> <span class="nf">class</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">Metaclass</span>
<span class="nf">Object</span> <span class="nf">class</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">Metaclass</span>
<span class="nf">Behavior</span> <span class="nf">class</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">Metaclass</span></code></pre></figure></p>
<p>
Example 13.4: Metaclass class is a Metaclass</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Metaclass</span> <span class="nf">class</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">Metaclass</span>
<span class="nf">Metaclass</span> <span class="nf">superclass</span> <span class="nf">---></span> <span class="nc">ClassDescription</span></code></pre></figure></p>
<h2>Chapter summary</h2>
<hr />
<p>Now you should understand better how classes are organized and the impact of a uniform object model. If you get lost or confused, you should always remember that message passing is the key: you look for the method in the class of the receiver. This works on any receiver. If the method is not found in the class of the receiver, it is looked up in its superclasses.</p>
<ul>
<li>Every class is an instance of a metaclass. Metaclasses are implicit. A Metaclass is created automatically when you create the class that is its sole instance.</li>
<li>The metaclass hierarchy parallels the class hierarchy. Method lookup for classes parallels method lookup for ordinary objects, and follows the metaclass’s superclass chain.</li>
<li>Every metaclass inherits from Class and Behavior. Every class is a Class . Since metaclasses are classes too, they must also inherit from Class. Behavior provides behaviour common to all entities that have instances.</li>
<li>Every metaclass is an instance of Metaclass. ClassDescription provides everything that is common to Class and Metaclass.</li>
<li>The metaclass of Metaclass is an instance of Metaclass. The instance-of relation forms a closed loop, so Metaclass class class ---> Metaclass.</li>
</ul>
Pharo by Example 122015-05-02T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/05/02/pharo-by-example-12<h2>Seaside by Example</h2>
<hr />
<p>Seaside is a framework for building web applications in Smalltalk. It was originally developed by Avi Bryant in 2002; once mastered, Seaside makes web applications almost as easy to write as desktop applications.</p>
<p>Two of the better known applications built with Seaside are SqueakSource1 and Dabble DB2. Seaside is unusual in that it is thoroughly object-oriented: there are no XHTML templates, no complicated control flows through web pages, and no encoding of state in URLs. Instead, you just send messages to objects. What a nice idea!</p>
<h3>Why do we need Seaside?</h3>
<hr />
<p>Modern web applications try to interact with the user in the same way as desktop applications: they ask the user questions and the user responds, usually by filling in a form or clicking a button. But the web works the other way around: the user’s browser makes a request of the server, and the server responds with a new web page. So web application development frameworks have to cope with a host of problems, chief among them being the management of this “inverted” control flow.</p>
<p>Because of this, many web applications try to forbid the use of the browser’s “back” button due to the difficulty of keeping track of the state of a session. Expressing non-trivial control flows across multiple web pages is often cumbersome, and multiple control flows can be difficult or impossible to express.</p>
<h3>Getting started</h3>
<hr />
<p>The easiest way to get started is to download the “Seaside One-Click Experience” from the Seaside web site3. This is a prepackaged version of Seaside 2.8 for Mac OSX, Linux and Windows. The same web site lists many pointers to additional resources, including documentation and tutorials. Be warned, however, that Seaside has evolved considerably over the years, and not all available material refers to the latest version of Seaside.</p>
<p>Seaside includes a web server; you can turn the server on, telling it to listen on port 8080, by evaluating WAKom startOn: 8080, and you can turn it off again by evaluating WAKom stop. In the default installation, the default administrator login is admin and the default password is seaside. To change them, evaluate: WADispatcherEditor initialize. This will prompt you for a new name and password.</p>
<p><ttp://seaside.st></p>
<blockquote><p>Start the Seaside server and direct a web browser to http://localhost:8080/ seaside/ .</p></blockquote>
<p><img src="/assets/img/smalltalk/Figure%2012.1.png" alt="Figure 12.1" /></p>
<p>Starting up Seaside</p>
<p><img src="/assets/img/smalltalk/Figure%2012.2.png" alt="Figure 12.2" /></p>
<p>The counter.</p>
<h3>Seaside components</h3>
<hr />
<p><img src="/assets/img/smalltalk/Figure%2012.6.png" alt="Figure 12.6" /></p>
<p>Configuring a new application</p>
<blockquote><p>Define a <em>subclass</em> of WAComponent called WAHelloWorld.</p></blockquote>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">WAComponent</span> <span class="nf">subclass:</span> <span class="ss">#WAHelloWorld</span>
<span class="nf">instanceVariableNames:</span> <span class="s">''</span>
<span class="nf">classVariableNames:</span> <span class="s">''</span>
<span class="nf">poolDictionaries:</span> <span class="s">''</span>
<span class="nf">category:</span> <span class="s">'Seaside-Component'</span></code></pre></figure></p>
<blockquote><p>Implement the following method, and put it in a protocol called rendering:</p></blockquote>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">WAHelloWorld</span> <span class="nf">>></span>
<span class="nf">renderContentOn:</span> <span class="nv">html</span>
<span class="nv">html</span> <span class="nf">text:</span> <span class="s">'hello world'</span></code></pre></figure></p>
<blockquote><p>Implement the following method on the class side of WAHelloWorld.</p></blockquote>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">WAHelloWorld</span> <span class="nf">class</span> <span class="nf">>></span>
<span class="nf">canBeRoot</span>
<span class="o">^</span> <span class="bp">true</span></code></pre></figure></p>
<p><img src="/assets/img/smalltalk/Figure%2012.6.1.png" alt="Figure 12.6.1" />
<img src="/assets/img/smalltalk/Figure%2012.6.2.png" alt="Figure 12.6.2" />
<img src="/assets/img/smalltalk/Figure%2012.6.3.png" alt="Figure 12.6.3" />
<img src="/assets/img/smalltalk/Figure%2012.6.4.png" alt="Figure 12.6.4" /></p>
<p><img src="/assets/img/smalltalk/Figure%2012.7.png" alt="Figure 12.7" /></p>
<p>“Hello World” in Seaside</p>
<h4>State backtracking and the “Counter” Application</h4>
<p><img src="/assets/img/smalltalk/Figure%2012.8.png" alt="Figure 12.8" /></p>
<p>The WACounter class, which implements the counter application. Methods with underlined names are on the class-side; those with plain-text names are on the instance side.</p>
<h3>Rendering XHTML</h3>
<hr />
<h4>Rendering the Counter</h4>
<p>The rendering of the counter is relatively straightforward; the code is shown in Figure 12.8. The current value of the counter is displayed as an XHTML heading, and the increment and decrement operations are implemented as html anchors (that is, links) with callbacks to blocks that will send increase and decrease to the counter object.</p>
<p>We will have a closer look at the rendering protocol in a moment. But before we do, let’s have a quick look at the multi-counter.</p>
<h4>From Counter to MultiCounter</h4>
<hr />
<p>WAMultiCounter, shown in Figure 12.9 is also a standalone application, so it overrides canBeRoot to answer true. In addition, it is a composite component, so Seaside requires it to declare its children by implementing a method children that answers an array of all the components it contains. It renders itself by rendering each of its subcomponents, separated by a horizontal rule. Aside from instance and class-side initialization methods, there is nothing else to the multi-counter!</p>
<p><img src="/assets/img/smalltalk/Figure%2012.9.png" alt="Figure 12.9" /></p>
<p>WAMultiCounter</p>
<h4>More about Rendering XHTML</h4>
<p>Here are some of the most basic rendering methods:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">html</span> <span class="nf">text:</span> <span class="s">'hello world'</span><span class="p">.</span> <span class="c">"render a plain text string"</span>
<span class="nv">html</span> <span class="nf">html:</span> <span class="s">'&ndash;'</span><span class="p">.</span> <span class="c">"render an XHTML incantation"</span>
<span class="nv">html</span> <span class="nf">render:</span> <span class="m">1</span><span class="p">.</span> <span class="c">"render any object"</span></code></pre></figure></p>
<h4>Using Brushes</h4>
<p><img src="/assets/img/smalltalk/Figure%2012.10.png" alt="Figure 12.10" /></p>
<p>RenderingDemo</p>
<blockquote><p>If you send a cascade of messages to a brush including the message with:, then with: should be the final message. with: both sets the content and renders the result.</p>
<p>Note that actions should appear only in callbacks. The code executed while rendering should not change the state of the application!</p></blockquote>
<h4>Forms</h4>
<p>Since a form is a complex entity, it is rendered using a block. Note that all the state changes happen in the callbacks, not as part of the rendering.</p>
<h3>CSS: Cascading style sheets</h3>
<hr />
<p>Cascading Style Sheets, or CSS for short, have emerged as a standard way for web applications to separate style from content. Seaside relies on CSS to avoid cluttering your rendering code with layout considerations.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">SeasideDemoWidget</span> <span class="nf">>></span>
<span class="nf">style</span>
<span class="o">^</span> <span class="s">'
body {
font: 10pt Arial, Helvetica, sans−serif, Times New Roman;
}
h2 {
font−size: 12pt; font−weight: normal; font−style: italic;
}
table { border−collapse: collapse; } td {
border: 2px solid #CCCCCC;
padding: 4px; }</p>
<h1>author {</h1>
<pre><code>border: 1px solid black; padding: 2px;
margin: 2px;
</code></pre>
<p>}
.subcomponent {
border: 2px solid lightblue; padding: 2px;
margin: 2px;
}
.highlight { background−color: yellow; } .boolean { background−color: lightgrey; } .field { background−color: lightgrey; }
'</span></code></pre></figure></p>
<p>SeasideDemoWidget common style sheet.</p>
<h3>Managing control flow</h3>
<hr />
<p>Seaside makes it particularly easy to design web applications with non-trivial control flow. There are basically two mechanisms that you can use:</p>
<ol>
<li>A component can call another component by sending caller call: callee. The caller is temporarily replaced by the callee, until the callee returns control by sending answer:. The caller is usually self, but could also be any other currently visible component.</li>
<li>A workflow can be be defined as a task. This is a special kind of component that subclasses WATask (instead of WAComponent). Instead of defining renderContentOn:, it defines no content of its own, but rather defines a go method that sends a series of call: messages to activate various subcomponents in turn.</li>
</ol>
<h4>Call and answer</h4>
<blockquote><p>call: and answer: should never be used while rendering. They may safely be sent from within a callback, or from within the go method of a task.</p></blockquote>
<h4>Convenience methods</h4>
<p><img src="/assets/img/smalltalk/Figure%2012.12.png" alt="Figure 12.12" /></p>
<p>Some standard dialogs</p>
<h4>Tasks</h4>
<p>A task is a component that subclasses WATask. It does not render anything itself, but simply calls other components in a control flow defined by implementing the method go.</p>
<p><img src="/assets/img/smalltalk/Figure%2012.13.png" alt="Figure 12.13" /></p>
<p>A simple task</p>
<h4>Transactions</h4>
<p><img src="/assets/img/smalltalk/Figure%2012.14.png" alt="Figure 12.14" /></p>
<p>The Sushi Store</p>
<p>The sushi store supports the following workflow:</p>
<ol>
<li>Visit the store.</li>
<li>Browse or search for sushi.</li>
<li>Add sushi to your shopping cart. 4. Checkout.</li>
<li>Verify your order.</li>
<li>Enter shipping address.</li>
<li>Verify shipping address.</li>
<li>Enter payment information.</li>
<li>Your fish is on its way!</li>
</ol>
<h3>A complete tutorial example</h3>
<hr />
<p>Let’s see how we can build a complete Seaside application from scratch.8 We will build a RPN (Reverse Polish Notation) calculator as a Seaside application that uses a simple stack machine as its underlying model. Furthermore, the Seaside interface will let us toggle between two displays — one which just shows us the current value on top of the stack, and the other which shows us the complete state of the stack. The calculator with the two display options is shown in Figure 12.15.</p>
<p><img src="/assets/img/smalltalk/Figure%2012.15.png" alt="Figure 12.15" /></p>
<p>RPN calculator and its stack machine</p>
<p>We begin by implementing the stack machine and its tests.</p>
<blockquote><p>Define a new class called MyStackMachine with an instance variable contents initialized to a new OrderedCollection.</p>
<p>The exercise should take at most a couple of hours. If you prefer to just look at the completed source code, you can grab it from the SqueakSource project http://www.squeaksource.com/ PharoByExample. The package to load is PBE-SeasideRPN. The tutorial that follows uses slightly different class names so that you can compare your implementation with ours.</p></blockquote>
<p>We will make use of 5 classes:</p>
<ul>
<li>MyRPNWidget—this should be an abstract class that defines the common CSS style sheet for the application, and other common behavior for the components of the RPN calculator. It is a subclass of WAComponent and the direct superclass of the following four classes.</li>
<li>MyCalculator — this is the root component. It should register the application (on the class side), it should instantiate and render its subcomponents, and it should register any state for backtracking.</li>
<li>MyKeypad — this displays the keys that we use to interact with the calculator.</li>
<li>MyDisplay — this component displays the top of the stack and provides a button to call another component to display the detailed view.</li>
<li>MyDisplayStack — this component shows the detailed view of the stack and provides a button to answer back. It is a subclass of MyDisplay.</li>
</ul>
<h3>A quick look at AJAX</h3>
<hr />
<p><img src="/assets/img/smalltalk/Figure%2012.17.png" alt="Figure 12.17" /></p>
<p>Seaside AJAX processing (simplified)</p>
<h2>Chapter summary</h2>
<hr />
<ul>
<li>The easiest way to get started is to download the “Seaside One-Click Experience” from http://seaside.st</li>
<li>TurntheserveronandoffbyevaluatingWAKomstartOn:8080andWAKom stop.</li>
<li>Reset the administrator login and password by evaluating WADispatcherEditor initialize.</li>
<li>Toggle Halos to directly view application source code, run-time objects, CSS and XHTML.</li>
<li>Send WAGlobalConfiguration setDeploymentMode to hide the toolbar.</li>
<li>Seaside web applications are composed of components, each of which is an instance of a subclass of WAComponent.</li>
<li>Only a root component may be registered as a component. It should implement canBeRoot on the class side. Alternatively it may register itself as an application in its class-side initialize method by sending self registerAsApplication: application path. If you override description it is possible to return a descriptive application name that will be displayed in the configuration editor.</li>
<li>To backtrack state, a component must implement the states method to answer an array of objects whose state will be restored if the user clicks the browser’s “back” button.</li>
<li>A component renders itself by implementing renderContentOn:. The argument to this method is an XHTML rendering canvas (usually called html).</li>
<li>A component can render a subcomponent by sending self render: subcomponent.</li>
<li>XHTML is generated programmatically by sending messages to brushes. A brush is obtained by sending a message, such as paragraph or div, to the html canvas.</li>
<li>If you send a cascade of messages to a brush that includes the message with:, then with: should be the last message sent. Thw with: message sets the contents and renders the result.</li>
<li>Actions should appear only in callbacks. You should not change the state of the application while you are rendering it.</li>
<li>You can bind various form widgets and anchors to instance variables with accessors by sending the message on: instance variable of: object to the brush.</li>
<li>You can define the CSS for a component hierarchy by defining the method style, which should return a string containing the style sheet. (For deployed applications, it is more usual to refer to a style sheet located at a static URL.)</li>
<li>Control flows can be programmed by sending x call: y, in which case component x will be replaced by y until y answers by sending answer: with a result in a callback. The receiver of call: is usually self, but may in general be any visible component.</li>
<li>Acontrolflowcanalsobespecifiedasatask—ainstanceofasubclassof WATask. It should implement the method go, which should call: a series of components in a workflow.</li>
<li>Use WAComponents’s convenience methods request:, inform:, confirm: and chooseFrom:caption: for basic interactions.</li>
<li>To prevent the user from using the browser’s “back” button to access a previous execution state of the web application, you can declare portions of the workflow to be a transaction by enclosing them in an isolate: block.</li>
</ul>
Pharo by Example 112015-05-01T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/05/01/pharo-by-example-11<h2>Morphic</h2>
<hr />
<p>Morphic is the name given to Pharo’s graphical interface. Morphic is written in Smalltalk, so it is fully portable between operating systems; as a consequence, Pharo looks exactly the same on Unix, MacOS and Windows. What distinguishes Morphic from most other user interface toolkits is that it does not have separate modes for “composing” and “running” the interface: all the graphical elements can be assembled and disassembled by the user, at any time.</p>
<h3>The history of Morphic</h3>
<hr />
<p><img src="/assets/img/smalltalk/Figure%2011.1.png" alt="Figure 11.1" /></p>
<p>Detaching a morph, here the Workspace menu item, to make it an independent button.</p>
<blockquote><p>To create a morph to represent a string object, execute the following code in a workspace.</p></blockquote>
<p><code>'Morph' asMorph openInWorld</code></p>
<p><em>Open a browser on the Color class and add the following method to it:</em></p>
<p>Getting a morph for an instance of Color.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Color</span> <span class="nf">>></span>
<span class="nf">asMorph</span>
<span class="o">^</span> <span class="nc">Morph</span> <span class="nb">new</span> <span class="nf">color:</span> <span class="bp">self</span></code></pre></figure></p>
<p>Now execute <code>Color orange asMorph openInWorld</code> in a workspace. Instead of the string-like morph, you get an orange rectangle!</p>
<h3>Manipulating morphs</h3>
<hr />
<p>Every morph, even if it is not currently open on the screen, has a position and a size. For convenience, all morphs are considered to occupy a rectangular region of the screen; if they are irregularly shaped, their position and size are those of the smallest rectangular “box” that surrounds them, which is known as the morph’s bounding box, or just its “bounds”. The position method returns a Point that describes the location of the morph’s upper left corner (or the upper left corner of its bounding box). The origin of the coordinate system is the screen’s upper left corner, with y coordinates increasing down the screen and x coordinates increasing to the right. The extent method also returns a point, but this point specifies the width and height of the morph rather than a location.</p>
<blockquote><p>Type the following code into a workspace and <code>do it</code>:</p></blockquote>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">joe</span> <span class="o">:=</span> <span class="nc">Morph</span> <span class="nb">new</span> <span class="nf">color:</span> <span class="nc">Color</span> <span class="nf">blue</span><span class="p">.</span>
<span class="nv">joe</span> <span class="nf">openInWorld</span><span class="p">.</span>
<span class="nv">bill</span> <span class="o">:=</span> <span class="nc">Morph</span> <span class="nb">new</span> <span class="nf">color:</span> <span class="nc">Color</span> <span class="nf">red</span> <span class="p">.</span>
<span class="nv">bill</span> <span class="nf">openInWorld</span><span class="p">.</span></code></pre></figure></p>
<p>Then type joe position and <code>print it</code> . To move joe, execute joe position: (joe position + (10@3)) repeatedly.</p>
<blockquote><p><em>To make bill follow joe, you can repeatedly execute this code:</em></p></blockquote>
<p><code>bill position: (joe position + (100@0))</code></p>
<h3>Composing morphs</h3>
<hr />
<p>One way of creating new graphical representations is by placing one morph inside another. This is called composition; morphs can be composed to any depth. You can place a morph inside another by sending the message addMorph: to the container morph.</p>
<blockquote><p>Try adding a morph to another one:</p></blockquote>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">star</span> <span class="o">:=</span> <span class="nc">StarMorph</span> <span class="nb">new</span> <span class="nf">color:</span> <span class="nc">Color</span> <span class="nf">yellow</span><span class="p">.</span>
<span class="nv">joe</span> <span class="o">:=</span> <span class="nc">Morph</span> <span class="nb">new</span> <span class="nf">color:</span> <span class="nc">Color</span> <span class="nf">blue</span><span class="p">.</span>
<span class="nv">joe</span> <span class="nf">addMorph:</span> <span class="nv">star</span><span class="p">.</span>
<span class="nv">star</span> <span class="nf">position:</span> <span class="nv">joe</span> <span class="nf">position</span><span class="p">.</span>
<span class="nv">joe</span> <span class="nf">openInWorld</span> <span class="p">.</span>
<span class="nv">star</span> <span class="nf">openInWorld</span><span class="p">.</span></code></pre></figure></p>
<p>The last line positions the star at the same coordinates as joe. Notice that the coordinates of the contained morph are still relative to the screen, not to the containing morph. There are many methods available to position a morph; browse the <em>geometry</em> protocol of class Morph to see for yourself. For example, to center the star inside joe, execute star center: joe center.</p>
<p><img src="/assets/img/smalltalk/Figure%2011.2.png" alt="Figure 11.2" /></p>
<p>The star is contained inside joe, the translucent blue morph.</p>
<h3>Creating and drawing your own morphs</h3>
<hr />
<p>While it is possible to make many interesting and useful graphical representations by composing morphs, sometimes you will need to create something completely different. To do this you define a subclass of Morph and override the drawOn: method to change its appearance.</p>
<p>The morphic framework sends the message drawOn: to a morph when it needs to redisplay the morph on the screen. The parameter to drawOn: is a kind of Canvas; the expected behaviour is that the morph will draw itself on that canvas, inside its bounds. Let’s use this knowledge to create a cross-shaped morph.</p>
<blockquote><p><em>Using the browser, define a new class CrossMorph inheriting from Morph:</em></p></blockquote>
<p><em>Defining CrossMorph</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Morph</span> <span class="nf">subclass:</span> <span class="ss">#CrossMorph</span>
<span class="nf">instanceVariableNames:</span> <span class="s">''</span>
<span class="nf">classVariableNames:</span> <span class="s">''</span>
<span class="nf">poolDictionaries:</span> <span class="s">''</span>
<span class="nf">category:</span> <span class="s">'PBE−Morphic'</span></code></pre></figure></p>
<p>We can define the drawOn: method like this:</p>
<p><em>Drawing a CrossMorph.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nf">drawOn:</span> <span class="nv">aCanvas</span>
<span class="p">|</span><span class="nv"> crossHeight crossWidth horizontalBar verticalBar </span><span class="p">|</span>
<span class="nv">crossHeight</span> <span class="o">:=</span> <span class="bp">self</span> <span class="nf">height</span> <span class="nf">/</span> <span class="m">3.0</span> <span class="p">.</span>
<span class="nv">crossWidth</span> <span class="o">:=</span> <span class="bp">self</span> <span class="nf">width</span> <span class="nf">/</span> <span class="m">3.0</span> <span class="p">.</span>
<span class="nv">horizontalBar</span> <span class="o">:=</span> <span class="bp">self</span> <span class="nf">bounds</span> <span class="nf">insetBy:</span> <span class="m">0</span> <span class="nf">@</span> <span class="nv">crossHeight</span><span class="p">.</span>
<span class="nv">verticalBar</span> <span class="o">:=</span> <span class="bp">self</span> <span class="nf">bounds</span> <span class="nf">insetBy:</span> <span class="nv">crossWidth</span> <span class="nf">@</span> <span class="m">0</span><span class="p">.</span>
<span class="nv">aCanvas</span> <span class="nf">fillRectangle:</span> <span class="nv">horizontalBar</span> <span class="nf">color:</span> <span class="bp">self</span> <span class="nf">color</span><span class="p">.</span>
<span class="nv">aCanvas</span> <span class="nf">fillRectangle:</span> <span class="nv">verticalBar</span> <span class="nf">color:</span> <span class="bp">self</span> <span class="nf">color</span></code></pre></figure></p>
<p><img src="/assets/img/smalltalk/Figure%2011.3.png" alt="Figure 11.3" /></p>
<p>A CrossMorph with its halo; you can resize it as you wish.</p>
<p>Sending the bounds message to a morph answers its bounding box, which is an instance of Rectangle. Rectangles understand many messages that create other rectangles of related geometry; here we use the insetBy: message with a point as its argument to create first a rectangle with reduced height, and then another rectangle with reduced width.</p>
<blockquote><p>To test your new morph, execute <em>CrossMorph new openInWorld</em>.</p>
<p>Define the following method in class CrossMorph:</p></blockquote>
<p><em>Shaping the sensitive zone of the CrossMorph.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nf">containsPoint:</span> <span class="nv">aPoint</span>
<span class="p">|</span><span class="nv"> crossHeight crossWidth horizontalBar verticalBar </span><span class="p">|</span>
<span class="nv">crossHeight</span> <span class="o">:=</span> <span class="bp">self</span> <span class="nf">height</span> <span class="nf">/</span> <span class="m">3.0</span><span class="p">.</span>
<span class="nv">crossWidth</span> <span class="o">:=</span> <span class="bp">self</span> <span class="nf">width</span> <span class="nf">/</span> <span class="m">3.0</span><span class="p">.</span>
<span class="nv">horizontalBar</span> <span class="o">:=</span> <span class="bp">self</span> <span class="nf">bounds</span> <span class="nf">insetBy:</span> <span class="m">0</span> <span class="nf">@</span> <span class="nv">crossHeight</span><span class="p">.</span>
<span class="nv">verticalBar</span> <span class="o">:=</span> <span class="bp">self</span> <span class="nf">bounds</span> <span class="nf">insetBy:</span> <span class="nv">crossWidth</span> <span class="nf">@</span> <span class="m">0</span><span class="p">.</span>
<span class="o">^</span> <span class="p">(</span><span class="nv">horizontalBar</span> <span class="nf">containsPoint:</span> <span class="nv">aPoint</span><span class="p">)</span>
<span class="nf">or:</span> <span class="p">[</span><span class="nv">verticalBar</span> <span class="nf">containsPoint:</span> <span class="nv">aPoint</span><span class="p">]</span></code></pre></figure></p>
<p><em>horizontalBar.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nf">horizontalBar</span>
<span class="p">|</span><span class="nv"> crossHeight </span><span class="p">|</span>
<span class="nv">crossHeight</span> <span class="o">:=</span> <span class="bp">self</span> <span class="nf">height</span> <span class="nf">/</span> <span class="m">3.0</span><span class="p">.</span>
<span class="o">^</span> <span class="bp">self</span> <span class="nf">bounds</span> <span class="nf">insetBy:</span> <span class="m">0</span> <span class="nf">@</span> <span class="nv">crossHeight</span></code></pre></figure></p>
<p><em>verticalBar.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nf">verticalBar</span>
<span class="p">|</span><span class="nv"> crossWidth </span><span class="p">|</span>
<span class="nv">crossWidth</span> <span class="o">:=</span> <span class="bp">self</span> <span class="nf">width</span> <span class="nf">/</span> <span class="m">3.0</span><span class="p">.</span>
<span class="o">^</span> <span class="bp">self</span> <span class="nf">bounds</span> <span class="nf">insetBy:</span> <span class="nv">crossWidth</span> <span class="nf">@</span> <span class="m">0</span></code></pre></figure></p>
<p>We can then define both drawOn: and containsPoint: using these methods:</p>
<p><em>Refactored CrossMorph >> drawOn:.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nf">drawOn:</span> <span class="nv">aCanvas</span>
<span class="nv">aCanvas</span> <span class="nf">fillRectangle:</span> <span class="bp">self</span> <span class="nf">horizontalBar</span> <span class="nf">color:</span> <span class="bp">self</span> <span class="nf">color</span><span class="p">.</span>
<span class="nv">aCanvas</span> <span class="nf">fillRectangle:</span> <span class="bp">self</span> <span class="nf">verticalBar</span> <span class="nf">color:</span> <span class="bp">self</span> <span class="nf">color</span></code></pre></figure></p>
<p><em>Refactored CrossMorph >> containsPoint:.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nf">containsPoint:</span> <span class="nv">aPoint</span>
<span class="o">^</span> <span class="p">(</span><span class="bp">self</span> <span class="nf">horizontalBar</span> <span class="nf">containsPoint:</span> <span class="nv">aPoint</span><span class="p">)</span>
<span class="nf">or:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">verticalBar</span> <span class="nf">containsPoint:</span> <span class="nv">aPoint</span><span class="p">]</span></code></pre></figure></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">m</span> <span class="o">:=</span> <span class="nc">CrossMorph</span> <span class="nb">new</span> <span class="nf">bounds:</span> <span class="p">(</span><span class="m">0</span><span class="nf">@</span><span class="m">0</span> <span class="nf">corner:</span> <span class="m">300</span><span class="nf">@</span><span class="m">300</span><span class="p">).</span>
<span class="nv">m</span> <span class="nf">openInWorld</span><span class="p">.</span>
<span class="nv">m</span> <span class="nf">color:</span> <span class="p">(</span><span class="nc">Color</span> <span class="nf">blue</span> <span class="nf">alpha:</span> <span class="m">0.3</span><span class="p">).</span></code></pre></figure></p>
<p><img src="/assets/img/smalltalk/Figure%2011.4.png" alt="Figure 11.4" /></p>
<p>The center of the cross is filled twice with the colour.</p>
<p><img src="/assets/img/smalltalk/Figure%2011.5.png" alt="Figure 11.5" /></p>
<p>The cross-shaped morph, showing a row of unfilled pixels.</p>
<p>The revised drawOn: method, which fills the center of the cross once.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nf">drawOn:</span> <span class="nv">aCanvas</span>
<span class="p">|</span><span class="nv"> topAndBottom </span><span class="p">|</span>
<span class="nv">aCanvas</span> <span class="nf">fillRectangle:</span> <span class="bp">self</span> <span class="nf">horizontalBar</span> <span class="nf">color:</span> <span class="bp">self</span> <span class="nf">color</span><span class="p">.</span>
<span class="nv">topAndBottom</span> <span class="o">:=</span> <span class="bp">self</span> <span class="nf">verticalBar</span> <span class="nf">areasOutside:</span> <span class="bp">self</span> <span class="nf">horizontalBar</span><span class="p">.</span>
<span class="nv">topAndBottom</span> <span class="nf">do:</span> <span class="p">[</span> <span class="o">:</span><span class="nv">each</span> <span class="p">|</span> <span class="nv">aCanvas</span> <span class="nf">fillRectangle:</span> <span class="nv">each</span> <span class="nf">color:</span> <span class="bp">self</span> <span class="nf">color</span><span class="p">]</span></code></pre></figure></p>
<p><em>CrossMorph»horizontalBar with explicit rounding.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nf">horizontalBar</span>
<span class="p">|</span><span class="nv"> crossHeight </span><span class="p">|</span>
<span class="nv">crossHeight</span> <span class="o">:=</span> <span class="p">(</span><span class="bp">self</span> <span class="nf">height</span> <span class="nf">/</span> <span class="m">3.0</span><span class="p">)</span> <span class="nf">rounded</span><span class="p">.</span>
<span class="o">^</span> <span class="bp">self</span> <span class="nf">bounds</span> <span class="nf">insetBy:</span> <span class="m">0</span> <span class="nf">@</span> <span class="nv">crossHeight</span></code></pre></figure></p>
<p><em>CrossMorph»verticalBar with explicit rounding.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nf">verticalBar</span>
<span class="p">|</span><span class="nv"> crossWidth </span><span class="p">|</span>
<span class="nv">crossWidth</span> <span class="o">:=</span> <span class="p">(</span><span class="bp">self</span> <span class="nf">width</span> <span class="nf">/</span> <span class="m">3.0</span><span class="p">)</span> <span class="nf">rounded</span><span class="p">.</span>
<span class="o">^</span> <span class="bp">self</span> <span class="nf">bounds</span> <span class="nf">insetBy:</span> <span class="nv">crossWidth</span> <span class="nf">@</span> <span class="m">0</span></code></pre></figure></p>
<h3>Interaction and animation</h3>
<hr />
<p>To build live user-interfaces using morphs, we need to be able to interact with them using the mouse and the keyboard. Moreover, the morphs need to be able respond to user input by changing their appearance and position — that is, by animating themselves.</p>
<h4>Mouse events</h4>
<p>When a mouse button is pressed, Morphic sends each morph under the mouse pointer the message handlesMouseDown:. If a morph answers true, then Morphic immediately sends it the mouseDown: message; it also sends the mouseUp: message when the user releases the mouse button. If all morphs answer false, then Morphic initiates a drag-and-drop operation. As we will discuss below, the mouseDown: and mouseUp: messages are sent with an argument — a MouseEvent object — that encodes the details of the mouse action.</p>
<p>Let’s extend CrossMorph to handle mouse events. We start by ensuring that all crossMorphs answer true to the handlesMouseDown: message.</p>
<blockquote><p>Add this method to CrossMorph:</p></blockquote>
<p><em>Declaring that CrossMorph will react to mouse clicks.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">CrossMorph</span> <span class="nf">>></span>
<span class="nf">handlesMouseDown:</span> <span class="nv">anEvent</span>
<span class="o">^</span> <span class="bp">true</span></code></pre></figure></p>
<p>Suppose that when we click on the cross, we want to change the color of the cross to red, and when we action-click on it, we want to change the color to yellow.</p>
<p><em>Reacting to mouse clicks by changing the morph’s color.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">CrossMorph</span> <span class="nf">>></span>
<span class="nf">mouseDown:</span> <span class="nv">anEvent</span>
<span class="nv">anEvent</span> <span class="nf">redButtonPressed</span> <span class="c">"click"</span>
<span class="nb">ifTrue:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">color:</span> <span class="nc">Color</span> <span class="nf">red</span><span class="p">].</span>
<span class="nv">anEvent</span> <span class="nf">yellowButtonPressed</span> <span class="c">"action−click"</span>
<span class="nb">ifTrue:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">color:</span> <span class="nc">Color</span> <span class="nf">yellow</span><span class="p">].</span>
<span class="bp">self</span> <span class="nf">changed</span></code></pre></figure></p>
<p>Notice that in addition to changing the color of the morph, this method also sends self changed. This makes sure that morphic sends drawOn: in a timely fashion. Notealsothatoncethemorphhandlesmouseevents,youcanno longer grab it with the mouse and move it. Instead you have to use the halo: meta-click on the morph to make the halo appear and grab either the brown move handle <code>fullscreen</code> or the black pickup handle <code>home</code> at the top of the morph.</p>
<p>The anEvent argument of mouseDown: is an instance of MouseEvent, which is a subclass of MorphicEvent. MouseEvent defines the redButtonPressed and yellowButtonPressed methods. Browse this class to see what other methods it provides to interrogate the mouse event.</p>
<h4>Keyboard events</h4>
<p>To catch keyboard events, we need to take three steps.</p>
<ol>
<li>Give the “keyboard focus” to a specific morph: for instance we can give focus to our morph when the mouse is over it.</li>
<li>Handle the keyboard event itself with the handleKeystroke: method: this message is sent to the morph that has keyboard focus when the user presses a key.</li>
<li>Release the keyboard focus when the mouse is no longer over our morph.</li>
</ol>
<p>Let’s extend CrossMorph so that it reacts to keystrokes. First, we need to arrange to be notified when the mouse is over the morph. This will happen if our morph answers true to the handlesMouseOver: message</p>
<blockquote><p>Declare that CrossMorph will react when it is under the mouse pointer.</p></blockquote>
<p><em>We want to handle “mouse over” events.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">CrossMorph</span> <span class="nf">>></span>
<span class="nf">handlesMouseOver:</span> <span class="nv">anEvent</span>
<span class="o">^</span> <span class="bp">true</span></code></pre></figure></p>
<p>This message is the equivalent of handlesMouseDown: for the mouse position. When the mouse pointer enters or leaves the morph, the mouseEnter: and mouseLeave: messages are sent to it.</p>
<blockquote><p>Define two methods so that CrossMorph catches and releases the keyboard focus, and a third method to actually handle the keystrokes.</p></blockquote>
<p><em>Getting the keyboard focus when the mouse enters the morph.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">CrossMorph</span> <span class="nf">>></span>
<span class="nf">mouseEnter:</span> <span class="nv">anEvent</span>
<span class="nv">anEvent</span> <span class="nf">hand</span> <span class="nf">newKeyboardFocus:</span> <span class="bp">self</span></code></pre></figure></p>
<p><em>Handing back the focus when the pointer goes away.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">CrossMorph</span> <span class="nf">>></span>
<span class="nf">mouseLeave:</span> <span class="nv">anEvent</span>
<span class="nv">anEvent</span> <span class="nf">hand</span> <span class="nf">newKeyboardFocus:</span> <span class="bp">nil</span></code></pre></figure></p>
<p><em>Receiving and handling keyboard events.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">CrossMorph</span> <span class="nf">>></span>
<span class="nf">handleKeystroke:</span> <span class="nv">anEvent</span>
<span class="p">|</span><span class="nv"> keyValue </span><span class="p">|</span>
<span class="nv">keyValue</span> <span class="o">:=</span> <span class="nv">anEvent</span> <span class="nf">keyValue</span><span class="p">.</span>
<span class="nv">keyValue</span> <span class="nf">=</span> <span class="m">30</span> <span class="c">"up arrow"</span>
<span class="nb">ifTrue:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">position:</span> <span class="bp">self</span> <span class="nf">position</span> <span class="nf">-</span> <span class="p">(</span><span class="m">0</span> <span class="nf">@</span> <span class="m">1</span><span class="p">)].</span>
<span class="nv">keyValue</span> <span class="nf">=</span> <span class="m">31</span> <span class="c">"down arrow"</span>
<span class="nb">ifTrue:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">position:</span> <span class="bp">self</span> <span class="nf">position</span> <span class="nf">+</span> <span class="p">(</span><span class="m">0</span> <span class="nf">@</span> <span class="m">1</span><span class="p">)].</span>
<span class="nv">keyValue</span> <span class="nf">=</span> <span class="m">29</span> <span class="c">"right arrow"</span>
<span class="nb">ifTrue:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">position:</span> <span class="bp">self</span> <span class="nf">position</span> <span class="nf">+</span> <span class="p">(</span><span class="m">1</span> <span class="nf">@</span> <span class="m">0</span><span class="p">)].</span>
<span class="nv">keyValue</span> <span class="nf">=</span> <span class="m">28</span> <span class="c">"left arrow"</span>
<span class="nb">ifTrue:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">position:</span> <span class="bp">self</span> <span class="nf">position</span> <span class="nf">-</span> <span class="p">(</span><span class="m">1</span> <span class="nf">@</span> <span class="m">0</span><span class="p">)]</span></code></pre></figure></p>
<p>We have written this method so that you can move the morph using the arrow keys. Note that when the mouse is no longer over the morph, the handleKeystroke: message is not sent, so the morph stops responding to keyboard commands. To discover the key values, you can open a Transcript window and add Transcript show: anEvent keyValue to method 11.17. The anEvent argument of handleKeystroke: is an instance of KeyboardEvent, another subclass of MorphicEvent. Browse this class to learn more about keyboard events.</p>
<h4>Morphic animations</h4>
<p>Morphic provides a simple animation system with two main methods: step is sent to a morph at regular intervals of time, while stepTime specifies the time in milliseconds between steps. In addition, startStepping turns on the stepping mechanism, while stopStepping turns it off again; isStepping can be used to find out whether a morph is currently being stepped.</p>
<blockquote><p>Make CrossMorph blink by defining these methods as follows:</p></blockquote>
<p><em>Defining the animation time interval.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">CrossMorph</span> <span class="nf">>></span>
<span class="nf">stepTime</span>
<span class="o">^</span> <span class="m">100</span></code></pre></figure></p>
<p><em>Making a step in the animation.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">CrossMorph</span> <span class="nf">>></span>
<span class="nf">step</span>
<span class="p">(</span><span class="bp">self</span> <span class="nf">color</span> <span class="nf">diff:</span> <span class="nc">Color</span> <span class="nf">black</span><span class="p">)</span> <span class="nf"><</span> <span class="m">0.1</span>
<span class="nb">ifTrue:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">color:</span> <span class="nc">Color</span> <span class="nf">red</span><span class="p">]</span>
<span class="nb">ifFalse:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">color:</span> <span class="bp">self</span> <span class="nf">color</span> <span class="nf">darker</span><span class="p">]</span></code></pre></figure></p>
<p>To start things off, you can open an inspector on a CrossMorph (using the debug handle <code>settings</code> in the morphic halo), type self startStepping in the small workspace pane at the bottom, and do it . Alternatively, you can modify the handleKeystroke: method so that you can use the + and - keys to start and stop stepping.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nf">handleKeystroke:</span> <span class="nv">anEvent</span>
<span class="p">|</span><span class="nv"> keyValue </span><span class="p">|</span>
<span class="nv">keyValue</span> <span class="o">:=</span> <span class="nv">anEvent</span> <span class="nf">keyValue</span><span class="p">.</span>
<span class="nv">keyValue</span> <span class="nf">=</span> <span class="m">30</span> <span class="c">"up arrow"</span>
<span class="nb">ifTrue:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">position:</span> <span class="bp">self</span> <span class="nf">position</span> <span class="nf">-</span> <span class="p">(</span><span class="m">0</span> <span class="nf">@</span> <span class="m">1</span><span class="p">)].</span>
<span class="nv">keyValue</span> <span class="nf">=</span> <span class="m">31</span> <span class="c">"down arrow"</span>
<span class="nb">ifTrue:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">position:</span> <span class="bp">self</span> <span class="nf">position</span> <span class="nf">+</span> <span class="p">(</span><span class="m">0</span> <span class="nf">@</span> <span class="m">1</span><span class="p">)].</span>
<span class="nv">keyValue</span> <span class="nf">=</span> <span class="m">29</span> <span class="c">"right arrow"</span>
<span class="nb">ifTrue:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">position:</span> <span class="bp">self</span> <span class="nf">position</span> <span class="nf">+</span> <span class="p">(</span><span class="m">1</span> <span class="nf">@</span> <span class="m">0</span><span class="p">)].</span>
<span class="nv">keyValue</span> <span class="nf">=</span> <span class="m">28</span> <span class="c">"left arrow"</span>
<span class="nb">ifTrue:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">position:</span> <span class="bp">self</span> <span class="nf">position</span> <span class="nf">-</span> <span class="p">(</span><span class="m">1</span> <span class="nf">@</span> <span class="m">0</span><span class="p">)].</span>
<span class="nv">keyValue</span> <span class="nf">=</span> <span class="sc">$+</span> <span class="nf">asciiValue</span> <span class="nb">ifTrue:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">startStepping</span><span class="p">].</span>
<span class="nv">keyValue</span> <span class="nf">=</span> <span class="sc">$-</span> <span class="nf">asciiValue</span> <span class="nb">ifTrue:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">stopStepping</span><span class="p">].</span></code></pre></figure></p>
<h3>Interactors</h3>
<hr />
<p>To prompt the user for input, the UIManager class provides a large number of ready-to-use dialog boxes. For instance, the request:initialAnswer: method returns the string entered by the user.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">UIManager</span> <span class="nf">default</span> <span class="nf">request:</span> <span class="s">'What''s your name?'</span> <span class="nf">initialAnswer:</span> <span class="s">'no name'</span></code></pre></figure></p>
<p><img src="/assets/img/smalltalk/Figure%2011.6.png" alt="Figure 11.6" /></p>
<p>An input dialog.</p>
<p>To display a popup menu, use one of the various chooseFrom: methods</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">UIManager</span> <span class="nf">default</span>
<span class="nf">chooseFrom:</span> <span class="ss">#(</span><span class="s">'circle'</span> <span class="s">'oval'</span> <span class="s">'square'</span> <span class="s">'rectangle'</span> <span class="s">'triangle'</span><span class="ss">)</span> <span class="nf">lines:</span> <span class="ss">#(</span><span class="m">2</span> <span class="m">4</span><span class="ss">)</span> <span class="nf">message:</span> <span class="s">'Choose a shape'</span></code></pre></figure></p>
<p><img src="/assets/img/smalltalk/Figure%2011.7.png" alt="Figure 11.7" /></p>
<p>Pop-up menu.</p>
<h3>Drag-and-drop</h3>
<hr />
<p>Morphic also supports drag-and-drop. Let’s examine a simple example with two morphs, a receiver morph and a dropped morph. The receiver will accept a morph only if the dropped morph matches a given condition: in our example, the morph should be blue. If it is rejected, the dropped morph decides what to do.</p>
<blockquote><p>Let’s first define the receiver morph:</p></blockquote>
<p><em>Defining a morph on which we can drop other morphs</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Morph</span> <span class="nf">subclass:</span> <span class="ss">#ReceiverMorph</span>
<span class="nf">instanceVariableNames:</span> <span class="s">''</span>
<span class="nf">classVariableNames:</span> <span class="s">''</span>
<span class="nf">poolDictionaries:</span> <span class="s">''</span>
<span class="nf">category:</span> <span class="s">'PBE−Morphic'</span></code></pre></figure></p>
<blockquote><p>Now define the initialization method in the usual way:</p></blockquote>
<p><em>Initializing ReceiverMorph.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">ReceiverMorph</span> <span class="nf">>></span>
<span class="nf">initialize</span>
<span class="bp">super</span> <span class="nf">initialize</span><span class="p">.</span>
<span class="nv">color</span> <span class="o">:=</span> <span class="nc">Color</span> <span class="nf">red</span><span class="p">.</span>
<span class="nv">bounds</span> <span class="o">:=</span> <span class="m">0</span> <span class="nf">@</span> <span class="m">0</span> <span class="nf">extent:</span> <span class="m">200</span> <span class="nf">@</span> <span class="m">200</span></code></pre></figure></p>
<p>How do we decide if the receiver morph will accept or repel the dropped morph? In general, both of the morphs will have to agree to the interaction. The receiver does this by responding to wantsDroppedMorph:event:; the first argument is the dropped morph, and the second the mouse event, so that the receiver can, for example, see if any modifier keys were held down at the time of the drop. The dropped morph is also given the opportunity to check and see if it likes the morph onto which it is being dropped; it is sent the message wantsToBeDroppedInto:. The default implementation of this method (in class Morph) answers true.</p>
<p><em>Accept dropped morphs based on their color.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">ReceiverMorph</span> <span class="nf">>></span>
<span class="nf">wantsDroppedMorph:</span> <span class="nv">aMorph</span> <span class="nf">event:</span> <span class="nv">anEvent</span>
<span class="o">^</span> <span class="nv">aMorph</span> <span class="nf">color</span> <span class="nf">=</span> <span class="nc">Color</span> <span class="nf">blue</span></code></pre></figure></p>
<p>What happens to the dropped morph if the receiving morph doesn’t want it? The default behaviour is for it to do nothing, that is, to sit on top of the receiving morph, but without interacting with it. A more intuitive behavior is for the dropped morph to go back to its original position. This can be achieved by the receiver answering true to the message repelsMorph:event: when it doesn’t want the dropped morph:</p>
<p><em>Changing the behaviour of the dropped morph when it is rejected.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">ReceiverMorph</span> <span class="nf">>></span>
<span class="nf">repelsMorph:</span> <span class="nv">aMorph</span> <span class="nf">event:</span> <span class="nv">ev</span>
<span class="o">^</span> <span class="p">(</span><span class="bp">self</span> <span class="nf">wantsDroppedMorph:</span> <span class="nv">aMorph</span> <span class="nf">event:</span> <span class="nv">ev</span><span class="p">)</span> <span class="nf">not</span></code></pre></figure></p>
<p>That’s all we need as far as the receiver is concerned.</p>
<blockquote><p>Create instances of ReceiverMorph and EllipseMorph in a workspace:</p></blockquote>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">ReceiverMorph</span> <span class="nb">new</span> <span class="nf">openInWorld</span><span class="p">.</span>
<span class="nc">EllipseMorph</span> <span class="nb">new</span> <span class="nf">openInWorld</span><span class="p">.</span></code></pre></figure></p>
<p>Try to drag-and-drop the yellow EllipseMorph onto the receiver. It will be rejected and sent back to its initial position.</p>
<blockquote><p>To change this behaviour, change the color of the ellipse morph to Color blue using an inspector. Blue morphs should be accepted by the ReceiverMorph.</p></blockquote>
<p>Let’s create a specific subclass of Morph, named DroppedMorph, so we can experiment a bit more:</p>
<p><em>Defining a morph we can drag-and-drop onto ReceiverMorph</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Morph</span> <span class="nf">subclass:</span> <span class="ss">#DroppedMorph</span>
<span class="nf">instanceVariableNames:</span> <span class="s">''</span>
<span class="nf">classVariableNames:</span> <span class="s">''</span> <span class="nf">poolDictionaries:</span> <span class="s">''</span>
<span class="nf">category:</span> <span class="s">'PBE−Morphic'</span></code></pre></figure></p>
<p><em>Initializing DroppedMorph.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">DroppedMorph</span> <span class="nf">>></span>
<span class="nf">initialize</span>
<span class="bp">super</span> <span class="nf">initialize</span><span class="p">.</span>
<span class="nv">color</span> <span class="o">:=</span> <span class="nc">Color</span> <span class="nf">blue</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">position:</span> <span class="m">250</span><span class="nf">@</span><span class="m">100</span></code></pre></figure></p>
<p>Now we can specify what the dropped morph should do when it is rejected by the receiver; here it will stay attached to the mouse pointer:</p>
<p><em>Reacting when the morph was dropped but rejected.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">DroppedMorph</span> <span class="nf">>></span>
<span class="nf">rejectDropMorphEvent:</span> <span class="nv">anEvent</span>
<span class="p">|</span><span class="nv">h</span><span class="p">|</span>
<span class="nv">h</span> <span class="o">:=</span> <span class="nv">anEvent</span> <span class="nf">hand</span><span class="p">.</span>
<span class="nc">WorldState</span> <span class="nf">addDeferredUIMessage:</span> <span class="p">[</span><span class="nv">h</span> <span class="nf">grabMorph:</span> <span class="bp">self</span><span class="p">].</span>
<span class="nv">anEvent</span> <span class="nf">wasHandled:</span> <span class="bp">true</span></code></pre></figure></p>
<p>Sending the hand message to an event answers the hand, an instance of HandMorph that represents the mouse pointer and whatever it holds. Here we tell the World that the hand should grab self, the rejected morph.</p>
<blockquote><p>Create two instances of DroppedMorph, and then drag-and-drop them onto the receiver.</p></blockquote>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">ReceiverMorph</span> <span class="nb">new</span> <span class="nf">openInWorld</span><span class="p">.</span>
<span class="p">(</span><span class="nc">DroppedMorph</span> <span class="nb">new</span> <span class="nf">color:</span> <span class="nc">Color</span> <span class="nf">blue</span><span class="p">)</span> <span class="nf">openInWorld</span><span class="p">.</span>
<span class="p">(</span><span class="nc">DroppedMorph</span> <span class="nb">new</span> <span class="nf">color:</span> <span class="nc">Color</span> <span class="nf">green</span><span class="p">)</span> <span class="nf">openInWorld</span><span class="p">.</span></code></pre></figure></p>
<p>The green morph is rejected and therefore stays attached to the mouse pointer.</p>
<h3>A complete example</h3>
<hr />
<p>Let’s design a morph to roll a die. Clicking on it will display the values of all sides of the die in a quick loop, and another click will stop the animation.</p>
<p><img src="/assets/img/smalltalk/Figure%2011.8.png" alt="Figure 11.8" /></p>
<p>The die in Morphic.</p>
<blockquote><p>Define the die as a subclass of BorderedMorph instead of Morph, because we will make use of the border.</p></blockquote>
<p><em>Defining the die morph</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">BorderedMorph</span> <span class="nf">subclass:</span> <span class="ss">#DieMorph</span>
<span class="nf">instanceVariableNames:</span> <span class="s">'faces dieValue isStopped'</span>
<span class="nf">classVariableNames:</span> <span class="s">''</span>
<span class="nf">poolDictionaries:</span> <span class="s">''</span>
<span class="nf">category:</span> <span class="s">'PBE−Morphic'</span></code></pre></figure></p>
<p>The instance variable faces records the number of faces on the die; we allow dice with up to 9 faces! dieValue records the value of the face that is currently displayed, and isStopped is true if the die animation has stopped running. To create a die instance, we define the faces: n method on the class side of DieMorph to create a new die with n faces.</p>
<p><em>Creating a new die with the number of faces we like.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">DieMorph</span> <span class="nf">class</span> <span class="nf">>></span>
<span class="nf">faces:</span> <span class="nv">aNumber</span>
<span class="o">^</span> <span class="bp">self</span> <span class="nb">new</span> <span class="nf">faces:</span> <span class="nv">aNumber</span></code></pre></figure></p>
<p>The initialize method is defined on the instance side in the usual way;
remember that new sends initialize to the newly-created instance.</p>
<p><em>Initializing instances of DieMorph.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">DieMorph</span> <span class="nf">>></span>
<span class="nf">initialize</span>
<span class="bp">super</span> <span class="nf">initialize</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">extent:</span> <span class="m">50</span> <span class="nf">@</span> <span class="m">50</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">useGradientFill</span><span class="p">;</span>
<span class="nf">borderWidth:</span> <span class="m">2</span><span class="p">;</span>
<span class="nf">useRoundedCorners</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">setBorderStyle:</span> <span class="ss">#complexRaised</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">fillStyle</span> <span class="nf">direction:</span> <span class="bp">self</span> <span class="nf">extent</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">color:</span> <span class="nc">Color</span> <span class="nf">green</span><span class="p">.</span>
<span class="nv">dieValue</span> <span class="o">:=</span> <span class="m">1</span><span class="p">.</span>
<span class="nv">faces</span> <span class="o">:=</span> <span class="m">6</span><span class="p">.</span>
<span class="nv">isStopped</span> <span class="o">:=</span> <span class="bp">false</span></code></pre></figure></p>
<p>We use a few methods of BorderedMorph to give a nice appearance to the die: a thick border with a raised effect, rounded corners, and a color gradient on the visible face. We define the instance method faces: to check for a valid parameter as follows:</p>
<p><em>Setting the number of faces of the die.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">DieMorph</span> <span class="nf">>></span>
<span class="nf">faces:</span> <span class="nv">aNumber</span>
<span class="c">"Set the number of faces"</span>
<span class="p">(</span><span class="nv">aNumber</span> <span class="nf">isInteger</span>
<span class="nf">and:</span> <span class="p">[</span><span class="nv">aNumber</span> <span class="nf">></span> <span class="m">0</span><span class="p">]</span>
<span class="nf">and:</span> <span class="p">[</span><span class="nv">aNumber</span> <span class="nf"><=</span> <span class="m">9</span><span class="p">])</span>
<span class="nb">ifTrue:</span> <span class="p">[</span><span class="nv">faces</span> <span class="o">:=</span> <span class="nv">aNumber</span><span class="p">]</span></code></pre></figure></p>
<p>It may be good to review the order in which the messages are sent when a die is created. For instance, if we start by evaluating DieMorph faces: 9:</p>
<ol>
<li>The class method DieMorph class»faces: sends new to DieMorph class.</li>
<li>The method for new (inherited by DieMorph class from Behavior) creates
the new instance and sends it the initialize message.</li>
<li>The initialize method in DieMorph sets faces to an initial value of 6.</li>
<li>DieMorph class»new returns to the class method DieMorph class»faces:, which then sends the message faces: 9 to the new instance.</li>
<li>The instance method DieMorph»faces: now executes, setting the faces instance variable to 9.</li>
</ol>
<p>Before defining drawOn:, we need a few methods to place the dots on the displayed face:</p>
<p><em>Nine methods for placing points on the faces of the die.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">DieMorph</span> <span class="nf">>></span>
<span class="nf">face1</span>
<span class="o">^</span> <span class="p">{</span><span class="m">0.75</span><span class="nf">@</span><span class="m">0.75</span><span class="p">}</span></p>
<p><span class="nf">DieMorph</span> <span class="nf">>></span>
<span class="nf">face2</span>
<span class="o">^</span> <span class="p">{</span><span class="m">0.75</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.5</span><span class="nf">@</span><span class="m">0.5</span><span class="p">}</span></p>
<p><span class="nf">DieMorph</span> <span class="nf">>></span>
<span class="nf">face3</span>
<span class="o">^</span> <span class="p">{</span><span class="m">0.75</span><span class="nf">@</span><span class="m">0.25</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.25</span><span class="nf">@</span><span class="m">0.75</span><span class="p">}</span></p>
<p><span class="nf">DieMorph</span> <span class="nf">>></span>
<span class="nf">face4</span>
<span class="o">^</span> <span class="p">{</span><span class="m">0.75</span><span class="nf">@</span><span class="m">0.25</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.25</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.5</span><span class="nf">@</span><span class="m">0.5</span><span class="p">}</span></p>
<p><span class="nf">DieMorph</span> <span class="nf">>></span>
<span class="nf">face5</span>
<span class="o">^</span> <span class="p">{</span><span class="m">0.75</span><span class="nf">@</span><span class="m">0.25</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.25</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.25</span><span class="nf">@</span><span class="m">0.5</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.5</span><span class="p">}</span></p>
<p><span class="nf">DieMorph</span> <span class="nf">>></span>
<span class="nf">face6</span>
<span class="o">^</span> <span class="p">{</span><span class="m">0.75</span><span class="nf">@</span><span class="m">0.25</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.25</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.25</span><span class="nf">@</span><span class="m">0.5</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.5</span> <span class="p">.</span> <span class="m">0.5</span><span class="nf">@</span><span class="m">0.5</span><span class="p">}</span></p>
<p><span class="nf">DieMorph</span> <span class="nf">>></span>
<span class="nf">face7</span>
<span class="o">^</span> <span class="p">{</span><span class="m">0.75</span><span class="nf">@</span><span class="m">0.25</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.25</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.25</span><span class="nf">@</span><span class="m">0.5</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.5</span> <span class="p">.</span> <span class="m">0.5</span><span class="nf">@</span><span class="m">0.5</span> <span class="p">.</span> <span class="m">0.5</span><span class="nf">@</span><span class="m">0.25</span><span class="p">}</span></p>
<p><span class="nf">DieMorph</span> <span class="nf">>></span>
<span class="nf">face8</span>
<span class="o">^</span> <span class="p">{</span><span class="m">0.25</span><span class="nf">@</span><span class="m">0.25</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.25</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.25</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.25</span><span class="nf">@</span><span class="m">0.5</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.5</span> <span class="p">.</span> <span class="m">0.5</span><span class="nf">@</span><span class="m">0.5</span> <span class="p">.</span> <span class="m">0.5</span><span class="nf">@</span><span class="m">0.25</span><span class="p">}</span></p>
<p><span class="nf">DieMorph</span> <span class="nf">>></span>
<span class="nf">face9</span>
<span class="o">^</span> <span class="p">{</span><span class="m">0.25</span><span class="nf">@</span><span class="m">0.25</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.25</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.25</span><span class="nf">@</span><span class="m">0.75</span> <span class="p">.</span> <span class="m">0.25</span><span class="nf">@</span><span class="m">0.5</span> <span class="p">.</span> <span class="m">0.75</span><span class="nf">@</span><span class="m">0.5</span> <span class="p">.</span> <span class="m">0.5</span><span class="nf">@</span><span class="m">0.5</span> <span class="p">.</span> <span class="m">0.5</span><span class="nf">@</span><span class="m">0.25</span> <span class="p">.</span> <span class="m">0.5</span><span class="nf">@</span><span class="m">0.75</span><span class="p">}</span></code></pre></figure></p>
<p>These methods define collections of the coordinates of dots for each face. The coordinates are in a square of size 1 ◊ 1; we will simply need to scale them to place the actual dots.</p>
<p>The drawOn: method does two things: it draws the die background with the super-send, and then draws the dots.</p>
<p><em>Drawing the die morph.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">DieMorph</span> <span class="nf">>></span>
<span class="nf">drawOn:</span> <span class="nv">aCanvas</span>
<span class="bp">super</span> <span class="nf">drawOn:</span> <span class="nv">aCanvas</span><span class="p">.</span>
<span class="p">(</span><span class="bp">self</span> <span class="nf">perform:</span> <span class="p">(</span><span class="s">'face'</span> <span class="nf">,</span> <span class="nv">dieValue</span> <span class="nf">asString</span><span class="p">)</span> <span class="nf">asSymbol</span><span class="p">)</span>
<span class="nf">do:</span> <span class="p">[</span><span class="o">:</span><span class="nv">aPoint</span> <span class="p">|</span> <span class="bp">self</span> <span class="nf">drawDotOn:</span> <span class="nv">aCanvas</span> <span class="nf">at:</span> <span class="nv">aPoint</span><span class="p">]</span></code></pre></figure></p>
<p>The second part of this method uses the reflective capacities of Smalltalk. Drawing the dots of a face is a simple matter of iterating over the collection given by the faceX method for that face, sending the drawDotOn:at: message for each coordinate. To call the correct faceX method, we use the perform: method which sends a message built from a string, here (’face’, dieValue asString) asSymbol. You will encounter this use of perform: quite regularly.</p>
<p><em>Drawing a single dot on a face.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">DieMorph</span> <span class="nf">>></span>
<span class="nf">drawDotOn:</span> <span class="nv">aCanvas</span> <span class="nf">at:</span> <span class="nv">aPoint</span> <span class="nv">aCanvas</span>
<span class="nf">fillOval:</span> <span class="p">(</span><span class="nc">Rectangle</span>
<span class="nf">center:</span> <span class="bp">self</span> <span class="nf">position</span> <span class="nf">+</span> <span class="p">(</span><span class="bp">self</span> <span class="nf">extent</span> <span class="nf">*</span> <span class="nv">aPoint</span><span class="p">)</span>
<span class="nf">extent:</span> <span class="bp">self</span> <span class="nf">extent</span> <span class="nf">/</span> <span class="m">6</span><span class="p">)</span>
<span class="nf">color:</span> <span class="nc">Color</span> <span class="nf">black</span></code></pre></figure></p>
<p>Since the coordinates are normalized to the [0:1] interval, we scale them to the dimensions of our die: self extent * aPoint.</p>
<blockquote><p>We can already create a die instance from a workspace:</p></blockquote>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="p">(</span><span class="nc">DieMorph</span> <span class="nb">new</span> <span class="nf">faces:</span> <span class="m">6</span><span class="p">)</span> <span class="nf">openInWorld</span><span class="p">.</span></code></pre></figure></p>
<p>To change the displayed face, we create an accessor that we can use as myDie dieValue: 4:</p>
<p><em>Setting the current value of the die.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">DieMorph</span> <span class="nf">>></span>
<span class="nf">dieValue:</span> <span class="nv">aNumber</span>
<span class="p">(</span><span class="nv">aNumber</span> <span class="nf">isInteger</span>
<span class="nf">and:</span> <span class="p">[</span><span class="nv">aNumber</span> <span class="nf">></span> <span class="m">0</span><span class="p">]</span>
<span class="nf">and:</span> <span class="p">[</span><span class="nv">aNumber</span> <span class="nf"><=</span> <span class="nv">faces</span><span class="p">])</span>
<span class="nb">ifTrue:</span>
<span class="p">[</span><span class="nv">dieValue</span> <span class="o">:=</span> <span class="nv">aNumber</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">changed</span><span class="p">]</span></code></pre></figure></p>
<p>Now we will use the animation system to show quickly all the faces:</p>
<p><em>Animating the die.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">DieMorph</span> <span class="nf">>></span>
<span class="nf">stepTime</span>
<span class="o">^</span> <span class="m">100</span></p>
<p><span class="nf">DieMorph</span> <span class="nf">>></span>
<span class="nf">step</span>
<span class="nv">isStopped</span> <span class="nb">ifFalse:</span> <span class="p">[</span><span class="bp">self</span> <span class="nf">dieValue:</span> <span class="p">(</span><span class="m">1</span> <span class="nf">to:</span> <span class="nv">faces</span><span class="p">)</span> <span class="nf">atRandom</span><span class="p">]</span></code></pre></figure></p>
<p>Now the die is rolling!</p>
<p>To start or stop the animation by clicking, we will use what we learned previously about mouse events. First, activate the reception of mouse events:</p>
<p><em>Handling mouse clicks to start and stop the animation.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">DieMorph</span> <span class="nf">>></span>
<span class="nf">handlesMouseDown:</span> <span class="nv">anEvent</span>
<span class="o">^</span> <span class="bp">true</span></p>
<p><span class="nf">DieMorph</span> <span class="nf">>></span>
<span class="nf">mouseDown:</span> <span class="nv">anEvent</span>
<span class="nv">anEvent</span> <span class="nf">redButtonPressed</span>
<span class="nb">ifTrue:</span> <span class="p">[</span><span class="nv">isStopped</span> <span class="o">:=</span> <span class="nv">isStopped</span> <span class="nf">not</span><span class="p">]</span></code></pre></figure></p>
<p>Now the die will roll or stop rolling when we click on it.</p>
<h3>More about the canvas</h3>
<hr />
<p>The drawOn: method has an instance of Canvas as its sole argument; the canvas is the area on which the morph draws itself. By using the graphics methods of the canvas you are free to give the appearance you want to a morph. If you browse the inheritance hierarchy of the Canvas class, you will see that it has several variants. The default variant of Canvas is FormCanvas; you will find the key graphics methods in Canvas and FormCanvas. These methods can draw points, lines, polygons, rectangles, ellipses, text, and images with rotation and scaling.</p>
<p>It is also possible to use other kinds of canvas, to obtain transparent morphs, more graphics methods, antialiasing, and so on. To use these features you will need an AlphaBlendingCanvas or a BalloonCanvas. But how can you obtain such a canvas in a drawOn: method, when drawOn: receives an instance of FormCanvas as its argument? Fortunately, you can transform one kind of canvas into another.</p>
<blockquote><p>To use a canvas with a 0.5 alpha-transparency in DieMorph, redefine drawOn: like this:</p></blockquote>
<p><em>Drawing a translucent die.</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">DieMorph</span> <span class="nf">>></span>
<span class="nf">drawOn:</span> <span class="nv">aCanvas</span>
<span class="p">|</span><span class="nv"> theCanvas </span><span class="p">|</span>
<span class="nv">theCanvas</span> <span class="o">:=</span> <span class="nv">aCanvas</span> <span class="nf">asAlphaBlendingCanvas:</span> <span class="m">0.5</span><span class="p">.</span>
<span class="bp">super</span> <span class="nf">drawOn:</span> <span class="nv">theCanvas</span><span class="p">.</span>
<span class="p">(</span><span class="bp">self</span> <span class="nf">perform:</span> <span class="p">(</span><span class="s">'face'</span> <span class="nf">,</span> <span class="nv">dieValue</span> <span class="nf">asString</span><span class="p">)</span> <span class="nf">asSymbol</span><span class="p">)</span>
<span class="nf">do:</span> <span class="p">[</span><span class="o">:</span><span class="nv">aPoint</span> <span class="p">|</span> <span class="bp">self</span> <span class="nf">drawDotOn:</span> <span class="nv">theCanvas</span> <span class="nf">at:</span> <span class="nv">aPoint</span><span class="p">]</span></code></pre></figure></p>
<p>That’s all you need to do!</p>
<p><img src="/assets/img/smalltalk/Figure%2011.9.png" alt="Figure 11.9" /></p>
<p>The die displayed with alpha-transparency.</p>
<h2>Chapter summary</h2>
<hr />
<p>Morphic is a graphical framework in which graphical interface elements can be dynamically composed.</p>
<ul>
<li>You can convert an object into a morph and display that morph on the screen by sending it the messages asMorph openInWorld.</li>
<li>You can manipulate a morph by meta-clicking on it and using the handles that appear. (Handles have help balloons that explain what they do.)</li>
<li>You can compose morphs by embedding one onto another, either by drag and drop or by sending the message addMorph:.</li>
<li>You can subclass an existing morph class and redefine key methods, like initialize and drawOn:.</li>
<li>You can control how a morph reacts to mouse and keyboard events by redefining the methods handlesMouseDown:, handlesMouseOver:, etc.</li>
<li>You can animate a morph by defining the methods step (what to do) and stepTime (the number of milliseconds between steps).
DieMorph»drawOn: aCanvas</li>
<li>theCanvas |
theCanvas := aCanvas asAlphaBlendingCanvas: 0.5. super drawOn: theCanvas.
(self perform: ('face' , dieValue asString) asSymbol)
do: [:aPoint | self drawDotOn: theCanvas at: aPoint]</li>
<li>Various pre-defined morphs, like PopUpMenu and FillInTheBlank, are avail- able for interacting with users.</li>
</ul>
Pharo by Example 102015-05-01T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/05/01/pharo-by-example-10<h2>Streams</h2>
<hr />
<h3>Two sequences of elements</h3>
<hr />
<p><img src="/assets/img/smalltalk/Figure%2010.1.png" alt="Figure 10.1" /></p>
<p>A stream positioned at its beginning.</p>
<p><img src="/assets/img/smalltalk/Figure%2010.2.png" alt="Figure 10.2" /></p>
<p>The same stream after the execution of the method next: the character a is “in the past” whereas b, c, d and e are “in the future”.</p>
<p><img src="/assets/img/smalltalk/Figure%2010.3.png" alt="Figure 10.3" /></p>
<p>The same stream after having written an x.</p>
<h3>Streams vs. collections</h3>
<hr />
<p>The collection protocol supports the storage, removal and enumeration of the elements of a collection, but does not allow these operations to be intermingled. For example, if the elements of an OrderedCollection are processed by a do: method, it is not possible to add or remove elements from inside the do: block. Nor does the collection protocol offer ways to iterate over two collections at the same time, choosing which collection goes forward and which does not. Procedures like these require that a traversal index or position reference is maintained outside of the collection itself: this is exactly the role of ReadStream, WriteStream and ReadWriteStream.</p>
<p>These three classes are defined to stream over some collection. For example, the following snippet creates a stream on an interval, then it reads two elements.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">r</span> <span class="o">:=</span> <span class="nc">ReadStream</span> <span class="nf">on:</span> <span class="p">(</span><span class="m">1</span> <span class="nf">to:</span> <span class="m">1000</span><span class="p">).</span>
<span class="nv">r</span> <span class="nf">next</span><span class="p">.</span> <span class="err">---></span> <span class="m">1</span>
<span class="nf">r</span> <span class="nf">next</span><span class="p">.</span> <span class="err">---></span> <span class="m">2</span>
<span class="nf">r</span> <span class="nf">atEnd</span><span class="p">.</span> <span class="err">---></span> <span class="bp">false</span></code></pre></figure></p>
<p>WriteStreams can write data to the collection:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">w</span> <span class="o">:=</span> <span class="nc">WriteStream</span> <span class="nf">on:</span> <span class="p">(</span><span class="nc">String</span> <span class="nf">new:</span> <span class="m">5</span><span class="p">).</span> <span class="nv">w</span> <span class="nf">nextPut:</span> <span class="sc">$a</span><span class="p">.</span>
<span class="nv">w</span> <span class="nf">nextPut:</span> <span class="sc">$b</span><span class="p">.</span>
<span class="nv">w</span> <span class="nf">contents</span><span class="p">.</span> <span class="err">---></span> <span class="s">'ab'</span></code></pre></figure></p>
<h3>Streaming over collections</h3>
<hr />
<h4>Reading collections</h4>
<p>This section presents features used for reading collections. Using a stream to read a collection essentially provides you a pointer into the collection. That pointer will move forward on reading and you can place it wherever you want. The class ReadStream should be used to read elements from collections.</p>
<p>Methods next and next: are used to retrieve one or more elements from the collection.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">stream</span> <span class="o">:=</span> <span class="nc">ReadStream</span> <span class="nf">on:</span> <span class="ss">#(</span><span class="m">1</span> <span class="err">(</span><span class="ss">a</span> <span class="ss">b</span> <span class="ss">c)</span> <span class="nf">false</span><span class="p">).</span>
<span class="nv">stream</span> <span class="nf">next</span><span class="p">.</span> <span class="err">---></span> <span class="m">1</span>
<span class="nf">stream</span> <span class="nf">next</span><span class="p">.</span> <span class="err">---></span> <span class="ss">#(#a</span> <span class="ss">#b</span> <span class="ss">#c)</span>
<span class="nf">stream</span> <span class="nf">next</span><span class="p">.</span> <span class="err">---></span> <span class="bp">false</span></p>
<p><span class="nf">stream</span> <span class="o">:=</span> <span class="nc">ReadStream</span> <span class="nf">on:</span> <span class="s">'abcdef'</span><span class="p">.</span>
<span class="nv">stream</span> <span class="nf">next:</span> <span class="m">0</span><span class="p">.</span> <span class="err">---></span> <span class="s">''</span>
<span class="nf">stream</span> <span class="nf">next:</span> <span class="m">1</span><span class="p">.</span> <span class="err">---></span> <span class="s">'a'</span>
<span class="nf">stream</span> <span class="nf">next:</span> <span class="m">3</span><span class="p">.</span> <span class="err">---></span> <span class="s">'bcd'</span>
<span class="nf">stream</span> <span class="nf">next:</span> <span class="m">2</span><span class="p">.</span> <span class="err">---></span> <span class="s">'ef'</span></code></pre></figure></p>
<h4>Writing to collections</h4>
<p>We have already seen how to read a collection by iterating over its elements using a ReadStream. We’ll now learn how to create collections using WriteStreams.</p>
<p>WriteStreams are useful for appending a lot of data to a collection at various locations. They are often used to construct strings that are based on static and dynamic parts as in this example:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">stream</span> <span class="o">:=</span> <span class="nc">String</span> <span class="nb">new</span> <span class="nf">writeStream</span><span class="p">.</span>
<span class="nf">stream</span>
<span class="err">nextPutAll:</span> <span class="s">'This Smalltalk image contains: '</span><span class="p">;</span>
<span class="nf">print:</span> <span class="nc">Smalltalk</span> <span class="nf">allClasses</span> <span class="nf">size</span><span class="p">;</span>
<span class="nf">nextPutAll:</span> <span class="s">' classes.'</span><span class="p">;</span>
<span class="nf">cr</span><span class="p">;</span>
<span class="nf">nextPutAll:</span> <span class="s">'This is really a lot.'</span><span class="p">.</span></p>
<p><span class="nv">stream</span> <span class="nf">contents</span><span class="p">.</span> <span class="err">---></span> <span class="s">'This Smalltalk image contains: 2322 classes. This is really a lot.'</span></code></pre></figure></p>
<p><strong>nextPut</strong>: adds the parameter to the stream;
<strong>nextPutAll</strong>: add seach element of the collection. Passed as a parameter, to the stream;
<strong>print</strong>: adds the textual representation of the parameter to the stream.</p>
<p><strong>About Concatenation</strong>. Using nextPut: and nextPutAll: on a WriteStream is often the best way to concatenate characters. Using the comma concatenation operator (,) is far less efficient:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="p">[|</span> <span class="nv">temp</span> <span class="nf">|</span>
<span class="nv">temp</span> <span class="o">:=</span> <span class="nc">String</span> <span class="nb">new</span><span class="p">.</span>
<span class="p">(</span><span class="m">1</span> <span class="nf">to:</span> <span class="m">100000</span><span class="p">)</span>
<span class="nf">do:</span> <span class="p">[</span> <span class="o">:</span><span class="nv">i</span> <span class="p">|</span> <span class="nv">temp</span> <span class="o">:=</span> <span class="nv">temp</span><span class="nf">,</span> <span class="nv">i</span> <span class="nf">asString,</span> <span class="s">''</span><span class="p">]]</span> <span class="nf">timeToRun</span> <span class="nf">---></span> <span class="m">115176</span> <span class="c">"(milliseconds)"</span></p>
<p><span class="p">[|</span> <span class="nv">temp</span> <span class="nf">|</span>
<span class="nv">temp</span> <span class="o">:=</span> <span class="nc">WriteStream</span> <span class="nf">on:</span> <span class="nc">String</span> <span class="nb">new</span><span class="p">.</span>
<span class="p">(</span><span class="m">1</span> <span class="nf">to:</span> <span class="m">100000</span><span class="p">)</span>
<span class="nf">do:</span> <span class="p">[</span> <span class="o">:</span><span class="nv">i</span> <span class="p">|</span> <span class="nv">temp</span> <span class="nf">nextPutAll:</span> <span class="nv">i</span> <span class="nf">asString</span><span class="p">;</span> <span class="nf">space</span><span class="p">].</span>
<span class="nv">temp</span> <span class="nf">contents</span><span class="p">]</span> <span class="nf">timeToRun</span> <span class="nf">---></span> <span class="m">1262</span> <span class="c">"(milliseconds)"</span></code></pre></figure></p>
<h4>Reading and writing at the same time</h4>
<p><img src="/assets/img/smalltalk/Figure%2010.5.png" alt="Figure 10.5" /></p>
<p>Reading and writing at the same time</p>
<p><img src="/assets/img/smalltalk/Figure%2010.9.png" alt="Figure 10.9" /></p>
<h3>Using streams for file access</h3>
<hr />
<h4>Creating file streams</h4>
<p>To create file streams, you will have to use one of the following instance creation methods offered by the class FileStream:</p>
<ul>
<li><p><strong>fileNamed</strong>: Open a file with the given name for reading and writing. If the file already exists, its prior contents may be modified or replaced, but the file will not be truncated on close. If the name has no directory part, then the file will be created in the default directory.</p></li>
<li><p><strong>newFileNamed</strong>: Create a new file with the given name,and answer a stream opened for writing on that file. If the file already exists, ask the user what to do.</p></li>
<li><p><strong>forceNewFileNamed</strong>: Create a new file with the given name, and answer a stream opened for writing on that file. If the file already exists, delete it without asking before creating the new file.</p></li>
<li><p><strong>oldFileNamed</strong>: Open an existing file with the given name for reading and writing. If the file already exists, its prior contents may be modified or replaced, but the file will not be truncated on close. If the name has no directory part, then the file will be created in the default directory.</p></li>
<li><p><strong>readOnlyFileNamed</strong>: Open an existing file with the given name for reading.</p></li>
</ul>
<h4>Binary streams</h4>
<p>By default, created streams are text-based which means you will read and write characters. If your stream must be binary, you have to send the message binary to your stream.</p>
<p>When your stream is in binary mode, you can only write numbers from 0 to 255 (1 Byte). If you want to use nextPutAll: to write more than one number at a time, you have to pass a ByteArray as argument.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nf">FileStream</span>
<span class="err">forceNewFileNamed:</span> <span class="s">'test.bin'</span>
<span class="nf">do:</span> <span class="p">[</span><span class="o">:</span><span class="nv">stream</span> <span class="p">|</span>
<span class="nv">stream</span>
<span class="nf">binary</span><span class="p">;</span>
<span class="nf">nextPutAll:</span> <span class="ss">#(</span><span class="m">145</span> <span class="m">250</span> <span class="m">139</span> <span class="m">98</span><span class="ss">)</span> <span class="nf">asByteArray</span><span class="p">].</span></p>
<p><span class="nf">FileStream</span>
<span class="err">readOnlyFileNamed:</span> <span class="s">'test.bin'</span>
<span class="nf">do:</span> <span class="p">[</span><span class="o">:</span><span class="nv">stream</span> <span class="p">|</span>
<span class="nv">stream</span> <span class="nf">binary</span><span class="p">.</span>
<span class="nv">stream</span> <span class="nf">size</span><span class="p">.</span> <span class="err">---></span> <span class="m">4</span>
<span class="nf">stream</span> <span class="nf">next</span><span class="p">.</span> <span class="err">---></span> <span class="m">145</span>
<span class="nf">stream</span> <span class="nf">upToEnd</span><span class="p">.</span> <span class="err">---></span> <span class="err">#</span><span class="p">[</span><span class="m">250</span> <span class="m">139</span> <span class="m">98</span><span class="p">]</span> <span class="p">].</span></code></pre></figure></p>
<p>Here is another example which creates a picture in a file named “test.pgm” (portable graymap file format). You can open this file with your favorite drawing program.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nf">FileStream</span>
<span class="err">forceNewFileNamed:</span> <span class="s">'test.pgm'</span>
<span class="nf">do:</span> <span class="p">[</span><span class="o">:</span><span class="nv">stream</span> <span class="p">|</span>
<span class="nv">stream</span>
<span class="nf">nextPutAll:</span> <span class="s">'P5'</span><span class="p">;</span> <span class="nf">cr</span><span class="p">;</span>
<span class="nf">nextPutAll:</span> <span class="s">'4 4'</span><span class="p">;</span> <span class="nf">cr</span><span class="p">;</span>
<span class="nf">nextPutAll:</span> <span class="s">'255'</span><span class="p">;</span> <span class="nf">cr</span><span class="p">;</span>
<span class="nf">binary</span><span class="p">;</span>
<span class="nf">nextPutAll:</span> <span class="ss">#(</span><span class="m">255</span> <span class="m">0</span> <span class="m">255</span> <span class="m">0</span><span class="ss">)</span> <span class="nf">asByteArray</span><span class="p">;</span>
<span class="nf">nextPutAll:</span> <span class="ss">#(</span><span class="m">0</span> <span class="m">255</span> <span class="m">0</span> <span class="m">255</span><span class="ss">)</span> <span class="nf">asByteArray</span><span class="p">;</span>
<span class="nf">nextPutAll:</span> <span class="ss">#(</span><span class="m">255</span> <span class="m">0</span> <span class="m">255</span> <span class="m">0</span><span class="ss">)</span> <span class="nf">asByteArray</span><span class="p">;</span>
<span class="nf">nextPutAll:</span> <span class="ss">#(</span><span class="m">0</span> <span class="m">255</span> <span class="m">0</span> <span class="m">255</span><span class="ss">)</span> <span class="nf">asByteArray</span>
<span class="p">]</span></code></pre></figure></p>
<h2>Chapter summary</h2>
<hr />
<p>Streams offer a better way than collections to incrementally read and write a sequence of elements. There are easy ways to convert back and forth between streams and collections.</p>
<ul>
<li>Streams may be either readable, writeable or both readable and writeable.</li>
<li>To convert a collection to a stream, define a stream “on” a collection, e.g., ReadStream on: (1 to: 1000), or send the messages readStream, etc. to the collection.</li>
<li>To convert a stream to a collection, send the message contents.</li>
<li>To concatenate large collections, instead of using the comma operator, it is more efficient to create a stream, append the collections to the stream with nextPutAll:, and extract the result by sending contents.</li>
<li>File streams are by default character-based. Send binary to explicitly make them binary.</li>
</ul>
Pharo by Example 92015-04-30T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/30/pharo-by-example-9<h2>Collections</h2>
<hr />
<h3>Introduction</h3>
<hr />
<p><img src="/assets/img/smalltalk/Figure%209.1.png" title="Some of the key collection classes in Pharo" alt="Figure 9.1" /></p>
<p>Some of the key collection classes in Pharo</p>
<h3>The varieties of collections</h3>
<hr />
<p><img src="/assets/img/smalltalk/Figure%209.2.png" title="Standard Collection protocols" alt="Figure 9.2" /></p>
<p>Standard Collection protocols</p>
<ul>
<li><strong>Sequenceable</strong>: Instances of all subclasses of SequenceableCollection start from a first element and proceed in a well-defined order to a last element. Instances of Set, Bag and Dictionary, on the other hand, are not sequenceable.</li>
<li><strong>Sortable</strong>: A SortedCollection maintains its elements in sort order.</li>
<li><strong>Indexable</strong>: Most sequenceable collections are also indexable, that is, elements can be retrieved with at:. Array is the familiar indexable data structure with a fixed size; anArray at: n retrieves the nth element of anArray, and anArray at: n put: v changes the nth element to v. LinkedLists and SkipList s are sequenceable but not indexable, that is, they understand first and last, but not at:.</li>
<li><strong>Keyed</strong>: Instances of Dictionary and its subclasses are accessed by keys instead of indices.</li>
<li><strong>Mutable</strong>: Most collections are mutable, but Intervals and Symbols are not. An Interval is an immutable collection representing a range of Integers. For example, 5 to: 16 by: 2 is an interval that contains the elements 5, 7, 9, 11, 13 and 15. It is indexable with at:, but cannot be changed with at:put:.</li>
<li><strong>Growable</strong>: Instances of Interval and Array are always of a fixed size.Other kinds of collections (sorted collections, ordered collections, and linked lists) can grow after creation.
The class OrderedCollection is more general than Array; the size of an OrderedCollection grows on demand, and it has methods for addFirst: and addLast: as well as at: and at:put:.</li>
<li><strong>Accepts duplicates</strong>: A Set will filter out duplicates, but a Bag will not. Dictionary, Set and Bag use the = method provided by the elements; the Identity variants of these classes use the == method, which tests whether the arguments are the same object, and the Pluggable variants use an arbitrary equivalence relation supplied by the creator of the collection.</li>
<li><strong>Heterogeneous</strong>: Most collections will hold any kind of element.AString , CharacterArray or Symbol, however, only holds Characters. An Array will hold any mix of objects, but a ByteArray only holds Bytes, an IntegerArray only holds Integers and a FloatArray only holds Floats. A LinkedList is constrained to hold elements that conform to the Link |> accessing protocol.</li>
</ul>
<h3>Implementations of collections</h3>
<hr />
<p>| Arrayed Implementation | Ordered Implementation | Hashed Implementation | Linked Implementation | Interval Implementation |
|---+---+---+---+---|
| Array String Symbol | OrderedCollection SortedCollection Text Heap | Set IdentitySet PluggableSet Bag IdentityBag Dictionary IdentityDictionary PluggableDictionary | LinkedList SkipList | Interval |</p>
<p>Some collection classes categorized by implementation technique</p>
<ol>
<li>Arrays store their elements in the (indexable) instance variables of the collection object itself; as a consequence, arrays must be of a fixed size, but can be created with a single memory allocation.</li>
<li>OrderedCollections and SortedCollections store their elements in an array that is referenced by one of the instance variables of the collection. Consequently, the internal array can be replaced with a larger one if the collection grows beyond its storage capacity.</li>
<li>The various kinds of set and dictionary also reference a subsidiary array for storage, but use the array as a hash table. Bags use a subsidiary Dictionary, with the elements of the bag as keys and the number of occurrences as values.</li>
<li>LinkedLists use a standard singly-linked representation.</li>
<li>Intervals are represented by three integers that record the two end points and the step size.</li>
</ol>
<h3>Examples of key classes</h3>
<hr />
<p>We will focus on the most common collection classes: OrderedCollection, Set, SortedCollection, Dictionary, Interval, and Array.</p>
<h4>Common creation protocol.</h4>
<p>There are several ways to create instances of collections. The most generic ones use the methods new: and with:. new: anInteger creates a collection of size anInteger whose elements will all be nil. with: anObject creates a collection and adds anObject to the created collection. Different collections will realize this behaviour differently.
You can create collections with initial elements using the methods with:, with:with: etc. for up to six elements.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Array</span> <span class="nf">with:</span> <span class="m">1</span> <span class="nf">---></span> <span class="ss">#(</span><span class="m">1</span><span class="ss">)</span>
<span class="nf">Array</span> <span class="nf">with:</span> <span class="m">1</span> <span class="nf">with:</span> <span class="m">2</span> <span class="nf">---></span> <span class="ss">#(</span><span class="m">1</span> <span class="m">2</span><span class="ss">)</span>
<span class="nf">Array</span> <span class="nf">with:</span> <span class="m">1</span> <span class="nf">with:</span> <span class="m">2</span> <span class="nf">with:</span> <span class="m">3</span> <span class="nf">---></span> <span class="ss">#(</span><span class="m">1</span> <span class="m">2</span> <span class="m">3</span><span class="ss">)</span>
<span class="nf">Array</span> <span class="nf">with:</span> <span class="m">1</span> <span class="nf">with:</span><span class="m">2</span> <span class="nf">with:</span><span class="m">3</span> <span class="nf">with:</span><span class="m">4</span> <span class="nf">---></span> <span class="ss">#(</span><span class="m">1234</span><span class="ss">)</span>
<span class="nf">Array</span> <span class="nf">with:</span> <span class="m">1</span> <span class="nf">with:</span><span class="m">2</span> <span class="nf">with:</span><span class="m">3</span> <span class="nf">with:</span><span class="m">4</span> <span class="nf">with:</span><span class="m">5</span> <span class="nf">---></span> <span class="ss">#(</span><span class="m">12345</span><span class="ss">)</span>
<span class="nf">Array</span> <span class="nf">with:</span> <span class="m">1</span> <span class="nf">with:</span><span class="m">2</span> <span class="nf">with:</span><span class="m">3</span> <span class="nf">with:</span><span class="m">4</span> <span class="nf">with:</span><span class="m">5</span> <span class="nf">with:</span><span class="m">6</span> <span class="nf">---></span> <span class="ss">#(</span><span class="m">123456</span><span class="ss">)</span></code></pre></figure></p>
<p>You can also use addAll: to add all elements of one kind of collection to another kind:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="p">(</span><span class="m">1</span> <span class="nf">to:</span> <span class="m">5</span><span class="p">)</span> <span class="nf">asOrderedCollection</span> <span class="nf">addAll:</span> <span class="s">'678'</span><span class="p">;</span>
<span class="nf">yourself</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">OrderedCollection</span><span class="p">(</span><span class="m">1</span> <span class="m">2</span> <span class="m">3</span> <span class="m">4</span> <span class="m">5</span> <span class="sc">$6</span> <span class="sc">$7</span> <span class="sc">$8</span><span class="p">)</span></code></pre></figure></p>
<blockquote><p>Take care that addAll: also returns its argument, and not the receiver!</p></blockquote>
<h4>Array</h4>
<p>An Array is a fixed-sized collection of elements accessed by integer indices. <strong>Contrary to the C convention, the first element of a Smalltalk array is at position 1 and not 0</strong>.
The main protocol to access array elements is the method at: and at:put:. at: anInteger returns the element at index anInteger. at:
anInteger put: anObject puts anObject at index anInteger. Arrays are fixed-size collections therefore we cannot add or remove elements at the end of an array. The following code creates an array of size 5, puts values in the first 3 locations and returns the first element.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="err">anArray</span><span class="o">:=</span> <span class="nc">Array</span> <span class="nf">new:</span> <span class="m">5</span><span class="p">.</span>
<span class="nv">anArray</span> <span class="nf">at:</span> <span class="m">1</span> <span class="nf">put:</span> <span class="m">4</span><span class="p">.</span>
<span class="nv">anArray</span> <span class="nf">at:</span> <span class="m">2</span> <span class="nf">put:</span> <span class="m">3</span><span class="nf">/</span><span class="m">2</span><span class="p">.</span>
<span class="nv">anArray</span> <span class="nf">at:</span> <span class="m">3</span> <span class="nf">put:</span> <span class="s">'ssss'</span><span class="p">.</span>
<span class="nv">anArray</span> <span class="nf">at:</span> <span class="m">1</span> <span class="nf">---></span> <span class="m">4</span></code></pre></figure></p>
<p>There are several ways to create instances of the class Array. We can use new:, with:, and the constructs #( ) and { }.</p>
<p><strong>Creation with new</strong>: new: anInteger creates an array of size anInteger. Array new: 5 creates an array of size 5.</p>
<p><strong>Creation with with</strong>: with: methods allows one to specify the value of the elements. The following code creates an array of three elements consisting of the number 4, the fraction 3/2 and the string 'lulu'.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Array</span> <span class="nf">with:</span> <span class="m">4</span> <span class="nf">with:</span> <span class="m">3</span><span class="nf">/</span><span class="m">2</span> <span class="nf">with:</span> <span class="s">'lulu'</span> <span class="nf">---></span> <span class="p">{</span><span class="m">4</span> <span class="p">.</span> <span class="p">(</span><span class="m">3</span><span class="nf">/</span><span class="m">2</span><span class="p">)</span> <span class="p">.</span> <span class="s">'lulu'</span><span class="p">}</span></code></pre></figure></p>
<p><strong>Literal creation with #()</strong>. #() creates literal arrays with static (or “literal”) elements that have to be known when the expression is compiled, and not when it is executed. The following code creates an array of size 2 where the first element is the (literal) number 1 and the second the (literal) string 'here'.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="ss">#(</span><span class="m">1</span> <span class="s">'here'</span><span class="ss">)</span> <span class="nf">size</span> <span class="nf">---></span> <span class="m">2</span></code></pre></figure></p>
<p>Now, if you evaluate #(1+2), you do not get an array with a single element 3 but instead you get the array #(1 #+ 2) i.e., with three elements: 1, the symbol #+ and the number 2.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="ss">#(</span><span class="m">1</span><span class="nf">+</span><span class="m">2</span><span class="ss">)</span> <span class="nf">---></span> <span class="ss">#(</span><span class="m">1</span> <span class="ss">#+</span> <span class="m">2</span><span class="ss">)</span></code></pre></figure></p>
<p>This occurs because the construct #() causes the compiler to interpret literally the expressions contained in the array. The expression is scanned and the resulting elements are fed to a new array. Literal arrays contain numbers, nil, true, false, symbols and strings.</p>
<p><strong>Dynamic creation with { }</strong>. Finally, you can create a dynamic array using the construct {}. { a . b } is equivalent to Array with: a with: b. This means in particular that the expressions enclosed by { and } are executed.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="p">{</span><span class="m">1</span><span class="nf">+</span><span class="m">2</span><span class="p">}</span> <span class="nf">---></span> <span class="ss">#(</span><span class="m">3</span><span class="ss">)</span>
<span class="p">{(</span><span class="m">1</span><span class="nf">/</span><span class="m">2</span><span class="p">)</span> <span class="nf">asFloat</span><span class="p">}</span> <span class="nf">at:</span> <span class="m">1</span> <span class="nf">---></span> <span class="m">0.5</span>
<span class="p">{</span><span class="m">10</span> <span class="nf">atRandom</span> <span class="p">.</span> <span class="m">1</span><span class="nf">/</span><span class="m">3</span><span class="p">}</span> <span class="nf">at:</span> <span class="m">2</span> <span class="nf">---></span> <span class="p">(</span><span class="m">1</span><span class="nf">/</span><span class="m">3</span><span class="p">)</span></code></pre></figure></p>
<p><strong>Element Access</strong>. Elements of all sequenceable collections can be accessed with at: and at:put:.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">anArray</span> <span class="o">:=</span> <span class="ss">#(</span><span class="m">1</span> <span class="m">2</span> <span class="m">3</span> <span class="m">4</span> <span class="m">5</span> <span class="m">6</span><span class="ss">)</span> <span class="nf">copy</span><span class="p">.</span>
<span class="nv">anArray</span> <span class="nf">at:</span> <span class="m">3</span> <span class="nf">---></span> <span class="m">3</span>
<span class="nf">anArray</span> <span class="nf">at:</span> <span class="m">3</span> <span class="nf">put:</span> <span class="m">33</span><span class="p">.</span>
<span class="nv">anArray</span> <span class="nf">at:</span> <span class="m">3</span> <span class="nf">---></span> <span class="m">33</span></code></pre></figure></p>
<p>Be careful with code that modifies literal arrays! The compiler tries to allocate space just once for literal arrays. Unless you copy the array, the second time you evaluate the code your “literal” array may not have the value you expect. (Without cloning, the second time around, the literal #(1 2 3 4 5 6) will actually be #(1 2 33 4 5 6)!) Dynamic arrays do not have this problem.</p>
<h4>OrderedCollection</h4>
<hr />
<p>OrderedCollection is one of the collections that can grow, and to which elements can be added sequentially. It offers a variety of methods such as add:, addFirst:, addLast:, and addAll:.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">ordCol</span> <span class="o">:=</span> <span class="nc">OrderedCollection</span> <span class="nb">new</span><span class="p">.</span>
<span class="nv">ordCol</span> <span class="nf">add:</span> <span class="s">'Seaside'</span><span class="p">;</span> <span class="nf">add:</span> <span class="s">'SqueakSource'</span><span class="p">;</span> <span class="nf">addFirst:</span> <span class="s">'Monticello'</span><span class="p">.</span>
<span class="nv">ordCol</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">OrderedCollection</span><span class="p">(</span><span class="s">'Monticello'</span> <span class="s">'Seaside'</span> <span class="s">'SqueakSource'</span><span class="p">)</span></code></pre></figure></p>
<p><strong>Removing Elements</strong>. The method remove: anObject removes the first occurrence of an object from the collection. If the collection does not include such an object, it raises an error.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">ordCol</span> <span class="nf">add:</span> <span class="s">'Monticello'</span><span class="p">.</span>
<span class="nv">ordCol</span> <span class="nf">remove:</span> <span class="s">'Monticello'</span><span class="p">.</span>
<span class="nv">ordCol</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">OrderedCollection</span><span class="p">(</span><span class="s">'Seaside'</span> <span class="s">'SqueakSource'</span> <span class="s">'Monticello'</span><span class="p">)</span></code></pre></figure></p>
<p><strong>Conversion</strong>. It is possible to get an OrderedCollection from an Array (or any other collection) by sending the message asOrderedCollection:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="ss">#(</span><span class="m">1</span> <span class="m">2</span> <span class="m">3</span><span class="ss">)</span> <span class="nf">asOrderedCollection</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">OrderedCollection</span><span class="p">(</span><span class="m">1</span> <span class="m">2</span> <span class="m">3</span><span class="p">)</span>
<span class="s">'hello'</span> <span class="nf">asOrderedCollection</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">OrderedCollection</span><span class="p">(</span><span class="sc">$h</span> <span class="sc">$e</span> <span class="sc">$l</span> <span class="sc">$l</span> <span class="sc">$o</span><span class="p">)</span></code></pre></figure></p>
<h4>Interval</h4>
<p>The class Interval represents ranges of numbers. For example, the interval of numbers from 1 to 100 is defined as follows:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Interval</span> <span class="nf">from:</span> <span class="m">1</span> <span class="nf">to:</span> <span class="m">100</span> <span class="nf">---></span> <span class="p">(</span><span class="m">1</span> <span class="nf">to:</span> <span class="m">100</span><span class="p">)</span></code></pre></figure></p>
<p>The printString of this interval reveals that the class Number provides us with a convenience method called to: to generate intervals:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="p">(</span><span class="nc">Interval</span> <span class="nf">from:</span> <span class="m">1</span> <span class="nf">to:</span> <span class="m">100</span><span class="p">)</span> <span class="nf">=</span> <span class="p">(</span><span class="m">1</span> <span class="nf">to:</span> <span class="m">100</span><span class="p">)</span> <span class="nf">---></span> <span class="bp">true</span></code></pre></figure></p>
<p>We can use Interval class»from:to:by: or Number»to:by: to specify the step between two numbers as follow:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="p">(</span><span class="nc">Interval</span> <span class="nf">from:</span> <span class="m">1</span> <span class="nf">to:</span> <span class="m">100</span> <span class="nf">by:</span> <span class="m">0.5</span><span class="p">)</span> <span class="nf">size</span> <span class="nf">---></span> <span class="m">199</span>
<span class="p">(</span><span class="m">1</span> <span class="nf">to:</span> <span class="m">100</span> <span class="nf">by:</span> <span class="m">0.5</span><span class="p">)</span> <span class="nf">at:</span> <span class="m">198</span> <span class="nf">---></span> <span class="m">99.5</span>
<span class="p">(</span><span class="m">1</span><span class="nf">/</span><span class="m">2</span> <span class="nf">to:</span> <span class="m">54</span><span class="nf">/</span><span class="m">7</span> <span class="nf">by:</span> <span class="m">1</span><span class="nf">/</span><span class="m">3</span><span class="p">)</span> <span class="nf">last</span> <span class="nf">---></span> <span class="p">(</span><span class="m">15</span><span class="nf">/</span><span class="m">2</span><span class="p">)</span></code></pre></figure></p>
<h4>Dictionary</h4>
<p>Dictionaries are important collections whose elements are accessed using keys. Among the most commonly used messages of dictionary you will find at:, at:put:, at:ifAbsent:, keys and values.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">colors</span> <span class="o">:=</span> <span class="nc">Dictionary</span> <span class="nb">new</span><span class="p">.</span>
<span class="nv">colors</span> <span class="nf">at:</span> <span class="ss">#yellow</span> <span class="nf">put:</span> <span class="nc">Color</span> <span class="nf">yellow</span><span class="p">.</span>
<span class="nv">colors</span> <span class="nf">at:</span> <span class="ss">#blue</span> <span class="nf">put:</span> <span class="nc">Color</span> <span class="nf">blue</span><span class="p">.</span>
<span class="nv">colors</span> <span class="nf">at:</span> <span class="ss">#red</span> <span class="nf">put:</span> <span class="nc">Color</span> <span class="nf">red</span><span class="p">.</span></p>
<p><span class="nv">colors</span> <span class="nf">at:</span> <span class="ss">#yellow</span> <span class="nf">---></span> <span class="nc">Color</span> <span class="nf">yellow</span>
<span class="nf">colors</span> <span class="nf">keys</span> <span class="nf">---></span> <span class="nv">a</span> <span class="nf">Set</span><span class="p">(</span><span class="ss">#blue</span> <span class="ss">#yellow</span> <span class="ss">#red</span><span class="p">)</span>
<span class="nf">colors</span> <span class="nf">values</span> <span class="nf">---></span> <span class="p">{</span><span class="nc">Color</span> <span class="nf">blue</span> <span class="p">.</span> <span class="nc">Color</span> <span class="nf">yellow</span> <span class="p">.</span> <span class="nc">Color</span> <span class="nf">red</span><span class="p">}</span></code></pre></figure></p>
<h4>IdentityDictionary</h4>
<p>While a dictionary uses the result of the messages = and hash to determine if two keys are the same, the class IdentityDictionary uses the identity (message ==) of the key instead of its values, i.e., it considers two keys to be equal only if they are the same object.</p>
<h4>Set</h4>
<p>The class Set is a collection which behaves as a mathematical set, i.e., as a collection with no duplicate elements and without any order. In a Set elements are added using the message add: and they cannot be accessed using the message at:. Objects put in a set should implement the methods hash and =.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">s</span> <span class="o">:=</span> <span class="nc">Set</span> <span class="nb">new</span><span class="p">.</span>
<span class="nv">s</span> <span class="nf">add:</span> <span class="m">4</span><span class="nf">/</span><span class="m">2</span><span class="p">;</span> <span class="nf">add:</span> <span class="m">4</span><span class="p">;</span> <span class="nf">add:</span><span class="m">2</span><span class="p">.</span> <span class="nv">ssize</span> <span class="nf">---></span> <span class="m">2</span></code></pre></figure></p>
<h4>SortedCollection</h4>
<p>In contrast to an OrderedCollection, a SortedCollection maintains its elements in sort order. By default, a sorted collection uses the message <= to establish sort order, so it can sort instances of subclasses of the abstract class Magnitude, which defines the protocol of comparable objects (<, =, >, >=, between:and:...).</p>
<p>You can create a SortedCollection by creating a new instance and adding elements to it:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">SortedCollection</span> <span class="nb">new</span> <span class="nf">add:</span> <span class="m">5</span><span class="p">;</span> <span class="nf">add:</span> <span class="m">2</span><span class="p">;</span> <span class="nf">add:</span> <span class="m">50</span><span class="p">;</span> <span class="nf">add:</span> <span class="err">−</span><span class="m">10</span><span class="p">;</span> <span class="nf">yourself</span><span class="p">.</span>
<span class="err">---></span> <span class="nv">a</span> <span class="nf">SortedCollection</span><span class="p">(</span><span class="err">−</span><span class="m">10</span> <span class="m">2</span> <span class="m">5</span> <span class="m">50</span><span class="p">)</span></code></pre></figure></p>
<blockquote><p><strong>FAQ</strong>: How do you sort a collection?</p>
<p><strong>ANSWER</strong>: Send the message asSortedCollection to it.</p></blockquote>
<h4>String</h4>
<p>A Smalltalk String represents a collection of Characters. It is sequenceable, indexable, mutable and homogeneous, containing only Character instances. Like Arrays, Strings have a dedicated syntax, and are normally created by directly specifying a String literal within single quotes, but the usual collection creation methods will work as well.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="s">'Hello'</span> <span class="nf">---></span> <span class="s">'Hello'</span>
<span class="nf">String</span> <span class="nf">with:</span> <span class="sc">$A</span> <span class="nf">---></span> <span class="s">'A'</span>
<span class="nf">String</span> <span class="nf">with:</span> <span class="sc">$h</span> <span class="nf">with:</span> <span class="sc">$i</span> <span class="nf">with:</span> <span class="sc">$!</span> <span class="nf">---></span> <span class="s">'hi!'</span>
<span class="nf">String</span> <span class="nf">newFrom:</span> <span class="ss">#(</span><span class="sc">$h</span> <span class="err">"</span> <span class="sc">$l</span> <span class="sc">$l</span> <span class="sc">$o</span><span class="ss">)</span> <span class="nf">---></span> <span class="s">'hello'</span></code></pre></figure></p>
<p><strong>String matching</strong>. It is possible to ask whether a pattern matches a string by sending the match: message. The pattern can specify * to match an arbitrary series of characters and # to match a single character. Note that match: is sent to the pattern and not the string to be matched.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="s">'GNU/Linux mag'</span> <span class="nf">findString:</span> <span class="s">'Linux'</span> <span class="nf">---></span> <span class="bp">true</span>
<span class="s">'GNU/Linux #ag'</span> <span class="nf">match:</span> <span class="s">'GNU/Linux tag'</span> <span class="nf">---></span> <span class="bp">true</span>
<span class="nf">Another</span> <span class="nf">useful</span> <span class="nf">method</span> <span class="nf">is</span> <span class="nf">findString:</span><span class="err">.</span></code></pre></figure></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="s">'GNU/Linux mag'</span> <span class="nf">findString:</span> <span class="s">'Linux'</span> <span class="nf">---></span> <span class="m">5</span>
<span class="s">'GNU/Linux mag'</span> <span class="nf">findString:</span> <span class="s">'linux'</span> <span class="nf">startingAt:</span> <span class="m">1</span> <span class="nf">caseSensitive:</span> <span class="bp">false</span> <span class="nf">---></span> <span class="m">5</span></code></pre></figure></p>
<p><strong>Some tests on strings</strong>. The following examples illustrate the use of isEmpty, includes: and anySatisfy: which are further messages defined not only on Strings but more generally on collections.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="s">'Hello'</span> <span class="nf">isEmpty</span> <span class="nf">---></span> <span class="bp">false</span>
<span class="s">'Hello'</span> <span class="nf">includes:</span> <span class="sc">$a</span> <span class="nf">---></span> <span class="bp">false</span>
<span class="s">'JOE'</span> <span class="nf">anySatisfy:</span> <span class="p">[</span><span class="o">:</span><span class="nv">c</span> <span class="p">|</span> <span class="nv">c</span> <span class="nf">isLowercase</span><span class="p">]</span> <span class="nf">---></span> <span class="bp">false</span>
<span class="s">'Joe'</span> <span class="nf">anySatisfy:</span> <span class="p">[</span><span class="o">:</span><span class="nv">c</span> <span class="p">|</span> <span class="nv">c</span> <span class="nf">isLowercase</span><span class="p">]</span> <span class="nf">---></span> <span class="bp">true</span></code></pre></figure></p>
<p><strong>String templating</strong>. There are three messages that are useful to manage string templating: format:, expandMacros and expandMacrosWith:.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="s">'{1} is {2}'</span> <span class="nf">format:</span> <span class="p">{</span><span class="s">'Pharo'</span> <span class="p">.</span> <span class="s">'cool'</span><span class="p">}</span> <span class="nf">---></span> <span class="s">'Pharo is cool'</span></code></pre></figure></p>
<p><strong>Some other utility methods</strong>. The class String offers numerous other utilities including the messages asLowercase, asUppercase and capitalized.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="s">'XYZ'</span> <span class="nf">asLowercase</span> <span class="nf">---></span> <span class="s">'xyz'</span>
<span class="s">'xyz'</span> <span class="nf">asUppercase</span> <span class="nf">---></span> <span class="s">'XYZ'</span>
<span class="s">'hilaire'</span> <span class="nf">capitalized</span> <span class="nf">---></span> <span class="s">'Hilaire'</span>
<span class="s">'1.54'</span> <span class="nf">asNumber</span> <span class="nf">---></span> <span class="m">1.54</span>
<span class="s">'this sentence is without a doubt far too long'</span> <span class="nf">contractTo:</span> <span class="m">20</span> <span class="nf">=</span><span class="err"≯æ</span> <span class="s">'this sent...too long'</span></code></pre></figure></p>
<p>Note that there is generally a difference between asking an object its string representation by sending the message printString and converting it to a string by sending the message asString. Here is an example of the difference.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="ss">#ASymbol</span> <span class="nf">printString</span> <span class="nf">---></span> <span class="s">'#ASymbol'</span>
<span class="ss">#ASymbol</span> <span class="nf">asString</span> <span class="nf">---></span> <span class="s">'ASymbol'</span></code></pre></figure></p>
<blockquote><p>A symbol is similar to a string but is guaranteed to be globally unique. For this reason symbols are preferred to strings as keys for dictionaries, in particular for instances of IdentityDictionary.</p></blockquote>
<h3>Collection iterators</h3>
<hr />
<h4>Iterating (do:)</h4>
<p>The method do: is the basic collection iterator. It applies its argument (a block taking a single argument) to each element of the receiver. The following example prints all the strings contained in the receiver to the transcript.</p>
<p><code>#('bob' 'joe' 'toto') do: [:each | Transcript show: each; cr].</code></p>
<p><strong>Variants</strong>. There are a lot of variants of do:, such as do:without:, doWithIndex: and reverseDo:: For the indexed collections (Array, OrderedCollection, SortedCollection) the method doWithIndex: also gives access to the current index. This method is related to to:do: which is defined in class Number.</p>
<p><code>#('bob' 'joe' 'toto') doWithIndex: [:each :i | (each = 'joe') ifTrue: [ ø i ] ] ---> 2</code></p>
<p>For ordered collections, reverseDo: walks the collection in the reverse order.</p>
<p>The following code shows an interesting message: do:separatedBy: which executes the second block only in between two elements.</p>
<pre><code>res := ''.
#('bob' 'joe' 'toto') do: [:e | res := res, e ] separatedBy: [res := res, '.'].
res ---> 'bob.joe.toto'
</code></pre>
<p>Note that this code is not especially efficient since it creates intermediate strings and it would be better to use a write stream to buffer the result:
<code>
String streamContents: [:stream | #('bob' 'joe' 'toto') asStringOn: stream delimiter: '.' ]
---> 'bob.joe.toto'
</code></p>
<p><strong>Dictionaries</strong>. When the message do: is sent to a dictionary, the elements taken into account are the values, not the associations. The proper methods to use are keysDo:, valuesDo:, and associationsDo:, which iterate respectively on keys, values or associations.</p>
<h4>Collecting results (collect:)</h4>
<p>If you want to process the elements of a collection and produce a new collection as a result, rather than using do:, you are probably better off using collect:, or one of the other iterator methods. Most of these can be found in the enumerating protocol of Collection and its subclasses.</p>
<p>Imagine that we want a collection containing the doubles of the elements in another collection. Using the method do: we must write the following:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">double</span> <span class="o">:=</span> <span class="nc">OrderedCollection</span> <span class="nb">new</span><span class="p">.</span>
<span class="ss">#(</span><span class="m">1</span> <span class="m">2</span> <span class="m">3</span> <span class="m">4</span> <span class="m">5</span> <span class="m">6</span><span class="ss">)</span> <span class="nf">do:</span> <span class="p">[</span><span class="o">:</span><span class="nv">e</span> <span class="p">|</span> <span class="nv">double</span> <span class="nf">add:</span> <span class="m">2</span> <span class="nf">*</span> <span class="nv">e</span><span class="p">].</span>
<span class="nv">double</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">OrderedCollection</span><span class="p">(</span><span class="m">2</span> <span class="m">4</span> <span class="m">6</span> <span class="m">8</span> <span class="m">10</span> <span class="m">12</span><span class="p">)</span></code></pre></figure></p>
<h4>Selecting and rejecting elements</h4>
<p>select: returns the elements of the receiver that satisfy a particular condition:
<code>(2 to: 20) select: [:each | each isPrime] ≠æ #(2 3 5 7 11 13 17 19)</code>
reject: does the opposite:
<code>(2to:20)reject:[:each|eachisPrime] ---> #(468910121415161820)</code></p>
<h4>Identifying an element with detect:</h4>
<p>The method detect: returns the first element of the receiver that matches block argument.</p>
<p><code>'through' detect: [:each | each isVowel] ---> $o</code></p>
<p>The method detect:ifNone: is a variant of the method detect:. Its second block is evaluated when there is no element matching the block.</p>
<p><code>Smalltalk allClasses detect: [:each | '*cobol*' match: each asString] ifNone: [ nil ] ---> nil</code></p>
<h4>Accumulating results with inject:into:</h4>
<p>Functional programming languages often provide a higher-order function called fold or reduce to accumulate a result by applying some binary operator iteratively over all elements of a collection. In Pharo this is done by Collection» inject:into:.</p>
<p>The first argument is an initial value, and the second argument is a two- argument block which is applied to the result this far, and each element in turn.</p>
<p>A trivial application of inject:into: is to produce the sum of a collection of numbers. Following Gauss, in Pharo we could write this expression to sum the first 100 integers:</p>
<p><code>(1 to: 100) inject: 0 into: [:sum :each | sum + each ] ---> 5050</code></p>
<h4>Other messages</h4>
<p><strong>count</strong>: The message count: returns the number of elements satisfying a condition. The condition is represented as a boolean block.</p>
<p><code>Smalltalk allClasses count: [:each | 'Collection*' match: each asString ] ---> 3</code></p>
<p><strong>includes</strong>: The message includes: checks whether the argument is contained in the collection.</p>
<p><code>colors := {Color white . Color yellow. Color red . Color blue . Color orange}. colors includes: Color blue. ---> true</code></p>
<p><strong>anySatisfy</strong>: The message anySatisfy: answers true if at least one element of the collection satisfies the condition represented by the argument.</p>
<p><code>colors anySatisfy: [:c | c red > 0.5] ---> true</code></p>
<h3>Some hints for using collections</h3>
<hr />
<p>A common mistake with add: The following error is one of the most frequent Smalltalk mistakes.</p>
<p><code>collection := OrderedCollection new add: 1; add: 2. collection ---> 2</code></p>
<p>Here the variable collection does not hold the newly created collection but rather the last number added. This is because the method add: returns the element added and not the receiver.</p>
<p>The following code yields the expected result:</p>
<pre><code>collection := OrderedCollection new. collection add: 1; add: 2.
collection ≠æ an OrderedCollection(1 2)
</code></pre>
<p>You can also use the message yourself to return the receiver of a cascade of messages:</p>
<p><code>collection := OrderedCollection new add: 1; add: 2; yourself ---> an OrderedCollection(1 2)</code>

<strong>Removing an element of the collection you are iterating on</strong>. Another mistake you may make is to remove an element from a collection you are currently iterating over. remove:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">range</span> <span class="o">:=</span> <span class="p">(</span><span class="m">2</span> <span class="nf">to:</span> <span class="m">20</span><span class="p">)</span> <span class="nf">asOrderedCollection</span><span class="p">.</span>
<span class="nv">range</span> <span class="nf">do:</span> <span class="p">[</span><span class="o">:</span><span class="nv">aNumber</span> <span class="p">|</span> <span class="nv">aNumber</span> <span class="nf">isPrime</span> <span class="nb">ifFalse:</span> <span class="p">[</span> <span class="nv">range</span> <span class="nf">remove:</span> <span class="nv">aNumber</span> <span class="p">]</span> <span class="p">].</span>
<span class="nv">range</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">OrderedCollection</span><span class="p">(</span><span class="m">2</span> <span class="m">3</span> <span class="m">5</span> <span class="m">7</span> <span class="m">9</span> <span class="m">11</span> <span class="m">13</span> <span class="m">15</span> <span class="m">17</span> <span class="m">19</span><span class="p">)</span></code></pre></figure></p>
<p>This result is clearly incorrect since 9 and 15 should have been filtered out! The solution is to copy the collection before going over it.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">range</span> <span class="o">:=</span> <span class="p">(</span><span class="m">2</span> <span class="nf">to:</span> <span class="m">20</span><span class="p">)</span> <span class="nf">asOrderedCollection</span><span class="p">.</span>
<span class="nv">range</span> <span class="nf">copy</span> <span class="nf">do:</span> <span class="p">[</span><span class="o">:</span><span class="nv">aNumber</span> <span class="p">|</span> <span class="nv">aNumber</span> <span class="nf">isPrime</span> <span class="nb">ifFalse:</span> <span class="p">[</span> <span class="nv">range</span> <span class="nf">remove:</span> <span class="nv">aNumber</span> <span class="p">]</span> <span class="p">].</span>
<span class="nv">range</span> <span class="nf">---></span> <span class="nv">an</span> <span class="nf">OrderedCollection</span><span class="p">(</span><span class="m">2</span> <span class="m">3</span> <span class="m">5</span> <span class="m">7</span> <span class="m">11</span> <span class="m">13</span> <span class="m">17</span> <span class="m">19</span><span class="p">)</span></code></pre></figure></p>
<p><strong>Redefining both = and hash</strong>. A difficult error to spot is when you redefine = but not hash. The symptoms are that you will lose elements that you put in sets or other strange behaviour. One solution proposed by Kent Beck is to use xor: to redefine hash. Suppose that we want two books to be considered equal if their titles and authors are the same. Then we would redefine not only = but also hash as follows:</p>
<p><em>Redefining = and hash</em></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Book</span> <span class="nf">>></span>
<span class="nf">aBook</span>
<span class="bp">self</span><span class="p">.</span><span class="nv">class</span> <span class="nf">=</span> <span class="nv">aBook</span> <span class="nf">class</span> <span class="nb">ifFalse:</span> <span class="p">[</span><span class="o">^</span> <span class="bp">false</span><span class="p">].</span>
<span class="o">^</span> <span class="nv">title</span> <span class="nf">=</span> <span class="nv">aBook</span> <span class="nf">title</span> <span class="nf">and:</span> <span class="p">[</span> <span class="nv">authors</span> <span class="nf">=</span> <span class="nv">aBook</span> <span class="nf">authors</span><span class="p">]</span></p>
<p><span class="nf">Book</span> <span class="nf">>></span>
<span class="nf">hash</span>
<span class="o">^</span> <span class="nv">title</span> <span class="nf">hash</span> <span class="nf">xor:</span> <span class="nv">authors</span> <span class="nf">hash</span></code></pre></figure></p>
<h2>Chapter summary</h2>
<hr />
<p>The Smalltalk collection hierarchy provides a common vocabulary for uniformly manipulating a variety of different kinds of collections.</p>
<ul>
<li>A key distinction is between SequenceableCollections, which maintain their elements in a given order, Dictionary and its subclasses, which maintain key-to-value associations, and Sets and Bags, which are unordered.</li>
<li>You can convert most collections to another kind of collection by sending them the messages asArray, asOrderedCollection etc..</li>
<li>To sort a collection, send it the message asSortedCollection.</li>
<li>Literal Arrays are created with the special syntax #( ... ). Dynamic Arrays
are created with the syntax { ... }.</li>
<li>A Dictionary compares keys by equality. It is most useful when keys are instances of String. An IdentityDictionary instead uses object identity to compare keys. It is more suitable when Symbols are used as keys, or when mapping object references to values.</li>
<li>Strings also understand the usual collection messages. In addition, a String supports a simple form of pattern-matching. For more advanced application, look instead at the RegEx package.</li>
<li>The basic iteration message is do:. It is useful for imperative code, such as modifying each element of a collection, or sending each element a message.</li>
<li>Instead of using do:, it is more common to use collect:, select:, reject:, includes:, inject:into: and other higher-level messages to process collections in a uniform way.</li>
<li>Never remove an element from a collection you are iterating over. If you must modify it, iterate over a copy instead.</li>
<li>If you override =, remember to override hash as well!</li>
</ul>
Pharo by Example 82015-04-29T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/29/pharo-by-example-8<h2>Basic Classes</h2>
<hr />
<p>Most of the magic of Smalltalk is not in the language but in the class libraries. To program effectively with Smalltalk, you need to learn how the class libraries support the language and environment. The class libraries are entirely written in Smalltalk and can easily be extended since a package may add new functionality to a class even if it does not define this class.</p>
<p>Our goal here is not to present in tedious detail the whole of the Pharo class library, but rather to point out the key classes and methods that you will need to use or override to program effectively. In this chapter we cover the basic classes that you will need for nearly every application: <strong>Object, Number</strong> and its <strong>subclasses, Character, String, Symbol</strong> and <strong>Boolean</strong>.</p>
<h3>Object</h3>
<hr />
<p>The class comment for the Object states:</p>
<blockquote><p><em>Object is the root class for almost all of the other classes in the class hierarchy. The exceptions are ProtoObject (the superclass of Object) and its subclasses. Class Object provides default behaviour common to all normal objects, such as access, copying, comparison, error handling, message sending, and reflection. Also utility messages that all objects should respond to are defined here. Object has no instance variables, nor should any be added. This is due to several classes of objects that inherit from Object that have special implementations (SmallInteger and UndefinedObject for example) or the VM knows about and depends on the structure and layout of certain standard classes.</em></p></blockquote>
<h4>Class membership</h4>
<p>Several methods allow you to query the class of an object.</p>
<p><strong>class</strong>. You can ask any object about its class using the message class.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="m">1</span> <span class="nf">class</span> <span class="nf">---></span> <span class="nc">SmallInteger</span></code></pre></figure></p>
<p>Conversely, you can ask if an object is an instance of a specific class:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="m">1</span> <span class="nf">isMemberOf:</span> <span class="nc">SmallInteger</span>
<span class="nf">---></span> <span class="bp">true</span> <span class="c">"must be precisely this class"</span>
<span class="m">1</span> <span class="nf">isMemberOf:</span> <span class="nc">Integer</span>
<span class="nf">---></span> <span class="bp">false</span>
<span class="m">1</span> <span class="nf">isMemberOf:</span> <span class="nc">Number</span>
<span class="nf">---></span> <span class="bp">false</span>
<span class="m">1</span> <span class="nf">isMemberOf:</span> <span class="nc">Object</span>
<span class="nf">---></span> <span class="bp">false</span></code></pre></figure></p>
<p><strong>isKindOf</strong>: Object»isKindOf: answers whether the receiver’s class is either the same as, or a subclass of the argument class.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="m">1</span> <span class="nf">isKindOf:</span> <span class="nc">SmallInteger</span>
<span class="nf">---></span> <span class="bp">true</span>
<span class="m">1</span> <span class="nf">isKindOf:</span> <span class="nc">Integer</span>
<span class="nf">---></span> <span class="bp">true</span>
<span class="m">1</span> <span class="nf">isKindOf:</span> <span class="nc">Number</span>
<span class="nf">---></span> <span class="bp">true</span>
<span class="m">1</span> <span class="nf">isKindOf:</span> <span class="nc">Object</span>
<span class="nf">---></span> <span class="bp">true</span>
<span class="m">1</span> <span class="nf">isKindOf:</span> <span class="nc">String</span>
<span class="nf">---></span> <span class="bp">false</span>
<span class="m">1</span><span class="nf">/</span><span class="m">3</span> <span class="nf">isKindOf:</span> <span class="nc">Number</span>
<span class="nf">---></span> <span class="bp">true</span>
<span class="m">1</span><span class="nf">/</span><span class="m">3</span> <span class="nf">isKindOf:</span> <span class="nc">Integer</span>
<span class="nf">---></span> <span class="bp">false</span></code></pre></figure></p>
<p>1/3 which is a Fraction is a kind of Number, since the class Number is a superclass of the class Fraction, but 1/3 is not a Integer.</p>
<p><strong>respondsTo</strong>: Object»respondsTo: answers whether the receiver understands the message selector given as an argument.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="m">1</span> <span class="nf">respondsTo:</span> <span class="ss">#,</span>
<span class="nf">---></span> <span class="bp">false</span></code></pre></figure></p>
<p><strong>Normally it is a bad idea to query an object for its class, or to ask it which messages it understands. Instead of making decisions based on the class of object, you should simply send a message to the object and let it <em>decide</em> (i.e., on the basis of its class) how it should behave.</strong></p>
<h4>Copying</h4>
<p>Copying objects introduces some subtle issues. Since instance variables are accessed by reference, a <strong>shallow copy</strong> of an object would share its references to instance variables with the original object:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">a1</span> <span class="o">:=</span> <span class="p">{</span> <span class="p">{</span> <span class="s">'harry'</span> <span class="p">}</span> <span class="p">}.</span>
<span class="nv">a1</span> <span class="nf">---></span> <span class="ss">#(#(</span><span class="s">'harry'</span><span class="ss">))</span>
<span class="nf">a2</span> <span class="o">:=</span> <span class="nv">a1</span> <span class="nf">shallowCopy</span><span class="p">.</span>
<span class="nv">a2</span> <span class="nf">---></span> <span class="ss">#(#(</span><span class="s">'harry'</span><span class="ss">))</span>
<span class="p">(</span><span class="nv">a1</span> <span class="nf">at:</span> <span class="m">1</span><span class="p">)</span> <span class="nf">at:</span> <span class="m">1</span> <span class="nf">put:</span> <span class="s">'sally'</span><span class="p">.</span>
<span class="nv">a1</span> <span class="nf">---></span> <span class="ss">#(#(</span><span class="s">'sally'</span><span class="ss">))</span>
<span class="nf">a2</span> <span class="nf">---></span> <span class="ss">#(#(</span><span class="s">'sally'</span><span class="ss">))</span> <span class="c">"the subarray is shared"</span></code></pre></figure></p>
<p>Object >> <strong>copyTwoLevel</strong> does the obvious thing when a simple shallow copy does not suffice:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">a1</span> <span class="o">:=</span> <span class="p">{</span> <span class="p">{</span> <span class="s">'harry'</span> <span class="p">}</span> <span class="p">}</span> <span class="p">.</span>
<span class="nv">a2</span> <span class="o">:=</span> <span class="nv">a1</span> <span class="nf">copyTwoLevel</span><span class="p">.</span>
<span class="p">(</span><span class="nv">a1</span> <span class="nf">at:</span> <span class="m">1</span><span class="p">)</span> <span class="nf">at:</span> <span class="m">1</span> <span class="nf">put:</span> <span class="s">'sally'</span><span class="p">.</span>
<span class="nv">a1</span> <span class="nf">---></span> <span class="ss">#(#(</span><span class="s">'sally'</span><span class="ss">))</span>
<span class="nf">a2</span> <span class="nf">---></span> <span class="ss">#(#(</span><span class="s">'harry'</span><span class="ss">))</span> <span class="c">"fully independent state"</span></code></pre></figure></p>
<p>Object >> <strong>deepCopy</strong> makes an arbitrarily deep copy of an object.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nv">a1</span> <span class="o">:=</span> <span class="p">{</span> <span class="p">{</span> <span class="p">{</span> <span class="s">'harry'</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">.</span>
<span class="nv">a2</span> <span class="o">:=</span> <span class="nv">a1</span> <span class="nf">deepCopy</span><span class="p">.</span>
<span class="p">(</span><span class="nv">a1</span> <span class="nf">at:</span> <span class="m">1</span><span class="p">)</span> <span class="nf">at:</span> <span class="m">1</span> <span class="nf">put:</span> <span class="s">'sally'</span><span class="p">.</span>
<span class="nv">a1</span> <span class="nf">---></span> <span class="ss">#(#(</span><span class="s">'sally'</span><span class="ss">))</span>
<span class="nf">a2</span> <span class="nf">---></span> <span class="ss">#(#(#(</span><span class="s">'harry'</span><span class="ss">)))</span></code></pre></figure></p>
<p>The problem with deepCopy is that it will not terminate when applied to a mutually recursive structure:</p>
<h4>Debugging</h4>
<p>The most important method here is <strong>halt</strong>. In order to set a breakpoint in a method, simply insert the message send self halt at some point in the body of the method. When this message is sent, execution will be interrupted and a debugger will open to this point in your program. (See Chapter 6 for more details about the debugger.)</p>
<p>The next most important message is <strong>assert:</strong>, which takes a block as its argument. If the block returns true, execution continues. Otherwise an AssertionFailure exception will be raised. If this exception is not otherwise caught, the debugger will open to this point in the execution. assert: is especially useful to support design by contract. The most typical usage is to check non-trivial pre-conditions to public methods of objects.</p>
<blockquote><p>Do not confuse <strong>Object >> assert:</strong> with <strong>TestCase >> assert:</strong>, which occurs in the SUnit testing framework (see Chapter 7). While the former expects a block as its argument1, the latter expects a Boolean. Although both are useful for debugging, they each serve a very different intent.</p></blockquote>
<h4>Error handling</h4>
<p>This protocol contains several methods useful for signaling run-time errors.</p>
<p>Sending self deprecated: <em>anExplanationString</em> signals that the current method should no longer be used, if deprecation has been turned on in the debug protocol of the preference browser. The String argument should offer an alternative.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="m">1</span> <span class="nf">doIfNotNil:</span> <span class="p">[</span> <span class="o">:</span><span class="nv">arg</span> <span class="p">|</span> <span class="nv">arg</span> <span class="nf">printString,</span> <span class="s">' is not nil'</span> <span class="p">]</span>
<span class="nf">---></span> <span class="nc">SmallInteger</span><span class="p">(</span><span class="nc">Object</span><span class="p">)</span> <span class="nf">>></span> <span class="nf">doIfNotNil:</span> <span class="nv">has</span> <span class="nv">been</span> <span class="nf">deprecated</span><span class="p">.</span> <span class="nv">use</span> <span class="nf">ifNotNilDo:</span></code></pre></figure></p>
<p><strong>doesNotUnderstand:</strong> is sent whenever message lookup fails. The default implementation, i.e., Object >> doesNotUnderstand: will trigger the debugger at this point. It may be useful to override doesNotUnderstand: to provide some other behaviour.</p>
<p><strong>Object >> error</strong> and <strong>Object >> error:</strong> are generic methods that can be used to raise exceptions. (Generally it is better to raise your own custom exceptions, so you can distinguish errors arising from your code from those coming from kernel classes.)</p>
<p>Abstract methods in Smalltalk are implemented by convention with the body self subclassResponsibility. Should an abstract class be instantiated by accident, then calls to abstract methods will result in <strong>Object >> subclassResponsibility</strong> being evaluated.</p>
<h4>Testing</h4>
<p>The testing methods have nothing to do with SUnit testing! A testing method is one that lets you ask a question about the state of the receiver and returns a Boolean.</p>
<p>Numerous testing methods are provided by Object. We have already seen <strong>isComplex</strong>. Others include <strong>isArray, isBoolean, isBlock, isCollection and so on</strong>. Generally such methods are to be avoided since querying an object for its class is a form of violation of encapsulation. Instead of testing an object for its class, one should simply send a request and let the object decide how to handle it.</p>
<p>Nevertheless some of these testing methods are undeniably useful. The most useful are probably <strong>ProtoObject >> isNil</strong> and Object»notNil (though the Null Object2 design pattern can obviate the need for even these methods).</p>
<h4>Initialize release</h4>
<p>A final key method that occurs not in Object but in ProtoObject is initialize.</p>
<p>initialize as an empty hook method</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">ProtoObject</span> <span class="nf">>></span>
<span class="nf">initialize</span>
<span class="c">"Subclasses should redefine this method to perform initializations on instance creation"</span></code></pre></figure></p>
<p>The reason this is important is that in Pharo, the default <strong>new</strong> method defined for every class in the system will send <strong>initialize</strong> to newly created instances.</p>
<p>new as a class-side template method</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">Behavior</span> <span class="nf">>></span>
<span class="nf">new</span>
<span class="c">"Answer a new initialized instance of the receiver (which is a class) with no indexable variables. Fail if the class is indexable."</span>
<span class="o">^</span> <span class="bp">self</span> <span class="nf">basicNew</span> <span class="nf">initialize</span></code></pre></figure></p>
<p>This means that simply by overriding the initialize hook method, new instances of your class will automatically be initialized. The initialize method should normally perform a super initialize to establish the class invariant for any inherited instance variables. (Note that this is not the standard behaviour of other Smalltalks.)</p>
<h3>Numbers</h3>
<hr />
<p>Remarkably, numbers in Smalltalk are not primitive data values but true objects. Of course numbers are implemented efficiently in the virtual machine, but the Number hierarchy is as perfectly accessible and extensible as any other portion of the Smalltalk class hierarchy.</p>
<p><img src="/assets/img/smalltalk/Figure%208.1.png" alt="Figure 8.1" />
The Number Hierarchy</p>
<p>Numbers are found in the Kernel-Numbers category. The abstract root of this hierarchy is Magnitude, which represents all kinds of classes supporting comparision operators. Number adds various arithmetic and other operators as mostly abstract methods. Float and Fraction represent, respectively, floating point numbers and fractional values. Integer is also abstract, thus distinguishing between subclasses SmallInteger, LargePositiveInteger and LargeNegativeInteger. For the most part users do not need to be aware of the difference between the three Integer classes, as values are automatically converted as needed.</p>
<h3>Magnitude</h3>
<p><strong>Magnitude</strong> is the parent <em>not only</em> of the <strong>Number</strong> classes, <em>but also</em> of other classes supporting comparison operations, such as <strong>Character, Duration and Timespan</strong>. (Complex numbers are not comparable, and so do not inherit from Number.)</p>
<h3>Number</h3>
<p>Similarly, Number defines +, −, * and / to be abstract, but all other arithmetic operators are generically defined.</p>
<p>All Number objects support various converting operators, such as asFloat and asInteger. There are also numerous shortcut constructor methods, such as i, which converts a Number to an instance of Complex with a zero real component, and others which generate Durations, such as hour, day and week.</p>
<p>Numbers directly support common math functions such as sin, log, raiseTo:, squared, sqrt and so on.</p>
<p>Number»printOn: is implemented in terms of the abstract method Number» printOn:base:. (The default base is 10.)</p>
<p>Testing methods include even, odd, positive and negative. Unsurprisingly Number overrides isNumber. More interesting, isInfinite is defined to return false.</p>
<p>Truncation methods include floor, ceiling, integerPart, fractionPart and so on.</p>
<p>|---+---+---+---|
| 1 + 2.5 | ---> | 3.5 | "Addition of two numbers" |
| 3.4 * 5 | ---> | 17.0 | "Multiplication of two numbers" |
| 8 / 2 | ---> | 4 | "Division of two numbers" |
| 10 − 8.3 | ---> | 1.7 | "Subtraction of two numbers"
| 12 = 11 | ---> | false | "Equality between two numbers" |
| 12 >= 11 | ---> | true | "Test if two numbers are different"
| 12 > 9 | ---> | true | "Greater than" |
| 12 >= 10 | ---> | true | "Greater or equal than" |
| 12 < 10 | ---> | false | "Smaller than" |
| 100@10 | ---> | 100@10 | "Point creation" |</p>
<h4>Float</h4>
<p>Float implements the abstract Number methods for floating point numbers.
More interestingly, Float class (i.e., the class-side of Float) provides methods to return the following constants: e, infinity, nan and pi.</p>
<p>|---+---+---|
| Float pi | ---> | 3.141592653589793 |
| Float infinity | ---> | Infinity |
| Float infinity isInfinite | ---> | true |</p>
<h4>Fraction</h4>
<p>Fractions are represented by instance variables for the numerator and denominator, which should be Integers. Fractions are normally created by Integer division (rather than using the constructor method Fraction»numerator:denominator:):</p>
<p>|---+---+---|
| 6/8 | ---> | (3/4) |
| (6/8) class | ---> | Fraction |</p>
<p>Multiplying a Fraction by an Integer or another Fraction may yield an Integer:</p>
<p>|---+---+---|
| 6/8*4 | ---> | 3 |</p>
<h4>Integer</h4>
<p>Integer is the abstract parent of three concrete integer implementations. In addition to providing concrete implementations of many abstract Number methods, it also adds a few methods specific to integers, such as factorial, atRandom, isPrime, gcd: and many others.</p>
<p>SmallInteger is special in that its instances are represented compactly — instead of being stored as a reference, a SmallInteger is represented directly using the bits that would otherwise be used to hold a reference. The first bit of an object reference indicates whether the object is a SmallInteger or not.</p>
<h3>Characters</h3>
<hr />
<p>Character is defined in the Collections-Strings category as a subclass of Magnitude. Printable characters are represented in Pharo as $\<char>. For example:</p>
<p>|---+---+---|
| $a < $b | ---> | true |</p>
<p>Non-printing characters can be generated by various class methods. Character class»value: takes the Unicode (or ASCII) integer value as argument and returns the corresponding character. The protocol accessing untypeable characters contains a number of convenience constructor methods such as backspace, cr, escape, euro, space, tab, and so on.</p>
<p>Character space = (Character value: Character space asciiValue) ≠æ true</p>
<p>The printOn: method is clever enough to know which of the three ways to generate characters offers the most appropriate representation:</p>
<p>|---+---+---|
| Character value: 1 | ---> | Character home |
| Character value: 2 | ---> | Character value: 2 |
| Character value: 32 | ---> | Character space |
| Character value: 97 | ---> | $a |</p>
<p>Various convenient testing methods are built in: <strong>isAlphaNumeric, isCharacter, isDigit, isLowercase, isVowel, and so on.</strong></p>
<h3>Strings</h3>
<hr />
<p>The String class is also defined in the category Collections-Strings. A String is an indexed Collection that holds only Characters.</p>
<p><img src="/assets/img/smalltalk/Figure%208.2.png" alt="Figure 8.2" /></p>
<p>The String Hierarchy</p>
<p>In fact, String is abstract and Pharo Strings are actually instances of the
concrete class ByteString.</p>
<p>|---+---+---|
| Character value: 97 | ---> | $a |
| 'hello world' class | ---> | ByteString |

The other important subclass of String is Symbol. The key difference is that there is only ever a single instance of Symbol with a given value. (This is sometimes called “the unique instance property”). In contrast, two separately constructed Strings that happen to contain the same sequence of characters will often be different objects.</p>
<p>| 'hel','lo' == 'hello' | ---> | false |
| ('hel','lo') asSymbol == #hello | ---> | true |</p>
<p>Another important difference is that a String is mutable, whereas a Symbol is immutable.</p>
<p>| 'hello' at: 2 put: $u; yourself | ---> | 'hullo' |
| #hello at: 2 put: $u | ---> | error |</p>
<h3>Booleans</h3>
<hr />
<p>The class Boolean offers a fascinating insight into how much of the Smalltalk language has been pushed into the class library. Boolean is the abstract super- class of the Singleton classes True and False.</p>
<p>Most of the behaviour of Booleans can be understood by considering the method ifTrue:ifFalse:, which takes two Blocks as arguments.</p>
<p><img src="/assets/img/smalltalk/Figure%208.3.png" alt="Figure 8.3" /></p>
<p>The Boolean Hierarchy</p>
<p>The method is abstract in Boolean. The implementations in its concrete
subclasses are both trivial:</p>
<p>Implementations of ifTrue:ifFalse:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="nc">True</span> <span class="nf">>></span>
<span class="nf">ifTrue:</span> <span class="nv">trueAlternativeBlock</span> <span class="nf">ifFalse:</span> <span class="nv">falseAlternativeBlock</span><br/>
<span class="o">^</span> <span class="nv">trueAlternativeBlock</span> <span class="nf">value</span></p>
<p><span class="nf">False</span> <span class="nf">>></span>
<span class="nf">ifTrue:</span> <span class="nv">trueAlternativeBlock</span> <span class="nf">ifFalse:</span> <span class="nv">falseAlternativeBlock</span>
<span class="o">^</span> <span class="nv">falseAlternativeBlock</span> <span class="nf">value</span></code></pre></figure></p>
<h2>Chapter summary</h2>
<hr />
<ul>
<li>If you override = then you should override hash as well.</li>
<li>Override postCopy to correctly implement copying for your objects.</li>
<li>Send self halt to set a breakpoint.</li>
<li>Return self subclassResponsibility to make a method abstract.</li>
<li>To give an object a String representation you should override printOn:.</li>
<li>Override the hook method initialize to properly initialize instances.</li>
<li>Number methods automatically convert between Floats, Fractions and Integers.</li>
<li>Fractions truly represent rational numbers rather than floats.</li>
<li>Characters are unique instances.</li>
<li>Strings are mutable; Symbols are not. Take care not to mutate string literals, however!</li>
<li>Symbols are unique; Strings are not.</li>
<li>Strings and Symbols are Collections and therefore support the usual Collection methods.</li>
</ul>
Pharo by Example 72015-04-28T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/28/pharo-by-example-7<h2>SUnit</h2>
<hr />
<h3>Why testing is important</h3>
<hr />
<p>Tests play several roles.</p>
<ul>
<li><strong>First</strong>, they provide documentation of the functionality that they cover.
<strong>Moreover</strong>, the documentation is active: watching the tests pass tells you that the documentation is up-to-date.</li>
<li><strong>Second</strong>, tests help developers to confirm that some changes that they have just made to a package have not broken anything else in the system — and to find the parts that break when that confidence turns out to be misplaced.</li>
<li><strong>Finally</strong>, writing tests at the same time as — or even before — programming
forces you to think about the functionality that you want to design, and how it should appear to the client, rather than about how to implement it.</li>
</ul>
<h3>What makes a good test?</h3>
<hr />
<p>Writing good tests is a skill that can be learned most easily by practicing. Let us look at the properties that tests should have to get a maximum benefit.</p>
<ol>
<li>Tests should be repeatable. You should be able to run a test as often as you want, and always get the same answer.</li>
<li>Tests should run without human intervention. You should even be able to run them during the night.</li>
<li>Tests should tell a story. Each test should cover one aspect of a piece of code. A test should act as a scenario that you or someone else can read to understand a piece of functionality.</li>
<li>Tests should have a change frequency lower than that of the functionality they cover: you do not want to have to change all your tests every time you modify your application. One way to achieve this is to write tests based on the public interfaces of the class that you are testing. It is OK to write a test for a private “helper” method if you feel that the method is complicated enough to need the test, but you should be aware that such a test may have to be changed, or thrown away
entirely, when you think of a better implementation.</li>
</ol>
<h3>SUnit by example</h3>
<hr />
<h4>Step 1: create the test class</h4>
<p>An Example Set Test class</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4</pre></td><td class="code"><pre><span class="nc">TestCase</span> <span class="nf">subclass:</span> <span class="ss">#ExampleSetTest</span>
<span class="nf">instanceVariableNames:</span> <span class="s">'full empty'</span>
<span class="nf">classVariableNames:</span> <span class="s">''</span> <span class="nf">poolDictionaries:</span> <span class="s">''</span>
<span class="nf">category:</span> <span class="s">'MySetTest'</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<h4>Step 2: initialize the test context</h4>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4</pre></td><td class="code"><pre><span class="nc">ExampleSetTest</span> <span class="nf">>></span>
<span class="nf">setUp</span>
<span class="nv">empty</span> <span class="o">:=</span> <span class="nc">Set</span> <span class="nb">new</span><span class="p">.</span>
<span class="nv">full</span> <span class="o">:=</span> <span class="nc">Set</span> <span class="nf">with:</span> <span class="m">5</span> <span class="nf">with:</span> <span class="m">6</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<h4>Step 3: write some test methods</h4>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4</pre></td><td class="code"><pre><span class="nc">ExampleSetTest</span> <span class="nf">>></span>
<span class="nf">testIncludes</span>
<span class="bp">self</span> <span class="nf">assert:</span> <span class="p">(</span><span class="nv">full</span> <span class="nf">includes:</span> <span class="m">5</span><span class="p">).</span>
<span class="bp">self</span> <span class="nf">assert:</span> <span class="p">(</span><span class="nv">full</span> <span class="nf">includes:</span> <span class="m">6</span><span class="p">)</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4
5
6</pre></td><td class="code"><pre><span class="nc">ExampleSetTest</span> <span class="nf">>></span>
<span class="nf">testOccurrences</span>
<span class="bp">self</span> <span class="nf">assert:</span> <span class="p">(</span><span class="nv">empty</span> <span class="nf">occurrencesOf:</span> <span class="m">0</span><span class="p">)</span> <span class="nf">=</span> <span class="m">0</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">assert:</span> <span class="p">(</span><span class="nv">full</span> <span class="nf">occurrencesOf:</span> <span class="m">5</span><span class="p">)</span> <span class="nf">=</span> <span class="m">1</span><span class="p">.</span>
<span class="nv">full</span> <span class="nf">add:</span> <span class="m">5</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">assert:</span> <span class="p">(</span><span class="nv">full</span> <span class="nf">occurrencesOf:</span> <span class="m">5</span><span class="p">)</span> <span class="nf">=</span> <span class="m">1</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4
5</pre></td><td class="code"><pre><span class="nc">ExampleSetTest</span> <span class="nf">>></span>
<span class="nf">testRemove</span>
<span class="nv">full</span> <span class="nf">remove:</span> <span class="m">5</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">assert:</span> <span class="p">(</span><span class="nv">full</span> <span class="nf">includes:</span> <span class="m">6</span><span class="p">).</span>
<span class="bp">self</span> <span class="nf">deny:</span> <span class="p">(</span><span class="nv">full</span> <span class="nf">includes:</span> <span class="m">5</span><span class="p">)</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<h4>Step 4: run the tests</h4>
<p>The easiest way to run the tests is directly from the browser. Simply action- click on the package, class name, or on an individual test method, and select <strong>run the tests (t)</strong>.</p>
<h4>Step 5: interpret the results</h4>
<p>The method assert: , which is defined in the class TestCase, expects a boolean argument, usually the value of a tested expression. When the argument is true, the test passes; when the argument is false, the test fails.</p>
<h3>The SUnit cook book</h3>
<hr />
<h4>Other assertions</h4>
<p>In addition to assert: and deny:, there are several other methods that can be used to make assertions.</p>
<ul>
<li><strong>First</strong>, assert:description: and deny:description: take a second argument which is a message string that can be used to describe the reason for the failure, if it is not obvious from the test itself.</li>
<li><strong>Next</strong>, SUnit provides two additional methods, should:raise: and shouldnt:raise: for testing exception propagation. For example, you would use (self should: aBlock raise: anException) to test that a particular exception is raised during the execution of aBlock.</li>
</ul>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4</pre></td><td class="code"><pre><span class="nc">ExampleSetTest</span> <span class="nf">>></span>
<span class="nf">testIllegal</span>
<span class="bp">self</span> <span class="nf">should:</span> <span class="p">[</span><span class="nv">empty</span> <span class="nf">at:</span> <span class="m">5</span><span class="p">]</span> <span class="nf">raise:</span> <span class="nc">Error</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">should:</span> <span class="p">[</span><span class="nv">empty</span> <span class="nf">at:</span> <span class="m">5</span> <span class="nf">put:</span> <span class="ss">#zork</span><span class="p">]</span> <span class="nf">raise:</span> <span class="nc">Error</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<h4>Running a single test</h4>
<p>Normally, you will run your tests using the Test Runner. If you don’t want to launch the Test Runner from the menu, you can execute TestRunner open as a <strong>print it</strong>.
</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3</pre></td><td class="code"><pre><span class="nc">ExampleSetTest</span> <span class="nf">run:</span> <span class="ss">#testRemove</span>
<span class="nf">---></span>
<span class="m">1</span> <span class="nf">run,</span> <span class="m">1</span> <span class="nf">passed,</span> <span class="m">0</span> <span class="nf">failed,</span> <span class="m">0</span> <span class="nf">errors</span><span class="err"></span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<h4>Running all the tests in a test class</h4>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3</pre></td><td class="code"><pre><span class="nc">ExampleSetTest</span> <span class="nf">suite</span> <span class="nf">run</span>
<span class="nf">---></span>
<span class="m">5</span> <span class="nf">run,</span> <span class="m">5</span> <span class="nf">passed,</span> <span class="m">0</span> <span class="nf">failed,</span> <span class="m">0</span> <span class="nf">errors</span> <span class="nf">Mu</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<h4>Must I subclass TestCase?</h4>
<p>In JUnit you can build a TestSuite from an arbitrary class containing test* methods. In Smalltalk you can do the same but you will then have to create a suite by hand and your class will have to implement all the essential TestCase methods like assert:. We recommend that you not try to do this. The framework is there: use it.</p>
<h3>The SUnit framework</h3>
<hr />
<p>SUnit consists of four main classes: TestCase, TestSuite, TestResult, and TestResource.</p>
<p><img src="/assets/img/smalltalk/Figure%207.3.png" alt="Figure 7.3" /></p>
<p>The four classes representing the core of SUnit</p>
<h4>TestCase</h4>
<p>TestCase is an abstract class that is designed to be subclassed; each of its subclasses represents a group of tests that share a common context (that is, a test suite). Each test is run by creating a new instance of a subclass of TestCase, running setUp, running the test method itself, and then running tearDown.
The context is specified by instance variables of the subclass and by the specialization of the method setUp, which initializes those instance variables. Subclasses of TestCase can also override method tearDown, which is invoked after the execution of each test, and can be used to release any objects allocated during setUp.</p>
<h4>TestSuite</h4>
<p>Instances of the class TestSuite contain a collection of test cases. An instance of TestSuite contains tests, and other test suites. That is, a test suite contains sub- instances of TestCase and TestSuite. Both individual TestCases and TestSuites understand the same protocol, so they can be treated in the same way; for example, both can be run. This is in fact an application of the composite pattern in which TestSuite is the composite and the TestCases are the leaves — see Design Patterns for more information on this pattern2.</p>
<h4>TestResult</h4>
<p>The class TestResult represents the results of a TestSuite execution. It records the number of tests passed, the number of tests failed, and the number of errors signalled.</p>
<h4>TestResource</h4>
<p>One of the important features of a suite of tests is that they should be inde- pendent of each other: the failure of one test should not cause an avalanche of failures of other tests that depend upon it, nor should the order in which the tests are run matter. Performing setUp before each test and tearDown afterwards helps to reinforce this independence.</p>
<h3>Advanced features of SUnit</h3>
<hr />
<p>In addition to TestResource, the current version of SUnit contains <strong> assertion description strings</strong>, <strong>logging support</strong>, and <strong>resumable test failures</strong>.</p>
<h4>Assertion description strings</h4>
<p>The TestCase assertion protocol includes a number of methods that allow the programmer to supply a description of the assertion. The description is a String; if the test case fails, this string will be displayed by the test runner. Of course, this string can be constructed dynamically.</p>
<h4>Logging support</h4>
<p>The description strings described above may also be logged to a Stream such as the Transcript, or a file stream. You can choose whether to log by overriding TestCase»isLogging in your test class; you must also choose where to log by overriding TestCase»failureLog to answer an appropriate stream.</p>
<h4>Continuing after a failure</h4>
<p>SUnit also allows us to specify whether or not a test should continue after a failure. This is a really powerful feature that uses the exception mechanisms offered by Smalltalk.</p>
<h3>The implementation of SUnit</h3>
<hr />
<h4>Running one test</h4>
<p><img src="/assets/img/smalltalk/Figure%207.4.png" alt="Figure 7.4" /></p>
<p>Running one test</p>
<h4>Running a TestSuite</h4>
<h3>Some advice on testing</h3>
<hr />
<ul>
<li><p><strong>Feathers’ Rules</strong> for Unit tests. Michael Feathers, an agile process consultant and author, writes:3
A test is not a unit test if:</p>
<ul>
<li>it talks to the database,</li>
<li>it communicates across the network,</li>
<li>it touches the file system,</li>
<li>it can’t run at the same time as any of your other unit tests, or</li>
<li>you have to do special things to your environment (such as editing config files) to run it.</li>
</ul>
</li>
<li><p><strong>Unit Tests vs. Acceptance Tests.</strong>
Unit tests capture one piece of functionality, and as such make it easier to identify bugs in that functionality. As far as possible try to have unit tests for each method that could possibly fail, and group them per class.
However, for certain deeply recursive or complex setup situations, it is easier to write tests that represent a scenario in the larger application; these are called acceptance tests or functional tests. Tests that break Feathers’ rules may make good acceptance tests. Group acceptance tests according to the functionality that they test. For example, if you are writing a compiler, you might write acceptance tests that make assertions about the code generated for each possible source language statement. Such tests might exercise many classes, and might take a long time to run because they touch the file system.
You can write them using SUnit, but you won’t want to run them each time you make a small change, so they should be separated from the true unit tests.</p></li>
<li><p><strong>Black’s Rule of Testing.</strong>
For every test in the system, you should be able to identify some property for which the test increases your confidence.
It’s obvious that there should be no important property that you are not testing. This rule states the less obvious fact that there should be no test that does not add value to the system by increasing your confidence that a useful property holds.
For example, several tests of the same property do no good. In fact, they do harm in two ways. First, they make it harder to infer the behaviour of the class by reading the tests. Second, because one bug in the code might then break many tests, they make it harder to estimate how many bugs remain in the code. So, have a property in mind when you write a test.</p></li>
</ul>
<h2>Chapter summary</h2>
<hr />
<p>This chapter explained why tests are an important investment in the future of your code. We explained in a step-by-step fashion how to define a few tests for the class Set. Then we gave an overview of the core of the SUnit framework by presenting the classes TestCase, TestResult, TestSuite and TestResources. Finally we looked deep inside SUnit by following the execution of a test and a test suite.</p>
<ul>
<li>To maximize their potential, unit tests should be fast, repeatable, in- dependent of any direct human interaction and cover a single unit of functionality.</li>
<li>Tests for a class called MyClass belong in a class classed MyClassTest, which should be introduced as a subclass of TestCase.</li>
<li>Initialize your test data in a setUp method.</li>
<li>Each test method should start with the word “test”.</li>
<li>Use the TestCase methods assert:, deny: and others to make assertions.</li>
<li>Run tests using the SUnit test runner tool (in the tool bar).</li>
</ul>
Pharo by Example 62015-04-27T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/27/pharo-by-example-6<h2>The Pharo programming environment</h2>
<hr />
<h3>Overview</h3>
<hr />
<ul>
<li>The <strong>Browser</strong> is the central development tool. You will use it to create, define, and organize your classes and methods. Using it you can also navigate through all the library classes: unlike other environments where the source code is stored in separate files, in Smalltalk all classes and methods are contained in the image.</li>
<li>The <strong>Message Names</strong> tool is used to look at all of the methods with a particular selector, or with a selector containing a substring.</li>
<li>The <strong>Method Finder</strong> tool will also let you find methods, but according to what they do as well as what they are called.</li>
<li>The <strong>Monticello Browser</strong> is the starting point for loading code from, and saving code in, Monticello packages.</li>
<li>The <strong>Process Browser</strong> provides a view on all of the processes (threads) executing in Smalltalk.</li>
<li>The <strong>Test Runner</strong> lets you run and debug SUnit tests, and is described in Chapter 7.</li>
<li>The <strong>Transcript</strong> is a window on the Transcript output stream, which is useful for writing log messages and has already been described in Section 1.5.</li>
<li>The <strong>Workspace</strong> is a window into which you can type input. It can be used for any purpose, but is most often used for typing Smalltalk expressions and executing them as do it s. The use of the workspace was also illustrated in Section 1.5.</li>
</ul>
<h2>Chapter summary</h2>
<hr />
<ul>
<li>The standard browser is your main interface for browsing existing cate- gories, classes, method protocols and methods, and for defining new ones. The browser offers several useful buttons to directly jump to senders or implementors of a message, versions of a method, and so on.</li>
<li>There exist several different browsers (such as the OmniBrowser and the Refactoring Browser), and several specialized browsers (such as the hierarchy browser) which provide different views of classes and methods.</li>
<li>From any of the tools,you can highlight the name of a class or a method and immediately jump to a browser by using the keyboard shortcut CMD–b.</li>
<li>You can also browse the Smalltalk system programmatically by sending messages to SystemNavigation default.</li>
<li>Monticello is a tool for exporting, importing, versioning and sharing packages of classes and methods. A Monticello package consists of a category, subcategories, and related methods protocols in other cate- gories.</li>
<li>The inspector and the explorer are two tools that are useful for exploring and interacting with live objects in your image. You can even inspect tools by meta-clicking to bring up their morphic halo and selecting the debug handle .</li>
<li>The debugger is a tool that not only lets you inspect the run-time stack of your program when an error is raised, but it also enables you to interact with all of the objects of your application, including the source code. In many cases you can modify your source code from the debugger and continue executing. The debugger is especially effective as a tool to support test-first development in tandem with SUnit (Chapter 7).</li>
<li>The process browser lets you monitor, query and interact with the pro- cesses current running in your image.</li>
<li>The method finder and them essage names browser are two tools for locating methods. The first is more useful when you are not sure of the name, but you know the expected behaviour. The second offers a more advanced browsing interface when you know at least a fragment of the name.</li>
<li>Change sets are automatically generated logs of all changes to the source code of your image. They have largely been superseded by Monticello as a means to store and exchange versions of your source code, but are still useful, especially for recovering from catastrophic failures, however rare these may be.</li>
<li>The file list browser is a tool for browsing the file system. It also allows you to filein source code from the file system.</li>
<li>In case your image crashes before you could save it or backup your source code with Monticello, you can always recover your most recent changes using a change list browser. You can then select the changes you want to replay and file them into the most recent copy of your image.</li>
</ul>
Pharo by Example 52015-04-26T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/26/pharo-by-example-5<h2>The Smalltalk object model</h2>
<hr />
<h3>The rules of the model</h3>
<hr />
<ul>
<li><strong>Rule 1.</strong> Everything is an object.</li>
<li><strong>Rule 2.</strong> Every object is an instance of a class.</li>
<li><strong>Rule 3.</strong> Every class has a superclass.</li>
<li><strong>Rule 4.</strong> Everything happens by sending messages.</li>
<li><strong>Rule 5.</strong> Method lookup follows the inheritance chain.</li>
</ul>
<h3>Everything is an Object</h3>
<hr />
<blockquote><p>Classes are objects too.</p></blockquote>
<p>Deep in the implementation of Smalltalk, there are three different kinds of objects.<br/>
There are</p>
<pre><code>(1) ordinary objects with instance variables that are passed by references,
</code></pre>
<p>there are</p>
<pre><code>(2) small integers that are passed by value,
</code></pre>
<p>and there are</p>
<pre><code>(3) indexable objects like arrays that hold a contiguous portion of memory. The beauty of Smalltalk is that you normally don’t need to care about the differences between these three kinds of object.
</code></pre>
<h3>Every object is an instance of a class</h3>
<hr />
<p>|---+---+---|
| 1 class | ---> | SmallInteger |
| 20 factorial class | ---> | LargePositiveInteger |
| 'hello' class | ---> | ByteString |
| #(1 2 3) class | ---> | Array |
| (4@5) class | ---> | Point |
| Object new class | ---> | Object|
| | |</p>
<p>A class defines the structure of its instances via instance variables, and the behavior of its instances via methods. Each method has a name, called its selector, which is unique within the class.</p>
<p>Since classes are objects, and every object is an instance of a class, it follows that classes must also be instances of classes. A class whose instances are classes is called a <strong>metaclass</strong>. Whenever you create a class, the system automatically creates a <strong>metaclass</strong> for you. The <strong>metaclass</strong> defines the structure and behavior of the class that is its instance. 99% of the time you will not need to think about metaclasses, and may happily ignore them.</p>
<h4>Instance variables</h4>
<h4>Methods</h4>
<blockquote><p>A class does not have access to the instance variables of its own instances.</p>
<p>An instance of a class does not have access to the class instance variables of its class.</p></blockquote>
<h3>Every class has a superclass</h3>
<hr />
<h4>Abstract methods and abstract classes</h4>
<p>Smalltalk has no dedicated syntax to specify that a method or a class is abstract. By convention, the body of an abstract method consists of the expression <strong>self subclassResponsibility</strong>. This is known as a “marker method”, and indicates that subclasses have the responsibility to define a concrete version of the method. <code>self subclassResponsibility</code> methods should always be overridden, and thus should never be executed. If you forget to override one, and it is executed, an exception will be raised.</p>
<h4>Traits</h4>
<p>A trait is a collection of methods that can be included in the behaviour of a class without the need for inheritance. This makes it easy for classes to have a unique superclass, yet still share useful methods with otherwise unrelated classes.
To define a new trait, simply replace the subclass creation template by a message to the class Trait.</p>
<p>Defining a new trait</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3</pre></td><td class="code"><pre><span class="nc">Trait</span> <span class="nf">named:</span> <span class="ss">#TAuthor</span>
<span class="nf">uses:</span> <span class="p">{</span> <span class="p">}</span>
<span class="nf">category:</span> <span class="s">'PBE−LightsOut'</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<p>Here we define the trait TAuthor in the category PBE-LightsOut. This trait does not use any other existing traits. In general we can specify a <em>trait composition expression</em> of other traits to use as part of the uses: keyword argument. Here we simply provide an empty array.</p>
<p>Traits may contain methods, but no instance variables. Suppose we would like to be able to add an author method to various classes, independent of where they occur in the hierarchy. We might do this as follows:</p>
<p>An author method</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4</pre></td><td class="code"><pre><span class="nc">TAuthor</span> <span class="nf">>></span> <span class="err">author:</span>
<span class="c">"Return author initials"</span>
<span class="c">"oscar nierstrasz"</span>
<span class="o">^</span> <span class="s">'on'</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<p>Using a trait</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4
5</pre></td><td class="code"><pre><span class="nc">BorderedMorph</span> <span class="nf">subclass:</span> <span class="ss">#LOGame</span>
<span class="nf">uses:</span> <span class="nc">TAuthor</span>
<span class="nf">instanceVariableNames:</span> <span class="s">'cells'</span>
<span class="nf">classVariableNames:</span> <span class="s">''</span> <span class="nf">poolDictionaries:</span> <span class="s">''</span>
<span class="nf">category:</span> <span class="s">'PBE−LightsOut'</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<p>If we now instantiate LOGame, it will respond to the author message as expected.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1</pre></td><td class="code"><pre><span class="nc">Game</span> <span class="nb">new</span> <span class="nf">author</span> <span class="nf">-></span> <span class="s">'on'</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<h3>Everything happens by sending messages</h3>
<hr />
<p>This rule captures the essence of programming in Smalltalk.
In procedural programming, the choice of which piece of code to execute when a procedure is called is made by the caller. The caller chooses the procedure or function to execute statically, by name.</p>
<p>In object-oriented programming, we do not “call methods”: we “send messages.” The choice of terminology is significant. Each object has its own responsibilities. We do not tell an object what to do by applying some procedure to it. Instead, we politely ask an object to do something for us by sending it a message. The message is not a piece of code: it is nothing but a name and a list of arguments. The receiver then decides how to respond by selecting its own method for doing what was asked. Since different objects may have different methods for responding to the same message, the method must be chosen dynamically, when the message is received.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2</pre></td><td class="code"><pre><span class="m">3</span> <span class="nf">+</span> <span class="m">4</span> <span class="nf">-></span> <span class="m">7</span> <span class="c">"send message + with argument 4 to integer 3"</span>
<span class="p">(</span><span class="m">1</span><span class="nf">@</span><span class="m">2</span><span class="p">)</span> <span class="nf">+</span> <span class="m">4</span> <span class="nf">-></span> <span class="m">5</span><span class="nf">@</span><span class="m">6</span> <span class="c">"send message + with argument 4 to point (1@2)"</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<p>As a consequence, we can send the same message to different objects, each of which may have its own method for responding to the message. We do not tell the SmallInteger 3 or the Point 1@2 how to respond to the message + 4. Each has its own method for +, and responds to + 4 accordingly.</p>
<p>One of the consequences of Smalltalk’s model of message sending is that it encourages a style in which objects tend to have very small methods and delegate tasks to other objects, rather than implementing huge, procedural methods that assume too much responsibility. Joseph Pelrine expresses this principle succinctly as follows:</p>
<blockquote><p>Don’t do anything that you can push off onto someone else.</p></blockquote>
<p>Many object-oriented languages provide both static and dynamic opera- tions for objects; in Smalltalk there are only dynamic message sends. Instead of providing static class operations, for instance, classes are objects and we simply send messages to classes.</p>
<p><strong>Nearly</strong> everything in Smalltalk happens by sending messages. At some point action must take place:</p>
<ul>
<li><strong>Variable</strong> declarations are not message sends. In fact, variable declarations are not even executable. Declaring a variable just causes space to be allocated for an object reference.</li>
<li><strong>Assignments</strong> are not message sends. An assignment to a variable causes that variable name to be freshly bound in the scope of its definition.</li>
<li><strong>Returns</strong> are not message sends. A return simply causes the computed result to be returned to the sender.</li>
<li><strong>Primitives</strong> are not message sends. They are implemented in the virtual machine.</li>
</ul>
<p>Other than these few exceptions, pretty much everything else does truly happen by sending messages. In particular, since there are no “public fields” in Smalltalk, the only way to update an instance variable of another object is to send it a message asking that it update its own field. Of course, providing setter and getter methods for all the instance variables of an object is not good object-oriented style. Joseph Pelrine also states this very nicely:</p>
<blockquote><p>Don’t let anyone else play with your data.</p></blockquote>
<h3>Method lookup follows the inheritance chain</h3>
<hr />
<ul>
<li>What happens when a method does not explicitly return a value?</li>
<li>What happens when a class reimplements a superclass method?</li>
<li>What is the difference between self and super sends?</li>
<li>What happens when no method is found?</li>
</ul>
<p><img src="/assets/img/smalltalk/Figure%205.2.png" alt="Figure 5.2" /></p>
<p>Method lookup follows the inheritance hierarchy.</p>
<blockquote><p>Return a value only when you intend for the sender to use the value.
</p>
<h4>Overriding and extension</h4>
<p>Super initialize</p></blockquote>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4</pre></td><td class="code"><pre><span class="nc">BorderedMorph</span><span class="err">»</span><span class="nv">initialize</span>
<span class="c">"initialize the state of the receiver"</span>
<span class="nf">super</span> <span class="nf">initialize</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">borderInitialize</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<blockquote><p>An initialize method should always start by sending super</p></blockquote>
<h4>Self sends and super sends</h4>
<p>We need super sends to compose inherited behaviour that would otherwise be overridden. The usual way to compose methods, whether inherited or not, however, is by means of self sends.
How do self sends differ from super sends? Like self, super represents the receiver of the message. The only thing that changes is the method lookup. Instead of lookup starting in the class of the receiver, it starts in the superclass of the class of the method where the super send occurs.
</p>
<h4>Self sends and super sends</h4>
<p><img src="/assets/img/smalltalk/Figure%205.3.png" alt="Figure 5.3" /></p>
<p>self and super sends</p>
<blockquote><p>A self send triggers a dynamic method lookup starting in the class of the receiver.</p>
<p>A super send triggers a static method lookup starting in the superclass of the class of the method performing the super send.</p></blockquote>
<h4>Message not understood</h4>
<p><img src="/assets/img/smalltalk/Figure%205.4.png" alt="Figure 5.4" /></p>
<p>Message foo is not understood</p>
<h3>Shared variables</h3>
<hr />
<p>Smalltalk provides three kinds of shared variables:</p>
<ol>
<li>globally shared variables;</li>
<li>variables shared between instances and classes (class variables), and</li>
<li>variables shared amongst a group of classes (pool variables). The names of all of these shared variables start with a capital letter, to warn us that they are indeed shared between multiple objects.</li>
</ol>
<h4>Global variables</h4>
<p>In Pharo, all global variables are stored in a namespace called Smalltalk, which is implemented as an instance of the class SystemDictionary. Global variables are accessible everywhere. Every class is named by a global variable; in addition, a few globals are used to name special or commonly useful objects.
The variable Transcript names an instance of TranscriptStream, a stream that writes to a scrolling window. The following code displays some information and then goes to the next line in the Transcript.</p>
<ul>
<li><strong>Smalltalk</strong> is theinstance of SystemDictionary that defines all of the globals— including Smalltalk itself. The keys to this dictionary are the symbols that name the global objects in Smalltalk code.</li>
<li><strong>Sensor</strong> is an instance of EventSensor, and represents input to Pharo. For example, Sensor keyboard answers the next character input on the key- board, and Sensor leftShiftDown answers true if the left shift key is being held down, while Sensor mousePoint answers a Point indicating the cur- rent mouse location.</li>
<li><strong>World</strong> is an instance of PasteUpMorph that represents the screen. World bounds answers a rectangle that defines the whole screen space; all
Morphs on the screen are submorphs of World.</li>
<li><strong>ActiveHand</strong> is the current instance of HandMorph, the graphical representa- tion of the cursor. ActiveHand’s submorphs hold anything being dragged by the mouse.</li>
<li><strong>Undeclared</strong> is another dictionary — it contains all the undeclared vari- ables. If you write a method that references an undeclared variable, the browser will normally prompt you to declare it, for example, as a global or as an instance variable of the class. However, if you later delete the declaration, the code will then reference an undeclared variable. Inspecting Undeclared can sometimes help explain strange behaviour!</li>
<li><strong>SystemOrganization</strong> is an instance of SystemOrganizer: it records the organi- zation of classes into packages. More precisely, it categorizes the names of classes.</li>
</ul>
<h4>Class variables</h4>
<p><img src="/assets/img/smalltalk/Figure%205.5.png" alt="Figure 5.5" /></p>
<p>Instance and class methods accessing different variables.</p>
<h4>Pool variables</h4>
<blockquote><p>Once again, we recommend that you avoid the use of pool variables and pool dictionaries.</p></blockquote>
<h2>Chapter summary</h2>
<hr />
<p>The object model of Pharo is both simple and uniform. Everything is an object, and pretty much everything happens by sending messages.</p>
<ul>
<li>Everything is an object. Primitive entities like integers are objects, but also classes are first-class objects.</li>
<li>Every object is an instance of a class. Classes define the structure of their instances via private instance variables and the behaviour of their instances via public methods. Each class is the unique instance of its metaclass. Class variables are private variables shared by the class and all the instances of the class. Classes cannot directly access instance vari- ables of their instances, and instances cannot access instance variables of their class. Accessors must be defined if this is needed.</li>
<li>Every class has a superclass. The root of the single inheritance hier- archy is ProtoObject. Classes you define, however, should normally inherit from Object or its subclasses. There is no syntax for defining abstract classes. An abstract class is simply a class with an abstract method — one whose implementation consists of the expression self subclassResponsibility. Although Pharo supports only single inheritance, it is easy to share implementations of methods by packaging them as traits. * Everything happens by sending messages. We do not “call methods”, we “send messages”. The receiver then chooses its own method for responding to the message.</li>
<li>Method lookup follows the inheritance chain; self sends are dynamic and start the method lookup again in the class of the receiver, whereas super sends are static, and start in the superclass of class in which the super send is written.</li>
<li>There are three kinds of shared variables. Global variables are accessible everywhere in the system. Class variables are shared between a class, its subclasses and its instances. Pool variables are shared between a selected set of classes. You should avoid shared variables as much as possible.</li>
</ul>
Pharo by Example 42015-04-25T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/25/pharo-by-example-4<h2>Understanding message syntax</h2>
<hr />
<h3>Identifying messages</h3>
<hr />
<ul>
<li>A message is composed of the message <em>selector</em> and the optional message arguments.</li>
<li>A message is sent to a receiver.</li>
<li>The combination of a message and its receiver is called a message send
as shown in Figure 4.1.</li>
</ul>
<p><img src="/assets/img/smalltalk/Figure%204.1.png" alt="Figure 4.1" /></p>
<p>Figure 4.1: Two messages composed of a receiver, a method selector, and a set of arguments.</p>
<blockquote><p>A message is always sent to a receiver, which can be a single literal, a block or a variable or the result of evaluating another message.</p></blockquote>
<h2>Three kinds of messages</h2>
<hr />
<ul>
<li><strong>Unary messages</strong> are messages that are sent to an object without any other information. For example in <strong>3 factorial</strong>, <em>factorial</em> is a unary message.</li>
</ul>
<blockquote><p>Unary messages are messages that do not require any argument.
They follow the syntactic template: receiver selector</p></blockquote>
<ul>
<li><strong>Binary messages</strong> are messages consisting of operators (often arithmetic). They are binary because they always involve only two objects: the receiver and the argument object. For example in 10 + 20, + is a binary message sent to the receiver 10 with argument 20.</li>
</ul>
<blockquote><p>Binary messages are messages that require exactly one argument and whose selector is composed of a sequence of characters from: +, −, *, /, &, =, >, |, <, ≥, and @. −− is not possible.
They follow the syntactic template: receiver selector argument.</p></blockquote>
<ul>
<li><strong>Keyword</strong> messages are message sconsisting of one or more keywords,each ending with a colon (:) and taking an argument. For example in anArray at: 1 put: 10, the keyword at: takes the argument 1 and the keyword put: takes the argument 10.</li>
</ul>
<blockquote><p>Keyword based messages are messages that require one or more arguments. Their selector consists of one or more keywords each ending in a colon (:). They follow the syntactic template:
receiver selectorWordOne: argumentOne wordTwo: argumentTwo.</p></blockquote>
<h3>Message composition</h3>
<hr />
<ol>
<li>Unary messages are always sent first, then binary messages and finally keyword messages.</li>
<li>Messages in parentheses are sent prior to any kind of messages.</li>
<li>Messages of the same kind are evaluated from left to right.</li>
</ol>
<h4>Unary > Binary > Keywords</h4>
<hr />
<blockquote><p><strong>Rule One.</strong> Unary messages are sent first, then binary messages, and finally keyword based messages.</p>
<p><strong>Unary > Binary > Keyword</strong></p></blockquote>
<h4>Parentheses first</h4>
<hr />
<blockquote><p><strong>Rule Two.</strong> Parenthesised messages are sent prior to other messages.</p>
<p><strong>(Msg) > Unary > Binary > Keyword</strong></p></blockquote>
<h4>From left to right</h4>
<hr />
<blockquote><p><strong>Rule Three.</strong> When the messages are of the same kind, the order of evaluation is from left to right.</p></blockquote>
<h4>Arithmetic inconsistencies</h4>
<hr />
<blockquote><p><strong>In Smalltalk,</strong> arithmetic operators such as + and * do not have different priority. + and * are just binary messages, therefore * does not have priority over +. Use parentheses to obtain the desired result.</p></blockquote>
<h3>Hints for identifying keyword messages</h3>
<hr />
<h4>Parentheses or not?</h4>
<hr />
<blockquote><p>The characters [, ], ( and ) delimit distinct areas. Within such an area, a keyword message is the longest sequence of words terminated by : that is not cut by the characters ., or ;. When the characters [, ], ( and ) surround some words with colons, these words participate in the keyword message local to the area defined.</p></blockquote>
<h4>When to use [ ] or ( )</h4>
<hr />
<p>You may also have problems understanding when to use square brackets rather than parentheses. The basic principle is that you should use [ ] when you do not know how many times, potentially zero, an expression should be evaluated. [expression] will create a block closure (i.e., an object) from expression, which may be evaluated any number of times (possibly zero), depending on the context. Here note that an expression can either be a message send, a variable, a literal, an assignment or a block.</p>
<p>Hence the conditional branches of ifTrue: or ifTrue:ifFalse: require blocks. Following the same principle both the receiver and the argument of a whileTrue: message require the use of square brackets since we do not know how many times either the receiver or the argument should be evaluated.</p>
<p>Parentheses, on the other hand, only affect the order of sending messages.
So in (expression), the expression will always be evaluated exactly once.</p>
<blockquote><p>|---+---|
|[ x isReady ] whileTrue: [ y doSomething ] | <em>"both the receiver and the argument must be blocks"</em> |
| 4 timesRepeat: [ Beeper beep ] | <em>"the argument is evaluated more than once, so must be a block"</em> |
| (x isReady) ifTrue: [ y doSomething ] | <em>"receiver is evaluated once, so is not a block"</em> |</p></blockquote>
<h3>Expression sequences</h3>
<hr />
<p><code>句号是分隔符,不是中止符.</code></p>
<p>Expressions (i.e., messages sends, assignments. . . ) separated by periods are evaluated in sequence. Note that there is no period between a variable definition and the following expression. The value of a sequence is the value of the last expression. The values returned by all the expressions except the last one are ignored. Note that the period is a separator and not a terminator. Therefore a final period is optional.</p>
<h3>Cascaded messages</h3>
<hr />
<p><code>级联消息</code></p>
<p>Smalltalk offers a way to send multiple messages to the same receiver using a semicolon (;). This is called the cascade in Smalltalk jargon.</p>
<blockquote><p>Expression Msg1 ; Msg2</p></blockquote>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3</pre></td><td class="code"><pre><span class="nc">Transcript</span> <span class="nf">show:</span> <span class="s">'Pharo is '</span><span class="p">.</span>
<span class="nc">Transcript</span> <span class="nf">show:</span> <span class="s">'fun '</span><span class="p">.</span>
<span class="nc">Transcript</span> <span class="nf">cr</span><span class="p">.</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<p>is equivalent to:</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4</pre></td><td class="code"><pre><span class="nf">Transcript</span>
<span class="err">show:</span> <span class="s">'Pharo is'</span><span class="p">;</span>
<span class="nf">show:</span> <span class="s">'fun '</span><span class="p">;</span>
<span class="nf">cr</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<h2>Chapter summary</h2>
<hr />
<ul>
<li>message is always sent to an object named the <em>receiver</em> which may be the result of other message sends.</li>
<li><strong>Unary messages</strong> are messages that do not require any argument. They are of the form of receiver <strong>selector</strong>.</li>
<li><strong>Binary messages</strong> are messages that involve two objects, the receiver and another object and whose selector is composed of one or more characters from the following list: +, −, *, /, |, &, =, >, <, ̃, and @. They are of the form: receiver <strong>selector</strong> argument</li>
<li><strong>Keyword messages</strong> are messages that involve more than one object and that contain at least one colon character (:).
They are of the form: receiver <strong>selectorWordOne:</strong> argumentOne <strong>wordTwo:</strong> argumentTwo</li>
<li><strong>Rule One.</strong> Unary messages are sent first, then binary messages, and finally keyword messages.</li>
<li><strong>Rule Two.</strong> Messages in parentheses are sent before any others.</li>
<li><strong>Rule Three.</strong> When the messages are of the same kind, the order of evaluation is from left to right.</li>
<li>In Smalltalk, traditional arithmetic operators such as + and * have the same priority. + and * are just binary messages, therefore * does not have priority over +. You must use parentheses to obtain a different result.</li>
</ul>
Pharo by Example 32015-04-24T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/24/pharo-by-example-3<h2>Syntax in a nutshell</h2>
<hr />
<ul>
<li>Syntactic elements</li>
</ul>
<blockquote><p>(i) six reserved keywords, or pseudo-variables: self, super, nil, true, false, and thisContext,<br/>
(ii) constant expressions for literal objects including numbers, characters, strings, symbols and arrays, <br/>
(iii) variable declarations, <br/>
(iv) assignments, <br/>
(v) block closures, and <br/>
(vi) messages.</p></blockquote>
<h3>Pharo Syntax in a Nutshell</h3>
<hr />
<p>| Syntax | What it represents |
|---+---|
| startPoint | a variable name |
| Transcript | a global variable name |
| self | pseudo-variable |
| 1 | decimal integer |
| 2r101 | binary integer |
| 1.5 | floating point number |
| 2.4e7 | exponential notation |
| $a | the character 'a' |
| 'Hello' | the string "Hello" |
| #Hello | the symbol #Hello |
| #(1 2 3) | a literal array |
| {1. 2. 1+2} | a dynamic array |
| "a comment" | a comment |
| | x y | | declaration of variables x and y |
| x := 1 | assign 1 to x |
| [x+y] | a block that evaluates to x+y |
| <primitive: 1> | virtual machine primitive or annotation |
| 3 factorial | unary message |
| 3+4 | binary messages |
| 2 raisedTo: 6 modulo: 10 | keyword message |
| ^ true | return the value true |
| Transcript show: ’hello’. Transcript cr | expression separator (.) |
| Transcript show: ’hello’; cr | message cascade (;) |</p>
<hr />
<p><strong>Local variables</strong> startPoint is a variable name, or identifier. By convention, identifiers are composed of words in “camelCase” (i.e., each word except the first starting with an upper case letter). The first letter of an instance variable, method or block argument, or temporary variable must be lower case. This indicates to the reader that the variable has a private scope.</p>
<p><strong>Shared variables</strong> Identifiers that start with upper case letters are global variables, class variables, pool dictionaries or class names. Transcript is a global variable, an instance of the class TranscriptStream.</p>
<p><strong>The receiver</strong> self is a keyword that refers to the object inside which the current method is executing. We call it “the receiver” because this object will normally have received the message that caused the method to execute. self is called a “pseudo-variable” since we cannot assign to it.</p>
<p><strong>Integers</strong> Inadditiontoordinarydecimalintegerslike42,Pharoalsoprovides a radix notation. 2r101 is 101 in radix 2 (i.e., binary), which is equal to decimal 5.</p>
<p><strong>Floating point numbers</strong> can be specified with their base-ten exponent: 2.4e7 is 2.4 ^ 107.</p>
<p><strong>Characters</strong> A dollar sign introduces a literal character: $a is the literal for ‘a’. Instances of non-printing characters can be obtained by sending appropriately named messages to the Character class, such as Character space and Character tab.</p>
<p><strong>Strings</strong> Single quotes are used to define a literal string. If you want a string with a quote inside, just double the quote, as in 'G''day'.</p>
<p><strong>Symbols</strong> are like Strings, in that they contain a sequence of characters. However, unlike a string, a literal symbol is guaranteed to be globally unique. There is only one Symbol object #Hello but there may be multiple String objects with the value 'Hello'.</p>
<p><strong>Compile-time arrays</strong> are defined by #( ), surrounding space-separated literals. Everything within the parentheses must be a compile-time constant. For example, #(27 (true false) abc) is a literal array of three elements: the integer 27, the compile-time array containing the two booleans, and the symbol #abc. (Note that this is the same as #(27 #(true false) #abc).)</p>
<p><strong>Run-time arrays</strong> Curly braces { } define a (dynamic) array at run-time. Elements are expressions separated by periods. So { 1. 2. 1+2 } defines an array with elements 1, 2, and the result of evaluating 1+2. (The curlybrace notation is peculiar to the Pharo and Squeak dialects of Smalltalk! In other Smalltalks you must build up dynamic arrays explicitly.)</p>
<p><strong>Comments</strong> are enclosed in double quotes. "hello" is a comment, not a string, and is ignored by the Pharo compiler. Comments may span multiple lines.</p>
<p><strong>Local variable definitions</strong> Vertical bars | | enclose the declaration of one or more local variables in a method (and also in a block).</p>
<p><strong>Assignment</strong> := assigns an object to a variable.</p>
<p><strong>Blocks</strong> Square brackets [ ] define a block, also known as a block closure or a lexical closure, which is a first-class object representing a function. As we shall see, blocks may take arguments and can have local variables.</p>
<p><strong>Primitives</strong> \<primitive:...> denotes an invocation of a virtualmachine primitive. (<primitive: 1> is the VM primitive for SmallInteger»+.) Any code following the primitive is executed only if the primitive fails. The same syntax is also used for method annotations.</p>
<p><strong>Unary messages</strong> consist of a single word (like factorial) sent to a receiver (like 3).</p>
<p><strong>Binary messages</strong> are operators (like +) sent to a receiver and taking a single argument. In 3+4, the receiver is 3 and the argument is 4.</p>
<p><strong>Keyword messages</strong> consist of multiple keywords (like raisedTo:modulo:), each ending with a colon and taking a single argument. In the expression 2 raisedTo: 6 modulo: 10, the message selector raisedTo:modulo: takes the two arguments 6 and 10, one following each colon. We send the message to the receiver 2.</p>
<p><strong>Method return</strong> <em>^</em> is used to return a value from a method.</p>
<p><strong>Sequences of statements</strong> A period or full-stop (.) is the statement separator. Putting a period between two expressions turns them into independent statements.</p>
<p><strong>Cascades</strong> Semicolons can be used to send a cascade of messages to a single receiver. In Transcript show: 'hello'; cr we first send the keyword message show: 'hello' to the receiver Transcript, and then we send the unary message cr to the same receiver.</p>
<h3>Message sends</h3>
<hr />
<ol>
<li><strong>Unary messages</strong> take no argument. 1 factorial sends the message factorial
to the object 1.</li>
<li><strong>Binary messages</strong> take exactly one argument. 1 + 2 sends the message +
with argument 2 to the object 1.</li>
<li><p><strong>Keyword messages</strong> take an arbitrary number of arguments. 2 raisedTo: 6 modulo: 10 sends the message consisting of the message selector
raisedTo:modulo: and the arguments 6 and 10 to the object 2.</p></li>
<li><p>Unary message selectors consist of alphanumeric characters, and start
with a lower case letter.</p></li>
<li><p>Binary message selectors consist of one or more characters from the following set:</p>
<p> <code>+−/\*~<>=@%|& ?,</code></p></li>
<li><p>Keyword message selectors consist of a series of alphanumeric keywords,
where each keyword starts with a lower-case letter and ends with a colon.</p></li>
<li>Unary messages have the highest precedence, then binary messages, and finally keyword messages.</li>
</ol>
<h3>Method syntax</h3>
<hr />
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4
5
6
7</pre></td><td class="code"><pre><span class="nf">lineCount</span>
<span class="c">"Answer the number of lines represented by the receiver, where every cr adds one line."</span>
<span class="p">|</span><span class="nv"> cr count </span><span class="p">|</span>
<span class="nv">cr</span> <span class="o">:=</span> <span class="nc">Character</span> <span class="nf">cr</span><span class="p">.</span>
<span class="nv">count</span> <span class="o">:=</span> <span class="m">1</span> <span class="nf">min:</span> <span class="bp">self</span> <span class="nf">size</span><span class="p">.</span> <span class="bp">self</span> <span class="nf">do:</span>
<span class="p">[</span><span class="o">:</span><span class="nv">c</span> <span class="p">|</span> <span class="nv">c</span> <span class="nf">==</span> <span class="nv">cr</span> <span class="nb">ifTrue:</span> <span class="p">[</span><span class="nv">count</span> <span class="o">:=</span> <span class="nv">count</span> <span class="nf">+</span> <span class="m">1</span><span class="p">]].</span>
<span class="o">^</span> <span class="nv">count</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<ol>
<li>the method pattern, containing the name (i.e., lineCount) and any arguments (none in this example);</li>
<li>comments (these may occur anywhere, but the convention is to put one at the top that explains what the method does);</li>
<li>declarations of local variables (i.e., cr and count); and</li>
<li>any number of expressions separated by dots; here there are four.</li>
</ol>
<h3>Block syntax</h3>
<hr />
<p><strong>块本质上是匿名函数,实际上是词法闭包</strong></p>
<p>Blocks provide a mechanism to defer the evaluation of expressions. A block is essentially an anonymous function. A block is evaluated by sending it the message value. The block answers the value of the last expression in its body, unless there is an explicit return (with ø), in which case it does not answer any value.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1</pre></td><td class="code"><pre><span class="p">[</span><span class="m">1</span><span class="nf">+</span><span class="m">2</span><span class="p">]</span> <span class="nf">value</span> <span class="nf">-></span> <span class="m">3</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<p>Blocks may take parameters, each of which is declared with a leading colon. A vertical bar separates the parameter declaration(s) from the body of the block. To evaluate a block with one parameter, you must send it the message value: with one argument. A two-parameter block must be sent value:value:, and so on, up to 4 arguments.</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2</pre></td><td class="code"><pre><span class="p">[</span><span class="o">:</span><span class="nv">x</span><span class="p">|</span><span class="m">1</span><span class="nf">+</span><span class="nv">x</span><span class="p">]</span><span class="nf">value:</span><span class="m">2</span> <span class="nf">-></span> <span class="m">3</span>
<span class="p">[</span><span class="o">:</span><span class="nv">x</span><span class="o">:</span><span class="nv">y</span><span class="p">|</span><span class="nv">x</span><span class="nf">+</span><span class="nv">y</span><span class="p">]</span> <span class="nf">value:</span><span class="m">1</span> <span class="nf">value:</span><span class="m">2</span> <span class="nf">-></span> <span class="m">3</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<h3>Conditionals and loops in a nutshell</h3>
<hr />
<ol>
<li><p>Smalltalk offers no special syntax for control constructs. Instead, these are typically expressed by sending messages to booleans, numbers and collections, with blocks as arguments.</p></li>
<li><p>Conditionals are expressed by sending one of the messages ifTrue:, ifFalse: or ifTrue:ifFalse: to the result of a boolean expression.</p></li>
</ol>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4</pre></td><td class="code"><pre><span class="p">(</span><span class="m">17</span> <span class="nf">*</span> <span class="m">13</span> <span class="nf">></span> <span class="m">220</span><span class="p">)</span>
<span class="nb">ifTrue:</span> <span class="p">[</span> <span class="s">'bigger'</span> <span class="p">]</span>
<span class="nb">ifFalse:</span> <span class="p">[</span> <span class="s">'smaller'</span> <span class="p">]</span>
<span class="nf">-></span> <span class="s">'bigger'</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<ol>
<li>Loops are typically expressed by sending messages to blocks, integers or collections. Since the exit condition for a loop may be repeatedly evaluated, it should be a block rather than a boolean value. Here is an example of a very procedural loop:</li>
</ol>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3</pre></td><td class="code"><pre><span class="nv">n</span> <span class="o">:=</span> <span class="m">1</span><span class="p">.</span>
<span class="p">[</span> <span class="nv">n</span> <span class="nf"><</span> <span class="m">1000</span> <span class="p">]</span> <span class="nb">whileTrue:</span> <span class="p">[</span> <span class="nv">n</span> <span class="o">:=</span> <span class="nv">n</span><span class="nf">*</span><span class="m">2</span> <span class="p">].</span>
<span class="nv">n</span> <span class="nf">-></span> <span class="m">1024</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<h3>Primitives and pragmas</h3>
<hr />
<ol>
<li><p>In Smalltalk everything is an object, and everything happens by sending messages. Nevertheless, at certain points we hit rock bottom. Certain objects can only get work done by invoking virtual machine primitives.</p></li>
<li><p>For example, the following are all implemented as primitives: memory allocation (new, new:), bit manipulation (bitAnd:, bitOr:, bitShift:), pointer and integer arithmetic (+, −, <, >, *, / , =, ==...), and array access (at:, at:put:).</p></li>
<li><p>Primitives are invoked with the syntax <primitive: aNumber>. A method that invokes such a primitive may also include Smalltalk code, which will be evaluated only if the primitive fails.</p></li>
</ol>
<h2>Chapter summary</h2>
<hr />
<ul>
<li>Pharo has (only) six reserved identifiers also called pseudo-variables: true, false, nil, self, super, and thisContext.</li>
<li>There are five kinds of literal objects: numbers (5, 2.5, 1.9e15, 2r111), characters ($a), strings ('hello'), symbols (#hello), and arrays (#('hello' #hi))</li>
<li>Strings are delimited by single quotes, comments by double quotes. To get a quote inside a string, double it.</li>
<li>Unlike strings, symbols are guaranteed to be globally unique.</li>
<li>Use #( ... ) to define a literal array. Use { ... } to define a dynamic array.
<code>Notethat#(1+2) size</code> -> 3, but <code>{1+2} size</code> -> 1</li>
<li>There are three kinds of messages: unary (e.g., 1 asString, Array new), binary (e.g., 3 + 4, 'hi' , ' there'), and keyword (e.g., 'hi' at: 2 put: $o)</li>
<li>A cascaded message send is a sequence of messages sent to the same target, separated by semi-colons:
<code>OrderedCollection new add: #calvin; add: #hobbes; size</code> -> 2</li>
<li>Local variables are declared with vertical bars. Use := for assignment. <code>|x| x:=1</code></li>
<li>Expressions consist of message sends, cascades and assignments, possibly grouped with parentheses. Statements are expressions separated by periods.</li>
<li>Block closures are expressions enclosed in square brackets. Blocks may take arguments and can contain temporary variables. The expressions in the block are not evaluated until you send the block a value... message with the correct number of arguments.</li>
<li>There is no dedicated syntax for control constructs, just messages that conditionally evaluate blocks.
<code>(Smalltalk includes: Class) ifTrue: [ Transcript show: Class superclass ]</code></li>
</ul>
Pharo by Example 22015-04-23T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/23/pharo-by-example-2<h2>A first application</h2>
<hr />
<ul>
<li><p>Creating a new Package</p>
<ol>
<li>open Pharo 4.</li>
<li>System Browser.</li>
<li>Add Package... Input <em>PBE-LightsOut</em>.</li>
</ol>
</li>
<li><p>Defining the class LOCell</p>
<ol>
<li><p>On Categories and Packages</p>
<p>A category is simply a collection of related classes in a Smalltalk image.
A package is a collection of related classes and extension methods that may be versioned using the Monticello versioning tool.</p></li>
<li><p>Creating a new class</p>
<ul>
<li>Replace Object by SimpleSwitchMorph.</li>
<li>Replace NameOfSubClass by LOCell.</li>
<li>Add mouseAction to the list of instance variables.</li>
<li>To actually send this message, you must <strong>accept</strong> <em>(CMD+s)</em> it.</li>
</ul>
<p> Defining the class LOCell</p></li>
</ol>
</li>
</ul>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4
5</pre></td><td class="code"><pre><span class="nc">SimpleSwitchMorph</span> <span class="nf">subclass:</span> <span class="ss">#LOCell</span>
<span class="nf">instanceVariableNames:</span> <span class="s">'mouseAction'</span>
<span class="nf">classVariableNames:</span> <span class="s">''</span>
<span class="nf">poolDictionaries:</span> <span class="s">''</span>
<span class="nf">category:</span> <span class="s">'PBE−LightsOut'</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<ul>
<li>Adding methods to a class
<ol>
<li><em>Select the protocol no messages (--all--) in the protocol pane.</em>
<ul>
<li>Initializing instances of LOCell</li>
<li>input <em>initiali</em> Return. then input other code.</li>
<li><strong>Accept</strong> <em>(CMD+s)</em> this method definition.</li>
</ul>
</li>
</ol>
</li>
</ul>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4
5
6
7
8
9</pre></td><td class="code"><pre><span class="nf">initialize</span>
<span class="bp">super</span> <span class="nf">initialize</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">label:</span> <span class="s">''</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">borderWidth:</span> <span class="m">2</span><span class="p">.</span>
<span class="nv">bounds</span> <span class="o">:=</span> <span class="m">0</span><span class="nf">@</span><span class="m">0</span> <span class="nf">corner:</span> <span class="m">16</span><span class="nf">@</span><span class="m">16</span><span class="p">.</span>
<span class="nv">offColor</span> <span class="o">:=</span> <span class="nc">Color</span> <span class="nf">paleYellow</span><span class="p">.</span>
<span class="nv">onColor</span> <span class="o">:=</span> <span class="nc">Color</span> <span class="nf">paleBlue</span> <span class="nf">darker</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">useSquareCorners</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">turnOff</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<ul>
<li>Inspecting an object
<ol>
<li>Open a <em>playground</em> (workspace). Type the expression LOCell new and <strong>inspect it</strong> .</li>
<li>Select root of <em>self</em>.</li>
<li>in the down windows, after <strong>self</strong> input.</li>
<li><strong>Accept</strong> <em>(CMD+d)</em>.</li>
</ol>
</li>
</ul>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4</pre></td><td class="code"><pre><span class="c">"a LOCell(937951232)"</span>
<span class="nf">self</span>
<span class="bp">self</span> <span class="nf">bounds:</span> <span class="p">(</span><span class="m">200</span><span class="nf">@</span><span class="m">200</span> <span class="nf">corner:</span> <span class="m">250</span><span class="nf">@</span><span class="m">250</span><span class="p">).</span>
<span class="bp">self</span> <span class="nf">openInWorld</span><span class="p">.</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<ul>
<li>Defining the class LOGame
<ol>
<li>Now let’s create the other class that we need for the game, which we will call LOGame.
Make the class definition template visible in the browser main window.
Defining the LOGame class</li>
</ol>
</li>
</ul>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3
4
5
6
7
8
9</pre></td><td class="code"><pre><span class="nf">initialize</span>
<span class="p">|</span><span class="nv"> sampleCell width height n </span><span class="p">|</span>
<span class="bp">super</span> <span class="nf">initialize</span><span class="p">.</span>
<span class="nv">n</span> <span class="o">:=</span> <span class="bp">self</span> <span class="nf">cellsPerSide</span><span class="p">.</span>
<span class="nv">sampleCell</span> <span class="o">:=</span> <span class="nc">LOCell</span> <span class="nb">new</span><span class="p">.</span>
<span class="nv">width</span> <span class="o">:=</span> <span class="nv">sampleCell</span> <span class="nf">width</span><span class="p">.</span>
<span class="nv">height</span> <span class="o">:=</span> <span class="nv">sampleCell</span> <span class="nf">height</span><span class="p">.</span>
<span class="bp">self</span> <span class="nf">bounds:</span> <span class="p">(</span><span class="m">5</span><span class="nf">@</span><span class="m">5</span> <span class="nf">extent:</span> <span class="p">((</span><span class="nv">width</span><span class="nf"><em></span><span class="nv">n</span><span class="p">)</span> <span class="nf">@</span><span class="p">(</span><span class="nv">height</span><span class="nf"></em></span><span class="nv">n</span><span class="p">))</span> <span class="nf">+</span> <span class="p">(</span><span class="m">2</span> <span class="nf">*</span> <span class="bp">self</span> <span class="nf">borderWidth</span><span class="p">)).</span>
<span class="nv">cells</span> <span class="o">:=</span> <span class="nc">Matrix</span> <span class="nf">new:</span> <span class="nv">n</span> <span class="nf">tabulate:</span> <span class="p">[</span> <span class="o">:</span><span class="nv">i</span> <span class="o">:</span><span class="nv">j</span> <span class="p">|</span> <span class="bp">self</span> <span class="nf">newCellAt:</span> <span class="nv">i</span> <span class="nf">at:</span> <span class="nv">j</span> <span class="p">].</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p>
<ul>
<li>Organizing methods into protocols</li>
</ul>
<h2>End of statement list encountered -></h2>
<hr />
<blockquote><p>forget lastline "."</p></blockquote>
<h2>Chapter summary</h2>
<hr />
<ul>
<li>Categories are groups of related classes.</li>
<li>A new class is created by sending a message to its superclass.</li>
<li>Protocols are groups of related methods.</li>
<li>A new method is created or modified by editing its definition in the browser and then accepting the changes.</li>
<li>The inspector offers a simple, general-purpose GUI for inspecting and interacting with arbitrary objects.</li>
<li>The browser detects usage of undeclared methods and variables, and offers possible corrections.</li>
<li>The initialize method is automatically executed after an object is created in Pharo. You can put any initialization code there.</li>
<li>The debugger provides a high-level GUI to inspect and modify the state of a running program.</li>
<li>You can share source code filing out a category.</li>
<li>A better way to share code is to use Monticello to manage an external repository, for example defined as a SqueakSource project.</li>
</ul>
Pharo by Example 12015-04-23T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/23/pharo-by-example-1<p>Site: <a href="http://pharobyexample.org/versions/PBE1-2009-10-28.pdf">http://pharobyexample.org/versions/PBE1-2009-10-28.pdf</a></p>
<p>Title: Pharo by Example</p>
<p>Author: Andrew P. Black Stéphane Ducasse Oscar Nierstrasz Damien Pollet</p>
<h2>A quick tour of Pharo</h2>
<hr />
<p>This chapter has introduced you to the Pharo environment and shown you how to use some of the major tools, such as the browser, the method finder, and the test runner. You have also seen a little of Pharo’s syntax, even though you may not understand it all yet.</p>
<p>• A running Pharo system consists of a virtual machine, a sources file, and image and changes files. Only these last two change, as they record a snapshot of the running system.</p>
<p>• When you restore a Pharo image, you will find yourself in exactly the same state — with the same running objects — that you had when you last saved that image.</p>
<p>• Pharo is designed to work with a three-button mouse to click, action- click or meta-click. If you don’t have a three-button mouse, you can use modifier keys to obtain the same effect.</p>
<p>• You click on the Pharo background to bring up the World menu and launch various tools.</p>
<p>• A workspace is a tool for writing and evaluating snippets of code. You can also use it to store arbitrary text.</p>
<p>• You can use keyboard shortcuts on text in the workspace, or any other tool, to evaluate code. The most important of these are do it
<strong>(CMD–d)</strong>, print it <strong>(CMD–p)</strong>, inspect it <strong>(CMD–i)</strong>, explore it <strong>(CMD–I)</strong> and browse it <strong>(CMD–b)</strong>.</p>
<p>• ThebrowseristhemaintoolforbrowsingPharocode,andfordeveloping new code.</p>
<p>• The test runner is a tool for running unit tests. It also supports Test Driven Development.</p>
Smalltalk 80: The Language and Its Implementation (4)2015-04-22T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/22/smalltalk-80-the-language-and-its-implementation-4<h2>Metaclass</h2>
<hr />
<ul>
<li><p>metaclass</p>
<p> The class of a class.</p></li>
<li><p>Class</p>
<p> An abstract superclass of all classes other than metaclasses.</p></li>
<li><p>Metaclass</p>
<p> A class whose instances are class of classes.</p></li>
</ul>
Smalltalk-80: The Language and Its Implementation (3)2015-04-22T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/22/smalltalk-80-the-language-and-its-implementation-3<h2>Subclasses</h2>
<hr />
<ul>
<li><p>subclass</p>
<p> A class that inherits variables and methods from an existing class.</p></li>
<li><p>superclass</p>
<p> The class from which variables and methods are inherited.</p></li>
<li><p>Object</p>
<p> The class that is the root of the tree-structured class hier- archy.</p></li>
<li><p>overriding</p>
<p> Specifying a method in a subclass for the same message as a method in a superclass.</p></li>
<li><p>super</p>
<p> A pseudo-variable that refers to the receiver of a message; differs from self in where to start the search for methods.</p></li>
<li><p>abstract class</p>
<p> A class that specifies protocol, but is not able to fully im- plement it; by convention, instances are not created of this kind of class.</p></li>
<li><p>subclassResponsibility</p>
<p> A message to report the error that a subclass should have implemented one of the superclass's messages.</p></li>
<li><p>shouldNotImplement</p>
<p> A message to report the error that this is a message inherited from a superclass but explicitly not available to instances of the subclass.</p></li>
</ul>
Smalltalk-80: The Language and Its Implementation (2)2015-04-22T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/22/smalltalk-80-the-language-and-its-implementation-2<h2>Classes and Instances</h2>
<hr />
<p>| Terminology | Summary |
|---+---|
| <strong>class</strong> | An object that describes the implementation of a set of similar objects. |
| <strong>instance</strong> | One of the objects described by a class; it has memory and responds to messages. |
| <strong>instance variable</strong> | A variable available to a single object for the entire lifetime of the object; instance variables can be named or indexed. |
| <strong>protocal description</strong> | A description of a class in terms of its instances public message protocal. |
| <strong>implementation description</strong> | A message selector and a set of argument names, one for each argument that a message with this selector must have. |
| <strong>temporary variable</strong> | A variable created for a specific activity and available only for the duration of that activity. |
| <strong>class variable</strong> | A variable shared by all the instances of a single class. |
| <strong>global variable</strong> | A variable shared by all the instances of all classes. |
| <strong>pool variable</strong> | A variable shared by the instances of a set of classes. |
| <strong>Smalltalk</strong> | A pool shared by all classes that contains the global variables. |
| <strong>method</strong> | A procedure describing how to perform one of an object's operations; it is made up of a message pattern, temporary variable declaration, and a sequence of expressions. A method is executed when a message matching its message pattern is sent to an instance of the class in which the method is found. |
| <strong>argument name</strong> | Name of a pseudo-variable available to a method only for the duration of that method's execution; the value of the argument names are the arguments of the message that invoked the method. |
| <strong>up</strong> | When used in a method, indicates that the value of the next expression is to be the value of the method. |
| <strong>self</strong> | A pseudo-variable referring to the receiver of a message. |
| <strong>message category</strong> | A group of methods in a class description. |
| <strong>primitive method</strong> | An operation performed directly by the Smalltalk-80 virtual machine; it is not described as a sequence of Smalltalk-80 expressions. |</p>
Smalltalk 80: The Language and Its Implementation (1)2015-04-21T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/21/smalltalk-80-the-language-and-its-implementation-1<p>Title: Smalltalk-80: The Language and Its Implementation</p>
<p>Author: Adele Goldberg and David Robson</p>
<p>Link: <a href="http://www.mirandabanda.org/bluebook/bluebook_imp_toc.html">http://www.mirandabanda.org/bluebook/bluebook_imp_toc.html</a></p>
<ul>
<li>System Class</li>
</ul>
<blockquote><p>The Smalltalk-80 system includes a set of classes that provides the standard functionality of a programming language and environment: arithmetic, data structures, control structures, and input/output facilities. The functionality of these classes will be specified in detail in Part Two of this book. Figure 1.3 is a diagram of the system classes presented in Part Two. Lines are drawn around groups of related classes; the groups are labeled to indicate the chapters in which the specifications of the classes can be found.</p></blockquote>
<ul>
<li>Arithmetic</li>
</ul>
<blockquote><p>The Smalltalk-80 system includes objects representing both real and rational numbers. Real numbers can be represented with an accuracy of about six digits. Integers with absolute value less than 252428scan be represented exactly. Rational numbers can be represented using these integers. There are also classes for representing linear magnitudes (like dates and times) and random number generators.</p></blockquote>
<ul>
<li>Data Structures</li>
<li>Control Structures</li>
<li>Programming Environment</li>
<li>Viewing and Interacting</li>
<li>Communication</li>
</ul>
<p>|Terminology |Summary|
|---+---|
| object| A component of the Smalltalk-80 system represented by some private memory and a set of operations.|
| message| A request for an object to carry out one of its operations. The object to which a message is sent.|
| receiver " interface| The messages to which an object can respond.|
| class| A description of a group of similar objects.|
| instance| One of the objects described by a class.|
| instance variable| A part of an object's private memory.|
| method| A description of how to perform one of an object's operations.|
| primitive method| An operation performed ,directly by the Smalltalk-80 virtual machine.|
| FinancialHistory| The name of a class used as an example in this book.|
| system classes| The set of classes that come with the Smalltalk-80 system.|</p>
<h1>Expression Syntax</h1>
<hr />
<ul>
<li><p>Literals</p></li>
<li><p>Numbers</p></li>
<li>Characters</li>
<li>Strings</li>
<li>Symbols</li>
<li><p>Arrays</p></li>
<li><p>Variables</p></li>
<li><p>Assignments</p></li>
<li>Pseudo-variable</li>
<li><p>Names</p></li>
<li><p>Messages</p></li>
<li><p>Selectors and Arguments</p></li>
<li>Returning Values</li>
<li>Parsing</li>
<li>Formatting Conventions</li>
<li><p>Cascading</p></li>
<li><p>Blocks</p></li>
<li><p>Control Structures</p></li>
<li>Conditionals</li>
<li>Block Arguments</li>
</ul>
<p>Summary of Terminology</p>
<ol>
<li>Literals describe certain constant objects, such as numbers and character strings.</li>
<li>Variable names describe the accessible variables. The value of a variable name is the current value of the variable with that name.</li>
<li>Message expressions describe messages to receivers. The value of a message expression is determined by the method the message invokes. That method is found in the class of the receiver.</li>
<li>Block expressions describe objects representing deferred activities. Blocks are used to implement control structures.</li>
</ol>
<p>语法简介</p>
<ol>
<li>文字描述某些常量对象,如数字和字符串。</li>
<li>变量名称描述访问变量。一个变量的值的名字是变量的当前值与这个名字。</li>
<li>消息表达式描述消息接收器。一条消息表达式的值是由方法调用的消息。方法是发现类上的接收器。</li>
<li>块表达式描述对象代表延迟活动。块是用于实现控制结构。</li>
</ol>
<p>|Terminology |Summary|
|---:+:---|
| <strong>Literals</strong> | 1. numbers <br />2. individual characters <br />3. strings of characters <br />4. symbols <br />5. arrays of other literal constants |
| <strong>Numbers</strong> | 1. 3 30.45 <br />2. -3 0.005<br />3. -14.0 13772<br /> 4. 1.586e-- 3<br />|
| <strong>Strings</strong> | 1. 'hi'<br /> 2. 'food'<br /> 3. 'the Smalltalk-80 system'<br />|</p>
<p>| <strong>Symbols</strong> | 1. #:bill<br />2. #M63<br />|</p>
<p>| <strong>Arrays</strong> | 1. #(1 2 3)<br />2. #('food" 'utilities' 'rent" 'household' 'transportation' 'taxes' 'recreation')<br/>3. # (('one' 1) ('not" "negative') 0 -- 1)<br />4. #(9 'nine' $9 nine (0 "zero' $0()'e' $f 'g' $h 'i'))|</p>
<p>| <strong>Variables</strong> | 1. index<br /> 2. initiallndex <br /> 3. textEditor<br /> 4. bin14<br /> 5. bin14Totai <br /> 6. HouseholdFinances <br /> 7. Rectangle <br /> 8. IncomeReasons<br />|</p>
<p>| <strong>Assignments</strong> | 1. quantity <- 19<br /> 2.index <- initiallndex <br /> 3. index <- initialIndex <- 1|</p>
<p>| <strong>Pseudo-variable Names</strong> | 1. nil <br /> 2. true <br /> 3. false |</p>
<p>| <strong>Messages</strong> | 1. 3 + 4 : computes the sum of three and four. <br /> 2. index + 1 : adds one to the number named index.<br /> 3. index > limit : inquires whether or not the number named index is greater than the number named limit.<br /> 4. theta sin : computes the sine of the number named theta.<br /> 5. quantity sqrt : computes the positive square root of the number named quantity.<br />|</p>
<p>| <strong>Selectors and Arguments</strong> | 1. Unary Message<br /> * theta sin<br /> * quantity sqrt <br /> * nameString size<br /> 2. Binary Message<br /> * 3 + 4<br /> * total - 1<br /> * total < = max<br /> 3. Keyword Messages<br /> * HouseholdFinances spend: 30.45 on: "food" <br /> * ages at: 'Brett Jorgensen' put: 3<br /> |</p>
<p>| <strong>Returning Values</strong> | 1. sum <- 3 + 4 <br /> 2. x <- theta sin <br /> index <- index + 1 <br /> 3. var <- HouseholdFinances spend: 32.50 on: 'utilities' <br /> |</p>
<p>| <strong>Parsing</strong> | 1. 1.5 tan rounded <br /> 2. index + offset * 2 <br /> 3. index + (offset. 2) <br /> 4. frame scale: factor max: 5 <br /> 5. frame scale: (factor max: 5) <br /> |</p>
<blockquote><ol>
<li>Unary expressions parse left to right.</li>
<li>Binary expressions parse left to right.</li>
<li>Binary expressions take precedence over keyword expressions.</li>
<li>Unary expressions take precedence over binary expressions.</li>
<li><p>Parenthesized expressions take precedence over unary expressions.</p></li>
<li><p>一元运算表达式解析从左到右。</p></li>
<li>二元表达式解析从左到右。</li>
<li>二元表达式优先于关键字表达式。</li>
<li>一元运算表达式优先于二元表达式。</li>
<li>带括号表达式优先于一元运算表达式。</li>
</ol>
</blockquote>
<p>| <strong>Formatting Conventions</strong> | A programmer is free to format expressions in various ways using spaces, tabs, and carriage returns. |</p>
<p>| <strong>Cascading</strong> | OrderedCollection new add: 1; add: 2; add: 3 <br /> Equals: <br /> temp ~ OrderedCoilection new. temp add: 1.<br /> temp add: 2.<br /> temp add: 3<br /> |</p>
<p>| <strong>Blocks</strong> | 1. [index <- index + 1]<br /> 2. [index <-index + 1. array at: index put: O]<br /> 3. [expenditures at: reason.]<br /> |</p>
<p>| <strong>Control Structures</strong> ||</p>
<ol>
<li>Assign a block to incrementBIock.</li>
<li>Assign a block to sumBIock.</li>
<li>Assign the number 0 to sum.</li>
<li>Assign the number 1 to index.</li>
<li>Sendthe message value to the block sumBIock.</li>
<li>Send the message, 1 to the number 1.</li>
<li>Send the message + 1 to the number 0.</li>
<li>Assign the number 1 to sum.</li>
<li>Send the message value to the block IncrementBIock.</li>
<li>Send the message + 1 to the number 1.</li>
<li>Assign the number 2 to index.</li>
<li>Send the message value to the block sumBIock.</li>
<li>Send the message * 2 to the number 2.</li>
<li>Send the message + 4 to the number 1.</li>
<li>Assign the number 5 to sum.</li>
</ol>
<p>| <strong>Conditionals</strong> | (number \\ 2) = 0 <br /> ifTrue" [parity <- 0]<br /> ifFalse: [parity <- 1]<br /> |</p>
<p>| <strong>Block Arguments</strong> | 1. [ :array I total <- total + array size]<br /> 2. [:x:yl (x,x) + (y,y)]<br /> |</p>
<h2>Summary of Terminology</h2>
<hr />
<p>| Terminology |Summary |
|---+---|
| <strong>expression</strong> | A sequence of characters that describes an object. |
| <strong>literal</strong> | An expression describing a constant, such as a number or a string. |
| <strong>symbol</strong> | A string whose sequence of characters is guaranteed to be different from that of any other symbol. |
| <strong>array</strong> | A data structure whose elements are associated with integer indices. |
| <strong>variable name</strong> | An expression describing the current value of a variable. |
| <strong>assignment</strong> | An expression describing a change of a variable's value. |
| <strong>pseudo-variable name</strong> | An expression similar to a variable name. However, unlike a variable name, the value of a pseudo-variable name cannot be changed by an assignment. |
| <strong>receiver</strong> | The object to which a message is sent. |
| <strong>message selector</strong> | The name of the type of operation a message requests of its receiver. |
| <strong>message argument</strong> | An object that specifies additional information for an operation. |
| <strong>unary message</strong> | A message without arguments. |
| <strong>keyword</strong> | An identifier with a trailing colon. |
| <strong>keyword message</strong> | A message with one or more arguments whose selector is made up of one or more keywords. |
| <strong>binary message</strong> | A message with one argument whose selector is made up of one or two special characters. |
| <strong>cascading</strong> | A description of several messages to one object in a single expression. |
| <strong>block</strong> | A description of a deferred sequence of actions. |
| <strong>block argument</strong> | A parameter that must be supplied when certain blocks are evaluated. |
| <strong>value</strong> | A message to a block asking it to carry out the set of actions it represents. |
| <strong>value:</strong> | A keyword used in a message to a block that has block arguments; the corresponding message asks the block to carry out its set of actions. |
| <strong>ifTrue:if False:</strong> | Message to a Boolean requesting conditional selection. |
| <strong>if False:if True:</strong> | Message to a Boolean requesting conditional selection. |
| <strong>ifTrue:</strong> | Message to a Boolean requesting conditional selection. |
| <strong>if False:</strong> | Message to a Boolean requesting conditional selection. |
| <strong>whileTrue:</strong> | Message to a block requesting conditional repetition. |</p>
I Can Read C++ and Java But I Can’t Read Smalltalk2015-04-20T00:00:00+00:00http://jiaxianhua.github.io/smalltalk/2015/04/20/i-can-read-c-and-java-but-i-cant-read-smalltalk<p>原文:<a href="http://www.eli.sdsu.edu/courses/spring01/cs635/readingSmalltalk.pdf">http://www.eli.sdsu.edu/courses/spring01/cs635/readingSmalltalk.pdf</a></p>
<p>标题: I Can Read C++ and Java But I Can’t Read Smalltalk</p>
<p>作者: Wilf LaLonde</p>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="c">"this is a commit"</span>
<span class="s">'this is a string'</span>
<span class="err">#</span><span class="s">'This is a symbol'</span>
<span class="ss">#ThisIsSymbolToo</span><span class="err">'</span></code></pre></figure></p>
<blockquote><p>:= // Means assignment</p>
<p>= //Means equality comparison</p>
<p>== //Means identity comparison</p></blockquote>
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="ss">#(</span><span class="m">1</span> <span class="m">2</span> <span class="m">3</span> <span class="m">4</span> <span class="m">5</span><span class="ss">)</span>
<span class="c">"string1string2"</span>
<span class="s">'string1'</span><span class="nf">,</span> <span class="s">'string2'</span> </code></pre></figure></p>
<h2>Keywords Are Pervasive</h2>
<hr />
<p><figure class="highlight"><pre><code class="language-smalltalk" data-lang="smalltalk"><span class="p">|</span><span class="nv"> t a v </span><span class="p">|</span>
<span class="p">|</span><span class="nv"> aTransformation angle aVector</span><span class="p">|</span></p>
<p><span class="bp">self</span> <span class="nf">rotateBy:</span> <span class="nv">angle</span> <span class="nf">around:</span> <span class="nv">vector</span>
<span class="nf">|</span><span class="nv">result</span><span class="nf">|</span>
<span class="nv">result</span> <span class="o">:=</span> <span class="nc">COMPUTE</span> <span class="nf">ANWSER</span><span class="p">.</span>
<span class="o">^</span><span class="nv">result</span></code></pre></figure></p>
iOS 学习资料整理2015-04-19T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/04/19/trip-to-ios<h1>iOS 学习资料整理</h1>
<p>原文: <a href="https://github.com/Aufree/trip-to-iOS">https://github.com/Aufree/trip-to-iOS</a></p>
<p>这份学习资料是为 iOS 初学者所准备的, 旨在帮助 iOS 初学者们快速找到适合自己的学习资料, 节省他们搜索资料的时间, 使他们更好的规划好自己的 iOS 学习路线, 更快的入门, 更准确的定位的目前所处的位置.</p>
<p>该文档会持续更新, 同时也欢迎更多具有丰富经验的 iOS 开发者将自己的常用的一些工具, 学习资料, 学习心得等分享上来, 我将定期筛选合并, 文档尚有一些不完善之处, 也请不吝指出, 感谢您对 iOS 所做的贡献, 让我们一起把国内的 iOS 做得更好, 谢谢.</p>
<p><strong>如果您有任何意见或建议也可以通过<a href="mailto:freedomlijinfa@gmail.com">邮件</a>或<a href="http://weibo.com/jinfali">微博</a>联系我</strong></p>
<h2>视频教程(英文)</h2>
<table>
<thead>
<tr>
<th>视频 </th>
<th> 简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://itunes.apple.com/us/course/developing-ios-7-apps-for/id733644550">Developing iOS 7 Apps for iPhone and iPad</a> </td>
<td> 斯坦福开放教程之一, 课程主要讲解了一些 iOS 开发工具和 API 以及 iOS SDK 的使用, 属于 iOS 基础视频</td>
</tr>
<tr>
<td><a href="https://itunes.apple.com/itunes-u/ipad-iphone-application-development/id473757255">iPad and iPhone Application Development</a> </td>
<td> 该课程的讲师 Paul Hegarty 是斯坦福大学软件工程学教授, 视频内容讲解得深入, 权威, 深受好评</td>
</tr>
<tr>
<td><a href="https://itunes.apple.com/itunes-u/advanced-iphone-development/id407243028">Advanced iPhone Development - Fall 2010</a> </td>
<td> iOS 开发的进阶课程, 开始涉及到 Core Animation, Core Data, OpenGL 等框架的应用</td>
</tr>
<tr>
<td><a href="https://developer.apple.com/devcenter/ios/index.action">iOS Dev Center</a> </td>
<td> 苹果官方提供的 iOS 学习视频</td>
</tr>
<tr>
<td><a href="http://www.lynda.com/search?q=ios">Lynda</a> </td>
<td> Lynda 上面 iOS 和 Objective-C 的学习资料比较多, 从初级到高级的都有, 覆盖面比较广, 无论 iOS 走到哪个层次, 都可以在上面挑到适合自己的课程</td>
</tr>
<tr>
<td><a href="https://www.codeschool.com/paths/ios">Code School</a> </td>
<td> CodeSchool 上面的 iOS 不多, 不过质量都不错, 一些课程也挺有趣的</td>
</tr>
<tr>
<td><a href="https://www.udemy.com/topic/learn-objective-c">Udemy</a> </td>
<td> Udemy 帮助初学者规划了视频学习路线, 从新手到高级分的比较详细</td>
</tr>
<tr>
<td><a href="https://itunes.apple.com/us/course/developing-ios-8-apps-swift/id961180099">Developing iOS 8 Apps with Swift</a> </td>
<td> 斯坦福白胡子老爷爷最新的 iOS8 和 Swift 课程, 现在 <a href="https://github.com/x140yu/Developing_iOS_8_Apps_With_Swift">GitHub</a> 上面也有人在翻译</td>
</tr>
</tbody>
</table>
<h2>视频教程(中文)</h2>
<table>
<thead>
<tr>
<th>视频 </th>
<th> 简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://v.163.com/special/opencourse/ios7.html">iOS 7 应用开发</a> </td>
<td> 斯坦福白胡子老爷爷的系列视频, 所有视频皆完成翻译, 视频较新, 翻译质量也很高</td>
</tr>
<tr>
<td><a href="http://v.163.com/special/opencourse/iphonekaifa.html">iPhone 开发教程 2010 年冬</a> </td>
<td> 全部视频翻译完毕, 较为深入的讲解 iPhone 开发, 视频适合给有一定 Objective-C 基础的人观看</td>
</tr>
<tr>
<td><a href="http://www.imooc.com/learn/173">使用 Swift 开发 iOS8 App 实战</a> </td>
<td> 慕课网的视频, 主要讲 Swift 的一些基本使用, 并在讲解的过程中做了几个小 APP, 最后还讲了 Sketch 制作分享按钮</td>
</tr>
<tr>
<td><a href="http://www.imooc.com/learn/218">征战 Objective-C</a> </td>
<td> 视频还未完结, 讲了一些 C 和 Objective-C 的基本语法, 适合零基础的人观看</td>
</tr>
<tr>
<td><a href="https://github.com/x140yu/Developing_iOS_8_Apps_With_Swift">Developing iOS 8 Apps with Swift</a> </td>
<td> GitHub 上正在翻译的斯坦福最新的 iOS8 课程, 目前正在翻译, 未完结</td>
</tr>
</tbody>
</table>
<h2>书籍</h2>
<table>
<thead>
<tr>
<th>书籍名称 </th>
<th> 推荐理由</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://www.amazon.com/dp/032194206X/ref=cm_sw_r_tw_dp_fWrPub0BGRHJ3">Objective-C Programming</a> </td>
<td> 内容不多, 却都是精华, 有了一点 C 语言基础可以快速阅读此书, 大概一天时间就可以看完, 看完后对 iOS 开发能够有个基本的印象, 该书的<a href="http://forums.bignerdranch.com">官方论坛</a>有各个章节习题的解答.</td>
</tr>
<tr>
<td><a href="http://book.douban.com/subject/24538384">iOS Programming</a> </td>
<td> 这本书在 Quora 上被评为 iOS 入门最佳书籍, 具体评价可见豆瓣下方该书籍的评论</td>
</tr>
<tr>
<td><a href="http://book.douban.com/subject/3688896">Cocoa Design Patterns</a> </td>
<td> 适合打算深入了解 Cocoa 的人看</td>
</tr>
<tr>
<td><a href="http://cocoadevcentral.com/d/learn_objectivec">Learn Objective-C</a> </td>
<td> 短小精练, 适合有编程基础的人在半小时内对 Objective-C 有个一定了解</td>
</tr>
<tr>
<td><a href="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html">Programming with Objective-C</a> </td>
<td> 看完 Learn Objective-C 可以接着看这个官方对 Objective-C 更为深入的介绍</td>
</tr>
<tr>
<td><a href="http://www.amazon.cn/dp/B00COG40Y0/ref=cm_sw_r_qz_2_dp_l2OPub0N45R0Q">Objective-C 基础教程</a> </td>
<td> 该书作者 Scott Knaster 是 Mac 开发界的传奇人物, 目前在 Google 出过多数书籍都广受许多程序员好评, 此书适合从初级跳到中级的 iOS 开发者阅读</td>
</tr>
<tr>
<td><a href="http://www.amazon.cn/dp/B00R43XG9S/ref=cm_sw_r_qz_pi_T2A_jdp_fCPPub0VBF67T">iOS 开发进阶</a> </td>
<td> 该书作者唐巧是国内 iOS 开发界的名人, 曾参与多个知名软件的开发, 目前该书尚在预售中, 书本内容由浅入深, 将读者一步一步引入到 iOS 中去, 同样适合初级跳到中级的 iOS 开发者阅读</td>
</tr>
<tr>
<td><a href="http://www.amazon.com/Programming-Objective-C-Edition-Developers-Library/dp/0321967607">Programming in Objective-C</a> </td>
<td> 这本书在亚马逊上面深受欢迎, 有关 Objective-C 的东西讲得非常详细</td>
</tr>
<tr>
<td><a href="http://www.amazon.cn/dp/B00JPVNFKM/ref=cm_sw_r_qz_4_dp_tdPPub14X59PV">iOS 测试指南</a> </td>
<td> 该书作者是豆瓣的员工, 书中写的多数内容都是作者在平时的工作实践当中提炼出来的测试经验, 重点讲述了各个测试阶段的具体实践方法, 并且通过持续集成串联了各个测试阶段的活动。</td>
</tr>
<tr>
<td><a href="http://book.douban.com/subject/6920082">Objective-C 编程之道</a> </td>
<td> 解析 iOS 的开山之作, 详细介绍了 MVC 在 Cocoa Touch 上的运作过程, 该书适用于 iOS 中级开发者阅读</td>
</tr>
<tr>
<td><a href="http://www.amazon.cn/dp/B00DE60G3S/ref=cm_sw_r_qz_2_dp_hdPPub11MFE6G">Objective-C 高级编程</a> </td>
<td> 本书主要介绍 iOS 与 OS X 多线程和内存管理, 深入破析了苹果官方公布的源代码, 告诉你一些苹果公司官方文档中不会出现的知识, 适合中级以上 iOS 开发人员阅读</td>
</tr>
<tr>
<td><a href="http://www.amazon.cn/dp/B00IDSGY06/ref=cm_sw_r_qz_2_dp_A2OPub0CH96YH">Effective Objective C 2.0</a> </td>
<td> 书里写了编写高质量 iOS 与 OS X 代码的 52 个有效方法, 适合 iOS 开发的进阶使用</td>
</tr>
<tr>
<td><a href="http://www.amazon.com/dp/0990402053/ref=cm_sw_r_tw_dp_louPub127Q1YP">Swift Fundamentals</a> </td>
<td> 估计将来这本书会成为 Swift 的经典入门书籍, 它的 Stars 数说明了一切</td>
</tr>
<tr>
<td><a href="http://numbbbbb.gitbooks.io/-the-swift-programming-language-/content">The Swift Programming Language 中文版</a> </td>
<td> 90 后开发者梁杰组织翻译的 Swift 编程语言中文版</td>
</tr>
</tbody>
</table>
<h2>博客</h2>
<table>
<thead>
<tr>
<th>博客地址 </th>
<th> 博主信息</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://onevcat.com/#blog">OneV's Den</a> </td>
<td> 王巍(喵神), 现居日本, 就职于 LINE, 知名 iOS 开发者, 写的文章大多深入浅出, 内容广泛, 目前在维护的 <a href="http://swifter.tips">Swifter</a> 也值得收藏</td>
</tr>
<tr>
<td><a href="http://blog.devtang.com">唐巧的技术博客</a> </td>
<td> 唐巧, 国内知名 iOS 开发者, 现就职于猿题库, 博客推出的 iOS 移动开发周报很受欢迎, 更新频繁</td>
</tr>
<tr>
<td><a href="http://blog.txx.im">txx's blog</a> </td>
<td> 90 后 iOS 开发者, 人称虾神, 文章内容讲解大多浅白易懂, 很值得看</td>
</tr>
<tr>
<td><a href="http://beyondvincent.com">破船之家</a> </td>
<td> 博主也是 iOS 大神一个, 经常更新一些 iOS 教程, 文章的质量都很高, 非常值得看</td>
</tr>
<tr>
<td><a href="http://nshipster.cn">NSHipster</a> </td>
<td> NSHipster 的中文网站, 主要对 NSHipster 的英文网站进行翻译, 博文出自 Mattt 大神之手, 文章大都写得很深入, 详细, 每周一更</td>
</tr>
<tr>
<td><a href="http://blog.leezhong.com">Limboy 无网不剩</a> </td>
<td> 李忠, 知乎前员工, 目前在负责花瓣 iOS 开发, 不少文章里面有介绍博主个人的学习方法, 让读者在学到技术的同时也掌握学习的技巧</td>
</tr>
<tr>
<td><a href="http://nianxi.net">念茜的博客</a> </td>
<td> iOS 圈的女神人物, 写的关于安全问题的文章都值得一看, 由于新博客刚开通不久, 目前文章较少, 可以去看下她以前的<a href="http://blog.csdn.net/yiyaaixuexi">博客</a></td>
</tr>
<tr>
<td><a href="http://weekly.ios-wiki.com/history">iOS技术周报</a> </td>
<td> 吴发伟, 天猫资深软件开发工程师, iOS 技术周报每周一更, 推送一些 iOS 技巧, 代码库, 设计等资讯.</td>
</tr>
<tr>
<td><a href="http://www.iwangke.me">iWangKe.me</a> </td>
<td> 王轲, IndieBros Studio 创始人, 优秀的 iOS 开发工程师, 写的文章深入浅出, 很多问题分析透彻, 非常有条理性</td>
</tr>
<tr>
<td><a href="http://www.jianshu.com/p/99e8b3f6f377">叶孤城</a> </td>
<td> 叶孤城, 优秀 iOS 开发工程师, 发表的文章都有很多干货, 对源码解析类文章写得浅显易懂, 并时常总结一些 iOS 开发技巧, 值得一读</td>
</tr>
<tr>
<td><a href="http://zhowkev.in">Kevin Blog</a> </td>
<td> 周楷雯, 秒视创始人, 知名 iOS 工程师, 做出了 <a href="https://github.com/kevinzhow/PNChart">PNChart</a> 和 <a href="https://github.com/kevinzhow/Waver">Waver</a> 这样的好项目, 在博客中也有谈到具体的实现过程</td>
</tr>
<tr>
<td><a href="http://imtx.me">IMTX</a> </td>
<td> 图拉鼎, 知名 Apple 平台开发者, 曾经的 Ubuntu 平台开发者, 文章有不少干货, 大多讲解技术实现和学习经验</td>
</tr>
<tr>
<td><a href="https://github.com/tangqiaoboy/iOSBlogCN">更多</a> </td>
<td> 唐巧收集的中文 iOS/Mac 开发博客列表, 更新频繁, 值得收藏</td>
</tr>
</tbody>
</table>
<h2>文章</h2>
<table>
<thead>
<tr>
<th>标题 </th>
<th> 内容简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.udemy.com/blog/learn-objective-c">Learn Objective C: The Path to iPhone Development</a> </td>
<td> Udemy 写的文章, 说明了一些学习 Objective-C 的前提条件, Objective-C 的发展历史, 学习方法以及学习资源</td>
</tr>
<tr>
<td><a href="http://lifehacker.com/i-want-to-write-ios-apps-where-do-i-start-1644802175">I Want to Write iOS Apps. Where Do I Start?</a> </td>
<td> 主要对 iOS 的开发环境进行了介绍, 并且涉及到了 Swift 的学习, iOS 上架的注意事项, iOS 的设计, 测试, 代码托管等, 讲解较为广泛, 同时也给出不少学习资源</td>
</tr>
<tr>
<td><a href="http://roadfiresoftware.com/2014/04/how-to-become-a-professional-ios-developer">How to become a professional iOS developer</a> </td>
<td> 文章写的很有条理, 文中多次强调了版本控制系统的重要性, 主要内容是对学习 iOS 开发到就职, 给出了自己的建议</td>
</tr>
<tr>
<td><a href="http://codewithchris.com/learning-ios-programming">Learning iOS Programming</a> </td>
<td> 作者总结了一些自己学习 iOS 的血的教训, 最后给出了一些不错学习建议</td>
</tr>
<tr>
<td><a href="https://www.udacity.com/career-paths/ios-developer">Become an iOS Developer</a> </td>
<td> 作者列举了一些学习 iOS 的方法以及常用的库, 以及自学 iOS 的一些建议</td>
</tr>
<tr>
<td><a href="http://www.devtang.com/blog/2014/07/27/ios-levelup-tips">iOS 开发如何提高</a> </td>
<td> 唐巧写的一篇文章, 主要是对 iOS 技术的提高做的一个总结, 文中不少资源, 工具, 学习方法</td>
</tr>
<tr>
<td><a href="http://limboy.me/ios/2014/12/31/learning-ios.html">自学 iOS 开发的一些经验</a> </td>
<td> 文章从入门到进阶到高级, 分为三个阶段, 有条理的讲出了 iOS 的整个学习过程中开发者可能遇到的问题, 并给出了解决办法, 奉献了不少好工具, 资源还有珍贵的学习经验</td>
</tr>
<tr>
<td><a href="http://readful.com/post/101914515826/0-ios">如何从 0 开始学 iOS 开发</a> </td>
<td> 作者给出了学习 iOS 的流程, 并给出一些不错的学习资源</td>
</tr>
<tr>
<td><a href="http://www.cocoachina.com/programmer/20141128/10353.html">如果我可以重新学习 iOS 开发</a> </td>
<td> 作者在文中给出了学习的一些建议, 也谈到了自己的学习方法</td>
</tr>
<tr>
<td><a href="http://www.cocoachina.com/ios/20141106/10147.html">iOS 开发学习路径的一些建议</a> </td>
<td> 文中谈到了英语的重要性, 以及写博客, 看源代码的好处</td>
</tr>
<tr>
<td><a href="http://www.jianshu.com/p/KSuDqb">iOS 开发入门</a> </td>
<td> 作者分享了自己学习 iOS 的经验和资源</td>
</tr>
<tr>
<td><a href="http://beyondvincent.com/blog/2013/07/18/106">Mac 和 iOS 开发资源汇总</a> </td>
<td> 破船之家发布的资源汇总</td>
</tr>
<tr>
<td><a href="http://www.raywenderlich.com/64546/introduction-to-cocoapods-2">CocoaPods 使用教程</a> </td>
<td> 文章讲解了 CocoaPods 的基本使用, 并且配上 AFNetworking 做出了一个小 Demo, 值得一看</td>
</tr>
</tbody>
</table>
<h2>相关网站</h2>
<table>
<thead>
<tr>
<th>网站 </th>
<th> 简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://tutsplus.com/tutorials/search?utf8=%E2%9C%93&search%5Btopic%5D=&search%5Bterms%5D=ios&button=">tutsplus</a> </td>
<td> 不定时更新一些 iOS 教程</td>
</tr>
<tr>
<td><a href="https://developer.apple.com/videos">WWDC</a> </td>
<td> 苹果官方每年一度的 WWDC 视频, 可以了解历年有关 iOS 发布的内容</td>
</tr>
<tr>
<td><a href="http://asciiwwdc.com">ASCIIwwdc</a> </td>
<td> WWDC 的文字版</td>
</tr>
<tr>
<td><a href="https://swift.zeef.com/robin.eggenkamp">Awesome Swift</a> </td>
<td> 该网站收集了很多关于 Swift 的学习资料, 新闻</td>
</tr>
<tr>
<td><a href="http://www.appcoda.com">Appcoda</a> </td>
<td> 经常发布一些 iOS 编程教程, 更新比较频繁, 想了解更多可以查看该网站的 About 界面</td>
</tr>
<tr>
<td><a href="http://nshipster.com">NSHipster</a> </td>
<td> NSHipster is a journal of the overlooked bits in Objective-C, Swift, and Cocoa. Updated weekly.</td>
</tr>
<tr>
<td><a href="http://www.thinkandbuild.it">Think and Build</a> </td>
<td> Some tutorials about Core Graphic and Core Animation.</td>
</tr>
<tr>
<td><a href="http://www.raywenderlich.com/tutorials">Tutorials</a> </td>
<td> 大把的 Objective-C, Swift, iOS 教程, 且全部免费, Raywenderlich 真是业界良心, 赞!</td>
</tr>
</tbody>
</table>
<h2>社区</h2>
<table>
<thead>
<tr>
<th>社区 </th>
<th> 简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://www.cocoachina.com">CocoaChina</a> </td>
<td> 全球最大苹果开发者中文社区</td>
</tr>
<tr>
<td><a href="http://code4app.com">code4app</a> </td>
<td> 经常更新一些很不错的 iOS 代码片段和一些 iOS 资源</td>
</tr>
<tr>
<td><a href="http://www.objc.io">objc</a> </td>
<td> 定期发布一些有关 Objective-C 的高质量的文章</td>
</tr>
<tr>
<td><a href="http://objccn.io">objc中国</a> </td>
<td> 喵神组织的对 objc.io 的翻译网站, 旨在推进国内技术圈整体水平, 翻译质量非常高</td>
</tr>
<tr>
<td><a href="http://www.devdiv.com">DevDiv</a> </td>
<td> 发布一些 iOS 的最新资讯及教程</td>
</tr>
<tr>
<td><a href="http://discuss.cocos2d-x.org">Cocos2d-x</a> </td>
<td> Cocos2d-x 论坛</td>
</tr>
<tr>
<td><a href="http://iphonedevsdk.com">iPhone Dev SDK</a> </td>
<td> 国外较有名的 iOS 开发者论坛</td>
</tr>
<tr>
<td><a href="http://forum.learncocoa.org">Learn Cocoa and iOS Development Forum</a> </td>
<td> <a href="http://www.amazon.com/Learn-Cocoa-Mac-Jack-Nutting/dp/1430245425">Learn Cocoa on the Mac</a> 和 <a href="http://www.amazon.com/Beginning-iOS-Development-Exploring-SDK/dp/143026022X">Beginning iOS 7 Development</a> 这两本书籍的官方论坛, 用户活跃度较高</td>
</tr>
<tr>
<td><a href="http://devforums.apple.com">Apple Developer Forums</a> </td>
<td> 苹果官方的开发者论坛</td>
</tr>
<tr>
<td><a href="http://www.swiftist.org">Swiftist</a> </td>
<td> Swift 中文社区</td>
</tr>
</tbody>
</table>
<h2>工具/插件</h2>
<table>
<thead>
<tr>
<th>工具/插件 </th>
<th> 简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://cocoapods.org">CocoaPods</a> </td>
<td> 开发 OS X 和 iOS 应用程序的一个第三方库的依赖管理工具, 本身是 Ruby 的一个 Gem, 极大的简化了 Objective-C 的开发流程</td>
</tr>
<tr>
<td><a href="http://alcatraz.io">Alcatraz</a> </td>
<td> Alcatraz 是一款管理 Xcode 插件、模版以及颜色配置的工具</td>
</tr>
<tr>
<td><a href="https://github.com/robbiehanson/XcodeColors">XcodeColors</a> </td>
<td> 使 Xcode 调试控制台色彩更丰富</td>
</tr>
<tr>
<td><a href="https://github.com/facebook/xctool">xctool</a> </td>
<td> Facebook 开源的一个 iOS 编译和测试的工具</td>
</tr>
<tr>
<td><a href="https://github.com/trawor/XToDo">XToDo</a> </td>
<td> 一款注释辅助插件,主要用于收集并列出项目中的<code>TODO</code>, <code>FIXME</code>, <code>???</code>, <code>!!!</code></td>
</tr>
<tr>
<td><a href="https://github.com/ksuther/KSImageNamed-Xcode">KSImageNamed-Xcode</a> </td>
<td> 自动补全图片命名的一款插件</td>
</tr>
<tr>
<td><a href="https://github.com/onevcat/VVDocumenter-Xcode">VVDocumenter</a> </td>
<td> 一个自动生成代码注释的工具</td>
</tr>
<tr>
<td><a href="https://imageoptim.com">ImageOptim</a> </td>
<td> 用于压缩图片一款工具</td>
</tr>
<tr>
<td><a href="https://github.com/KrauseFx/fastlane">fastlane</a> </td>
<td> 开发流程工具,将开发过程流程化,极大提高开发效率</td>
</tr>
<tr>
<td><a href="http://benscheirman.com/2013/08/the-ios-developers-toolbelt">iOS 必备的 75 个工具</a> </td>
<td> 其中包含了非常多好用的工具, 涉及到设计, 分析, 部署等, 总结的十分详细, 有<a href="http://blog.jobbole.com/46799">中文翻译</a></td>
</tr>
<tr>
<td><a href="http://blog.devtang.com/blog/2014/06/29/ios-dev-tools">更多</a> </td>
<td> 唐巧总结的一些图形应用工具, 命令行工具, Xcode 插件, 并介绍了一点基础的用法</td>
</tr>
</tbody>
</table>
<h2>指南/教程</h2>
<table>
<thead>
<tr>
<th>网址 </th>
<th> 简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://developer.apple.com/app-store/review/guidelines">App Store Review Guidelines</a> </td>
<td> iOS 应用商店审核指南, 有<a href="http://www.cocoachina.com/ios/20140227/7892.html">中文翻译版</a></td>
</tr>
<tr>
<td><a href="http://dev.swiftguide.cn">Swift 语言指南</a> </td>
<td> 有很多丰富的 Swift 学习资料, 学习 Swift 有这份资料可以省下很多力气</td>
</tr>
<tr>
<td><a href="http://ourcoders.com/thread/show/117">苹果 Xcode 帮助文档阅读指南</a> </td>
<td> Tinyfool 推出的一篇对于帮助新手阅读官方文档的指南</td>
</tr>
<tr>
<td><a href="https://developer.apple.com/programs/ios/gettingstarted">Get started with your iOS developer pragram</a> </td>
<td> 苹果写的一篇入门指南, 粗略讲解了 iOS 程序从开发到上架的整个流程</td>
</tr>
<tr>
<td><a href="http://blog.teamtreehouse.com/the-beginners-guide-to-objective-c-language-and-variables">Teamtreehouse</a> </td>
<td> 文章主要讲解 Objective-C 的一些语法, 文章内容有趣且通俗易懂</td>
</tr>
<tr>
<td><a href="http://www.appdeveloperatlas.com">A map for iOS development</a> </td>
<td> 一张 iOS 开发地图, 做得很赞, 看完对 iOS 开发流程有一定的认知</td>
</tr>
<tr>
<td><a href="https://developer.apple.com/library/ios/referencelibrary/GettingStarted/RoadMapiOS">Start Developing iOS Apps Today</a> </td>
<td> 苹果官方给出的 iOS 入门教程, 看过之后能够做一个 To-Do 小程序</td>
</tr>
<tr>
<td><a href="http://rypress.com/tutorials/objective-c">Ry’s Objective-C Tutorial</a> </td>
<td> 讲解 Objective-C 的教程, 图文并茂, 适合新手阅读</td>
</tr>
<tr>
<td><a href="https://github.com/raywenderlich/objective-c-style-guide">Objective-C Style Guide</a> </td>
<td> Ray Wenderlich 推出的 Objective-C 风格指南</td>
</tr>
<tr>
<td><a href="http://www.shinobicontrols.com/iOS8DayByDay">iOS8 Day-by-Day</a> </td>
<td> 每日一个 iOS8 的小教程, 所以的 DEMO 都可以在其 <a href="https://github.com/ShinobiControls/iOS8-day-by-day">GitHub</a> 上面的找到相关代码</td>
</tr>
</tbody>
</table>
<h2>邮件订阅</h2>
<ul>
<li><a href="http://iosdevweekly.com">iOS Dev Weekly</a> (每周一期,内容多为这一星期里值得关注的 GitHub 项目、文章、工具等)</li>
<li><a href="http://iosdesign.co">iOS Design Weekly</a> (Tips, news and inspiration delivered each week)</li>
</ul>
<h2>文档</h2>
<ul>
<li><a href="https://developer.apple.com/library/ios/navigation">iOS Developer Library</a> (iOS 开发必看, 有此文档足矣, 内容非常之详细)</li>
</ul>
<h2>Awesome 系列</h2>
<ul>
<li><a href="https://github.com/vsouza/awesome-ios">Awesome iOS</a></li>
<li><a href="https://github.com/matteocrippa/awesome-swift">Awesome-Swift(1)</a></li>
<li><a href="https://github.com/Wolg/awesome-swift">Awesome-Swift(2)</a></li>
</ul>
<h2>知乎上的讨论</h2>
<ul>
<li><a href="http://www.zhihu.com/question/20016551">如何才能系统的学习 iOS 开发,理解一些规则和深层次的机制原理?</a></li>
<li><a href="http://www.zhihu.com/question/19627420">没有 C 和 Objective-C 基础如何快速学习 iOS 开发?</a></li>
<li><a href="http://www.zhihu.com/question/20264108">iOS 开发怎么入门?</a></li>
<li><a href="http://www.zhihu.com/question/20130048">iOS 开发入门需要学习哪些知识,从一无所知到精通需要多长时间?</a></li>
<li><a href="http://www.zhihu.com/question/20919784">12 岁如何入门 iOS 编程?</a></li>
<li><a href="http://www.zhihu.com/question/22914651">GitHub 上都有哪些值得关注学习的 iOS 开源项目?</a></li>
</ul>
<h2>Quora 上的讨论</h2>
<ul>
<li><a href="http://www.quora.com/What-are-the-best-resources-to-learn-iOS-development">What are the best resources to learn iOS development?</a></li>
<li><a href="https://www.quora.com/What-are-the-best-new-resources-for-learning-iOS-development-in-2014">What are the best new resources for learning iOS development in 2014?</a></li>
</ul>
<h2>国内知名的程序员开发日报</h2>
<ul>
<li><a href="http://app.memect.com">App 开发日报</a></li>
<li><a href="http://toutiao.io">开发者头条</a></li>
<li><a href="http://weekly.manong.io">码农周刊</a></li>
</ul>
<h2>贡献者</h2>
<p>点击<a href="https://github.com/Aufree/trip-to-iOS/graphs/contributors">该链接</a>查看该项目的所有贡献者</p>
<h2>License</h2>
<p>以上内容采用 <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh">CC BY-NC-SA 3.0</a> 进行许可, 转载请注明出处, 版权归本人及所有贡献者所有</p>
GitHub Top 100 简介2015-04-19T00:00:00+00:00http://jiaxianhua.github.io/github/2015/04/19/top-100-objective-c-on-github<h2>GitHub Top 100 简介</h2>
<p>原文:<a href="https://github.com/Aufree/trip-to-iOS/blob/master/Top-100.md">https://github.com/Aufree/trip-to-iOS/blob/master/Top-100.md</a></p>
<p>主要对当前 GitHub 排名前 100 的项目做一个简单的简介, 方便初学者快速了解到当前 Objective-C 在 GitHub 的情况.</p>
<p><strong>若有任何疑问可通过微博<a href="http://weibo.com/jinfali">@李锦发</a>联系我</strong></p>
<table>
<thead>
<tr>
<th>项目名称 </th>
<th> 项目信息</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. <a href="https://github.com/AFNetworking/AFNetworking">AFNetworking</a> </td>
<td> 作者是 NSHipster 的博主, iOS 开发界的大神级人物, 毕业于卡内基·梅隆大学, 开源了许多牛逼的项目, 这个便是其中之一, AFNetworking 采用 NSURLConnection + NSOperation, 主要方便与服务端 API 进行数据交换, 操作简单, 功能强大, 现在许多人都用它取代 ASIHTTPRequest</td>
</tr>
<tr>
<td>2. <a href="https://github.com/BradLarson/GPUImage">GPUImage</a> </td>
<td> 一款强大的图片滤镜工具, 支持自定义滤镜, 可用来实时处理图片和视频流, 作者是 SonoPlot 公司的 CTO, 在很小的时候便开始接触编程, 他在 <a href="http://stackoverflow.com/users/19679/brad-larson">SO</a> 上面的回答也有很多值得阅读, GPUImage 这个项目从 2012 年开始, 使用 OpenGL 图形程序接口编写, 性能非常好, 现在很多 iOS 程序员都用它来实现 iOS 的模糊效果</td>
</tr>
<tr>
<td>3. <a href="https://github.com/rs/SDWebImage">SDWebImage</a> </td>
<td> 作者 Olivier Poitrey 是 Dailymotion 的 CTO, 拥有多个不错的开源项目, 此项目常用于对从 Web 端接受到的图片进行缓存, 是 UIImageView 的扩展, 应用起来比较简单</td>
</tr>
<tr>
<td>4. <a href="https://github.com/RestKit/RestKit">RestKit</a> </td>
<td> 主要用于 iOS 上网络通信, 允许与 RESTful Web 服务交互, 常用于处理 API, 解析 JSON, 映射响应对象等操作, 简单易用, 方便你把所有精力都放在对数据的操作上</td>
</tr>
<tr>
<td>5. <a href="https://github.com/ReactiveCocoa/ReactiveCocoa">ReactiveCocoa</a> </td>
<td> 由 GitHub 工程师们开发的一个应用于 iOS 和 OS X 开发的函数响应式编程新框架, Matt 称其为 "An open source project that exemplifies this brave new era for Objective-C", 也有人说它是 Cocoa 的未来, 具体可看唐巧写的这篇<a href="http://www.devtang.com/blog/2014/02/11/reactivecocoa-introduction">文章</a></td>
</tr>
<tr>
<td>6. <a href="https://github.com/facebookarchive/three20">three20</a> </td>
<td> 由 Facebook iOS 客户端衍生出的一款 iPhone 框架, 内置许多丰富的功能, 有丰富的界面, 对底层的操作便捷, 为开发者省下了很多时间, 但现在已经停止了更新, 一个 <a href="https://github.com/facebookarchive/three20/pull/832?utm_source=iOS+Dev+Weekly&utm_campaign=46a7deb647-iOS_Dev_Weekly_Issue_100&utm_medium=email&utm_term=0_7bda94b7ca-46a7deb647-299428269">PR</a> 把代码删得干干净净, 不要好奇去点开 Files changed, 我点开后该页面直接卡死, three20 当中的一位作者创建了 <a href="https://github.com/jverkoey/nimbus">Nimbus</a>, 算是 three20 的一个替代品</td>
</tr>
<tr>
<td>7. <a href="https://github.com/jdg/MBProgressHUD">MBProgressHUD</a> </td>
<td> 作者 Matej Bukovinski 是一位全栈工程师, UI/UX 设计师, 此项目是一款提示框第三方库, 帮助开发者快速应用到项目中)</td>
</tr>
<tr>
<td>8. <a href="https://github.com/magicalpanda/MagicalRecord">MagicalRecord</a> </td>
<td> 作者是 Coursera 的 iOS 工程师, 该项目创作灵感来自于 Ruby on Rails 的 Active Record, 主要为方便操作 CoreData 而生, 帮助清除 CoreData 引用的代码, 协助方便 CoreData 的工作</td>
</tr>
<tr>
<td>9. <a href="https://github.com/ccgus/fmdb">FMDB</a> </td>
<td> 一个对 SQLite 进行封装的库, 使用起来方便, 简单</td>
</tr>
<tr>
<td>10. <a href="https://github.com/Mantle/Mantle">Mantle</a> </td>
<td> 作者是 GitHub 的员工, 文档写的很清楚: Mantle makes it easy to write a simple model layer for your Cocoa or Cocoa Touch application, 主要用来将 JSON 数据模型化为 Model 对象, 唱吧在前段时间也改用 Mantle 了.</td>
</tr>
<tr>
<td>11. <a href="https://github.com/Grouper/FlatUIKit">FlatUIKit</a> </td>
<td> 收集了很多扁平化 UI 的 iOS 组件, 方便使用</td>
</tr>
<tr>
<td>12. <a href="https://github.com/pokeb/asi-http-request">ASIHTTPRequest</a> </td>
<td> 一个轻量级的 iOS 网络通信类库, 基于 CFNetwork 框架开发, 但现在已经停止更新, 多数开发者改用 AFNetworking 替代)</td>
</tr>
<tr>
<td>13. <a href="https://github.com/path/FastImageCache">FastImageCache</a> </td>
<td> Path 公司出品的 iOS 库, 作者 Mallory Paine 是苹果前员工, 此类库适用于在滚动时快速显示图像, 高速持久是其最大的特点</td>
</tr>
<tr>
<td>14. <a href="https://github.com/Masonry/Masonry">Masonry</a> </td>
<td> 一个轻量级的布局框架, 同时支持 iOS 和 Mac OS X, 语法优雅, 帮助开发者快速适配不同分辨率的 iOS 设备</td>
</tr>
<tr>
<td>15. <a href="https://github.com/facebook/Shimmer">Shimmer</a> </td>
<td> Facebook 推出的一款具有闪烁效果的第三方控件, 供它旗下一款名为 Paper 的应用使用, 安装使用整个过程都十分简单</td>
</tr>
<tr>
<td>16. <a href="https://github.com/TransitApp/SVProgressHUD">SVProgressHUD</a> </td>
<td> 又一款轻量级的 iOS 第三方控件, 用于显示任务加载时的动画, 非常轻便, 容易使用</td>
</tr>
<tr>
<td>17. <a href="https://github.com/jigish/slate">Slate</a> </td>
<td> 一款窗口管理应用程序, 但在两年前就已经停止更新了</td>
</tr>
<tr>
<td>18. <a href="https://github.com/johnezang/JSONKit">JSONKit</a> </td>
<td> 主要用于解析 JSON, 适用于 iOS6 以下环境, 自从 iOS5 开始 Apple 官方给出了 NSJSONSerialization API, 自此大家都用官方的了</td>
</tr>
<tr>
<td>19. <a href="https://github.com/jverkoey/nimbus">Nimbus</a> </td>
<td> 作者 Jeff 曾为 Facebook, Google 做过不少好东西, 也是 three20 的成员之一, three20 停更后, 他创造出这个框架来代替 three20, 文档齐全</td>
</tr>
<tr>
<td>20. <a href="https://github.com/CocoaLumberjack/CocoaLumberjack">CocoaLumberjack</a> </td>
<td> 这是 Mac 和 iOS 的一款强大的日志框架, 配置简单, 多线程, 提供更高级的 log 功能, 可用于代替默认的 NSLog 语句</td>
</tr>
<tr>
<td>21. <a href="https://github.com/facebook/facebook-ios-sdk">Facebook SDK for iOS</a> </td>
<td> Facebook 官方的 iOS SDK, 方便开发者集成 Facebook 的一些功能到自己的 iOS APP 里面</td>
</tr>
<tr>
<td>22. <a href="https://github.com/facebook/AsyncDisplayKit">AsyncDisplayKit</a> </td>
<td> Facebook 开源的一款 iOS UI 框架, Paper 用的就是该框架, 另外框架还用到了 Facebook 早期开源 Pop 动画引擎</td>
</tr>
<tr>
<td>23. <a href="https://github.com/supermarin/Alcatraz">Alcatraz</a> </td>
<td> Alcatraz 是一款管理 Xcode 插件、模版以及颜色配置的工具, 可以集成到 Xcode 的图形界面中, 安装删除都是几条命令的事, 很方便, 支持自己开发插件并上传</td>
</tr>
<tr>
<td>24. <a href="https://github.com/Inferis/ViewDeck">ViewDeck</a> </td>
<td> 一款开源的 iOS 活动面板组件, 还原 Path 2.0 的侧滑效果, 作者因为时间关系在两年前停止对其更新</td>
</tr>
<tr>
<td>25. <a href="https://github.com/jessesquires/JSQMessagesViewController">JSQMessagesViewController</a> </td>
<td> 优雅的 iOS 消息类库, 常用于聊天应用中, 可定制性高</td>
</tr>
<tr>
<td>26. <a href="https://github.com/Flipboard/FLEX">FLEX</a> </td>
<td> 这是 Flipboard 官方发布的一组专门用于 iOS 开发的应用内调试工具, 开发者无需将其连接到 LLDB/Xcode 或其他远程调试服务器,支持直接在 App 中运行</td>
</tr>
<tr>
<td>27. <a href="https://github.com/facebook/xctool">Xctool</a> </td>
<td> 是 Facebook 开源的一个命令行工具,用来替代苹果的 XcodeBuild 工具, 极大的方便了 iOS 的构建和测试, 输出错误信息也比较友好, 受到许多 iOS 开发者的称赞, 经常与其搭配使用的还有 OCUnit, <a href="https://travis-ci.org">Travis CI</a>, <a href="http://oclint.org">OCLint</a> 等测试工具</td>
</tr>
<tr>
<td>28. <a href="https://github.com/OpenEmu/OpenEmu">OpenEmu</a> </td>
<td> 超强的游戏模拟器, 做游戏开发必备, 官网做得也很不错</td>
</tr>
<tr>
<td>29. <a href="https://github.com/nicklockwood/iCarousel">iCarousel</a> </td>
<td> 作者是英国 Charcoal Design 公司的创始人, 开源领域的贡献颇为卓著, 这个项目就是其中之一, 这是一款可以在 iOS 上实现旋转木马视图切换效果的第三方控件, 并提供多种切换效果</td>
</tr>
<tr>
<td>30. <a href="https://github.com/romaonthego/RESideMenu">RESideMenu</a> </td>
<td> 作者 Roman Efimov 是雅虎的 iOS 工程师, 这个项目实现了 iOS 上的菜单侧滑效果, 创意来源于 Dribbble, 该项目支持 iOS8</td>
</tr>
<tr>
<td>321 <a href="https://github.com/kevinzhow/PNChart">PNChart</a> </td>
<td> 作者周楷雯是 90 后, 秒视的创始人, 该项目是一个带动画效果的图表控件, 简约易用, 受到不少开发者喜爱</td>
</tr>
<tr>
<td>31.2<a href="https://github.com/square/PonyDebugger">PonyDebugger</a> </td>
<td> 由 Square 公司推出的一款优秀的 iOS 应用网络调试工具, 用户可以实时看到应用程序的网络请求, 也可以对 iOS 应用程序的核心数据栈进行远程调试</td>
</tr>
<tr>
<td>33. <a href="https://github.com/jverdi/JVFloatLabeledTextField">JVFloatLabeledTextField</a> </td>
<td> 作者是 Thumb Labs 的联合创始人, JVFloatLabeledTextField 是 UITextField 的子类, 主要实现输入框标签浮动效果, 创作灵感来自 Dribbble, 已出现多个移植版本</td>
</tr>
<tr>
<td>34. <a href="https://github.com/CEWendel/SWTableViewCell">SWTableViewCell</a> </td>
<td> UITableViewCell 的子类, 实现了左右滑动显示信息视图并调出按钮</td>
</tr>
<tr>
<td>35. <a href="https://github.com/levey/AwesomeMenu">AwesomeMenu</a> </td>
<td> 作者是一位中国人, 该项目主要是使用 CoreAnimation 还原了 Path menu 的动画效果</td>
</tr>
<tr>
<td>36. <a href="https://github.com/tonymillion/Reachability">Reachability</a> </td>
<td> Reachablity 是用于检测 iOS 设备网络环境的库</td>
</tr>
<tr>
<td>37. <a href="https://github.com/onevcat/VVDocumenter-Xcode">VVDocumenter-Xcode</a> </td>
<td> 作者是王巍国内著名的 iOS 开发者, 人称喵神, 目前在日本 LINE 公司工作, 该项目帮助开发者轻松的生成注释文档, 节省了不少工作量, 赞</td>
</tr>
<tr>
<td>38. <a href="https://github.com/google/physical-web">The Physical Web</a> </td>
<td> 由 Chrome 团队主导的一个项目, 意在用 URL 连接世界, 方便用户接受数据, 目前尚处在实验阶段</td>
</tr>
<tr>
<td>39. <a href="https://github.com/samuelclay/NewsBlur">NewsBlur</a> </td>
<td> 作者独自一个人 Samuel Clay 做出来的一款名为 NewsBlur 的新闻阅读器, 很多人都称其为 Google Reader 的替代品, 这是它的源码</td>
</tr>
<tr>
<td>40. <a href="https://github.com/cocos2d/cocos2d-spritebuilder">Cocos2D-SpriteBuilder</a> </td>
<td> 一个可用于在 iOS, Mac 和 Android 上制作 2D 游戏或其它图形/交互应用的框架, 之前的项目名称为 Cocos Swift, 目前该项目在 GitHub 上更新较为频繁</td>
</tr>
<tr>
<td>41. <a href="https://github.com/TTTAttributedLabel/TTTAttributedLabel">TTTAttributedLabel</a> </td>
<td> UILabel 的替代品, 使 iOS 上的 Label 功能更加丰富, 可支持链接植入等功能</td>
</tr>
<tr>
<td>42. <a href="https://github.com/robbiehanson/CocoaAsyncSocket">CocoaAsyncSocket</a> </td>
<td> 一个功能强大、简单易用的异步 socket 通讯类库, 支持 TCP 和 UDP 协议, 可用于 Mac 和 iOS 设备上, 作者 Robbie Hanson 是 Deusty 的首席软件工程师</td>
</tr>
<tr>
<td>43. <a href="https://github.com/devinross/tapkulibrary">TapkuLibrary</a> </td>
<td> 作者是 Devin Ross, 这是在 iOS 上一款功能强大的 UI 效果类库, 可以实现多种酷炫的效果, 目前仍在更新中</td>
</tr>
<tr>
<td>44. <a href="https://github.com/CanvasPod/Canvas">Canvas</a> </td>
<td> 无需编码实现牛逼的动画效果的库, 连设计师都可以快速上手</td>
</tr>
<tr>
<td>45. <a href="https://github.com/square/SocketRocket">SocketRocket</a> </td>
<td> Square 公司开源的一个 WebSocket 客户端, 稳定并且易用, 做实时应用常会用到, 受广大开发者喜爱</td>
</tr>
<tr>
<td>46. <a href="https://github.com/ECSlidingViewController/ECSlidingViewController">ECSlidingViewController</a> </td>
<td> 一个视图控制器容器, 将子视图处理成两层, 通过滑动来处理层的切换, 创作灵感来自 Facebook 和 Path的 App, 作者是 Cleveland 的员工</td>
</tr>
<tr>
<td>47. <a href="https://github.com/stig/json-framework">Json Framework</a> </td>
<td> 用于解析 JSON 数据的一个框架, 但是在 iOS5 以上版本大多数人都选择使用 NSJSONSerialization 来解析 JSON, 该项目现在在 GitHub 上也几乎没怎么更新了</td>
</tr>
<tr>
<td>48. <a href="https://github.com/facebook/Tweaks">Tweaks</a> </td>
<td> Facebook 开源的一款工具, 旨在帮助 iOS 开发者更快的迭代应用, 方便用户动态的调整参数, 是的, Paper 这个项目也用到了</td>
</tr>
<tr>
<td>49. <a href="https://github.com/realm/realm-cocoa">realm-cocoa</a> </td>
<td> Realm-Cocoa 是 Realm 公司推出一款移动端数据库, 可以运行在手机、平板和可穿戴设备之上, 其目标是取代 CoreData 和 SQLite 数据库</td>
</tr>
<tr>
<td>50. <a href="https://github.com/zwaldowski/BlocksKit">BlocksKit</a> </td>
<td> 一个开源的与 Cocoa 紧密集合的基础性框架</td>
</tr>
<tr>
<td>51. <a href="https://github.com/arashpayan/appirater">Appirater</a> </td>
<td> 一款用于提醒用户给你的 App 打分的工具</td>
</tr>
<tr>
<td>52. <a href="https://github.com/kif-framework/KIF">KIF</a> </td>
<td> Square 出品的一个开源的用户界面测试框架, 极大的简化了 iOS 开发者的 UI 测试流程</td>
</tr>
<tr>
<td>53. <a href="https://github.com/slackhq/SlackTextViewController">SlackTextViewController</a> </td>
<td> Slack 推出的一款具有文字输入框高度自适应, 自动输入, 复制单元格内容等功能的解决方案</td>
</tr>
<tr>
<td>54. <a href="https://github.com/IFTTT/JazzHands">JazzHands</a> </td>
<td> IFTTT 开源的一个简单易用的关键帧基础动画框架, 可通过手势、scroll views, KVO, ReactiveCocoa 等方式来控制动画</td>
</tr>
<tr>
<td>55. <a href="https://github.com/BoltsFramework/Bolts-iOS">Bolts-iOS</a> </td>
<td> Bolts 是一个 Parse 和 Facebook 在内部使用的底层库, 方便移动开发</td>
</tr>
<tr>
<td>56. <a href="https://github.com/eczarny/spectacle">Spectacle</a> </td>
<td> 一款易用的 OS X 窗口分屏操作快捷键工具, 这是其源代码</td>
</tr>
<tr>
<td>57. <a href="https://github.com/tombenner/nui">nui</a> </td>
<td> 方便样式化 iOS 应用中的 UI 元素, 可在短时间内样式化整个应用, 类 CSS 原理</td>
</tr>
<tr>
<td>58. <a href="https://github.com/Induction/Induction">Induction</a> </td>
<td> Induction 是一款用于理解数据关系的管理工具, 这是其程序代码</td>
</tr>
<tr>
<td>59. <a href="https://github.com/icanzilb/JSONModel">JSONModel</a> </td>
<td> 一个能迅速解析服务器返回的 Json 数据的库, 方便数据的类型转换</td>
</tr>
<tr>
<td>60. <a href="https://github.com/Cocoanetics/DTCoreText">DTCoreText</a> </td>
<td> 一个开源的 iOS 富文本组件, 它可以解析 HTML 与 CSS 并最终用 CoreText 绘制出来, 通常用于在一些需要显示富文本的场景下代替低性能的 UIWebView</td>
</tr>
<tr>
<td>61. <a href="https://github.com/schneiderandre/popping">Popping</a> </td>
<td> 基于 Facebook Pop 引擎的 iOS 动画库, 集合了很多动画效果</td>
</tr>
<tr>
<td>62. <a href="https://github.com/KrauseFx/TSMessages">TSMessages</a> </td>
<td> 一个用来弹出显示警告和通知的轻量级库, 样式丰富, 简单易用</td>
</tr>
<tr>
<td>63. <a href="https://github.com/facebook/KVOController">KVOController</a> </td>
<td> 一个简单安全的 KVO(Key-value Observing, 键-值 观察)工具, 提供简单方便、线程安全的API, Facebook 的开源项目之一</td>
</tr>
<tr>
<td>64. <a href="https://github.com/mwaterfall/MWPhotoBrowser">MWPhotoBrowser</a> </td>
<td> 一款简单的 iOS 照片浏览控件</td>
</tr>
<tr>
<td>65. <a href="https://github.com/mutualmobile/MMDrawerController">MMDrawerController</a> </td>
<td> 一个轻量级, 易于使用的侧边抽屉导航 iOS 控件</td>
</tr>
<tr>
<td>66. <a href="https://github.com/escoz/QuickDialog">QuickDialog</a> </td>
<td> 用于快速创建复杂的 iOS 表单, 自定义了 UITableViewCell, TableView 的样式</td>
</tr>
<tr>
<td>67. <a href="https://github.com/samvermette/SVPullToRefresh">SVPullToRefresh</a> </td>
<td> 一款只需一行代码便可集成上拉刷新和下拉加载的组件</td>
</tr>
<tr>
<td>68. <a href="https://github.com/nothingmagical/cheddar-ios">cheddar-ios</a> </td>
<td> Cheddar 是一款简单易用的日程管理软件, 这是其早期版本的开源代码, 该项目已停止维护</td>
</tr>
<tr>
<td>69. <a href="https://github.com/XVimProject/XVim">XVim</a> </td>
<td> 一款在 Xcode 上实现了 Vim 功能的插件</td>
</tr>
<tr>
<td>70. <a href="https://github.com/enormego/EGOTableViewPullRefresh">EGOTableViewPullRefresh</a> </td>
<td> 一款提供下拉刷新的控件, 最后更新时间是一年前</td>
</tr>
<tr>
<td>71. <a href="https://github.com/gimenete/iOS-boilerplate">iOS-boilerplate</a> </td>
<td> iOS 应用程序的基础模板, 使用该模板可以省掉许多项目初始编码的工作, 内置非常多丰富的功能, 现已经停止维护</td>
</tr>
<tr>
<td>72. <a href="https://github.com/gotosleep/JASidePanels">JASidePanels</a> </td>
<td> 一个 UIViewController 容器, 灵感来自 Facebook 和 Path 2.0 应用的菜单, 实现了左右侧滑的操作</td>
</tr>
<tr>
<td>73. <a href="https://github.com/mattt/FormatterKit">FormatterKit</a> </td>
<td> 收集了很多构思优秀的 NSFormatter 子类</td>
</tr>
<tr>
<td>74. <a href="https://github.com/erichoracek/MSDynamicsDrawerViewController">MSDynamicsDrawerViewController</a> </td>
<td> 实现了具有动态弹性效果的抽屉式侧边导航栏, 效果丰富, 可定制性强</td>
</tr>
<tr>
<td>75. <a href="https://github.com/boctor/idev-recipes">idev-recipes</a> </td>
<td> iDevRecipes 博客的代码, 演示如何实现一些有趣的控件, 该项目在两年前(2013)停止了更新</td>
</tr>
<tr>
<td>76. <a href="https://github.com/robbiehanson/XMPPFramework">XMPPFramework</a> </td>
<td> 一个基于 RFC-3920 实现, 支持多线程和线程保护, 同时通用于所有的 iOS 和 Mac OS 开发设备的通信框架.</td>
</tr>
<tr>
<td>77. <a href="https://github.com/MacGapProject/MacGap1">MacGap1</a> </td>
<td> 一款可以将 HTML/CSS/JS 网络应用打包成原生 Mac App 的工具</td>
</tr>
<tr>
<td>78. <a href="https://github.com/nicklockwood/FXBlurView">FXBlurView</a> </td>
<td> iOS 模糊背景类库, 可以方便的根据底层显示的状态生成模糊效果</td>
</tr>
<tr>
<td>79. <a href="https://github.com/shu223/iOS7-Sampler">iOS7-Sampler</a> </td>
<td> 整合演示了多个具有 iOS7 新特性的的项目, 提供了非常多的例子参考</td>
</tr>
<tr>
<td>80. <a href="https://github.com/mxcl/PromiseKit">PromiseKit</a> </td>
<td> 提供强大的 iOS 开发异步功能, 是 Promises 的实现, 受到广大开发者的追捧</td>
</tr>
<tr>
<td>81. <a href="https://github.com/facebook/origami">Origami</a> </td>
<td> 此为 Facebook 推出的 Quartz Composer 的一个开源插件, 由其设计团队花费了 9 个月打造而成, 目的是为方便设计师快速构建原型, 以零代码完成复杂动画的合成和测试, 堪称神器</td>
</tr>
<tr>
<td>82. <a href="https://github.com/fpillet/NSLogger">NSLogger</a> </td>
<td> 一款强大的日志分析工具, 具有大窗口查看 Log, 自定义日志等级等功能</td>
</tr>
<tr>
<td>83. <a href="https://github.com/ksuther/KSImageNamed-Xcode">KSImageNamed-Xcode</a> </td>
<td> 一款对 UIImage 的 imageNamed 提供自动补全功能的插件, 非常方便</td>
</tr>
<tr>
<td>84. <a href="https://github.com/smileyborg/PureLayout">PureLayout</a> </td>
<td> 一个简单却强大的 AutoLayout API 库, 兼容了 Objective-C 和 Swift, 扩展了 UIView/NSView, NSArray, 和 NSLayoutConstraint</td>
</tr>
<tr>
<td>85. <a href="https://github.com/tomaz/appledoc">AppleDoc</a> </td>
<td> 一款 Objective-C 文档生成工具, 生成的文档风格保持与 Apple 官方的一致, 极大的方便了 Xcode 识别自己写的 API 文档, 安装也是十分的简单</td>
</tr>
<tr>
<td>86. <a href="https://github.com/gnachman/iTerm2">iTerm2</a> </td>
<td> iTerm2 被不少程序员称赞为 Mac 下最好用的终端, 这是其源代码, 配合 oh-my-zsh 使用效果更佳</td>
</tr>
<tr>
<td>87. <a href="https://github.com/kiwi-bdd/Kiwi">Kiwi</a> </td>
<td> 一个行为驱动开发测试框架, 适用于 iOS 平台, 旨在为开发者提供一个简单配置便可使用的 BDD 库</td>
</tr>
<tr>
<td>88. <a href="https://github.com/alloy/terminal-notifier">terminal-notifier</a> </td>
<td> 一款命令行工具, 用来给 Mac OS X 用户发送通知</td>
</tr>
<tr>
<td>89. <a href="https://github.com/uranusjr/macdown">MacDown</a> </td>
<td> Mac OS X 下的一款开源的 Markdown 编辑器, 创意来自与 Mou, 使用 brew cask 即可完成安装</td>
</tr>
<tr>
<td>90. <a href="https://github.com/twitter/twui">TwUI</a> </td>
<td> Twitter 开源的一个支持硬件加速的 Mac 的 UI 框架, 最后一次的更新时间是在 3 年前(2012)</td>
</tr>
<tr>
<td>91. <a href="https://github.com/honcheng/PaperFold-for-iOS">PaperFold for iOS</a> </td>
<td> 实现了类似折纸效果的视图切换, 可从不同方向进行切换, 该项目已经许久未更新</td>
</tr>
<tr>
<td>92. <a href="https://github.com/vfr/Reader">Reader</a> </td>
<td> 一款开源的 iOS PDF 阅读器, 附带书签, 列纲要等功能</td>
</tr>
<tr>
<td>93. <a href="https://github.com/marcuswestin/WebViewJavascriptBridge">WebViewJavascriptBridge</a> </td>
<td> 一个方便使用 Objective-C 与 JavaScript 进行通信的第三方库, 支持消息发送, 接收, 消息处理器的注册与调用以及设置消息处理的回调</td>
</tr>
<tr>
<td>94. <a href="https://github.com/shu223/iOS8-Sampler">iOS8-Sampler</a> </td>
<td> iOSX-Sampler 系列之一, 整合演示了多个具有 iOS8 新特性的的项目, 提供了非常多的例子参考</td>
</tr>
<tr>
<td>95. <a href="https://github.com/robbiehanson/CocoaHTTPServer">CocoaHTTPServer</a> </td>
<td> 一个用于 Mac OS X 或 iOS 应用的轻量级、可嵌入的HTTP 服务器框架, 方便开发者在应用中嵌入一个 HTTP 服务器</td>
</tr>
<tr>
<td>96. <a href="https://github.com/rsms/kod">Kod</a> </td>
<td> Mac OS X 上一款专为程序员打造的编辑器, 这是其开源代码, 可惜的是作者在 2011 年停止了维护</td>
</tr>
<tr>
<td>97. <a href="https://github.com/michaeltyson/TPKeyboardAvoiding">TPKeyboardAvoiding</a> </td>
<td> 下拉键盘在 iOS 移动文本字段的通用解决方案, 能够自动处理键盘弹出后出现遮挡到文本输入框的问题</td>
</tr>
<tr>
<td>98. <a href="https://github.com/MugunthKumar/MKNetworkKit">MKNetworkKit</a> </td>
<td> 一个轻量级网络请求框架, 完全基于 ARC, 仅有两个类, 具有自主操作多个网络请求, 更加准确的显示网络活动指标等优点</td>
</tr>
<tr>
<td>99. <a href="https://github.com/pkluz/PKRevealController">PKRevealController</a> </td>
<td> 一个 iOS 平台上的视图控制器集合, 通过展现多个视图控制器来进行控制器之间的切换. 设置简单, 高度灵活</td>
</tr>
<tr>
<td>00. <a href="https://github.com/AlanQuatermain/AQGridView">AQGridView</a> </td>
<td> 一个命令行工具, 通过项目里的 <code>.xcdatamodel</code> 文件, 可以为每个 entity 生成两个类, 方便 CoreData 的使用</td>
</tr>
</tbody>
</table>
Mac OS X Node.js2015-04-18T00:00:00+00:00http://jiaxianhua.github.io/software/2015/04/18/mac-os-x-nodejs<p>Download Node.js</p>
<p>open <a href="https://nodejs.org/download/">https://nodejs.org/download/</a> to download Mac OS X Install (.pkg)</p>
<p>install Node.js</p>
<p>open Terminal</p>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>node -h
Usage: node <span class="o">[</span>options] <span class="o">[</span> -e script | script.js <span class="o">]</span> <span class="o">[</span>arguments]
node debug script.js <span class="o">[</span>arguments]</p>
<p>Options:
-v, --version print node<span class="s1">'s version
-e, --eval script evaluate script
-p, --print evaluate script and print result
-i, --interactive always enter the REPL even if stdin
does not appear to be a terminal
--no-deprecation silence deprecation warnings
--throw-deprecation throw an exception anytime a deprecated function is used
--trace-deprecation show stack traces on deprecations
--v8-options print v8 command line options
--max-stack-size=val set max v8 stack size (bytes)
--icu-data-dir=dir set ICU data load path to dir
(overrides NODE_ICU_DATA)
--enable-ssl2 enable ssl2
--enable-ssl3 enable ssl3</p>
<p>Environment variables:
NODE_PATH '</span>:<span class="s1">'-separated list of directories
prefixed to the module search path.
NODE_MODULE_CONTEXTS Set to 1 to load modules in their own
global contexts.
NODE_DISABLE_COLORS Set to 1 to disable colors in the REPL
NODE_ICU_DATA Data path for ICU (Intl object) data</p>
<p>Documentation can be found at http://nodejs.org/</span></code></pre></figure></p>
gitbook2015-04-18T00:00:00+00:00http://jiaxianhua.github.io/software/2015/04/18/gitbook<p>Install</p>
<p><code>$ sudo npm install -g gitbook-cli</code></p>
<p>Test</p>
<p><code>$ gitbook -V</code></p>
<p><code>0.3.3</code></p>
simulator和emulator区别2015-04-17T00:00:00+00:00http://jiaxianhua.github.io/game/2015/04/17/the-different-between-simlsimulator-and-emulator<p>原文: <a href="http://blog.csdn.net/laurensky/article/details/3323756">http://blog.csdn.net/laurensky/article/details/3323756</a></p>
<h2>解释一</h2>
<hr />
<p><strong>emulator</strong> <br/>
n.[计]仿真器。</p>
<p>通过软件方式,精确地在一种处理器上仿真另一种处理器或者硬件的运行方式。其目的是完全仿真被仿真硬件在接收到各种外界信息的时候的反应。我们现在常见的MAME、ePSXe等都是这一类。</p>
<p><strong>simulator</strong><br/>
n. 模拟器。</p>
<p>通过某种手段,来模拟某些东西。不一定要完全正确的原理,追求的只是尽可能的相像。比如DWI、BandJAM等都属于这一类。</p>
<h2>解释二</h2>
<hr />
<p> 模拟(Simulation)即选取一个物理的或抽象的系统的某些行为特征,用另一系统来表示它们的过程。
模拟技术的高级阶段称为仿真模拟(Emulation)、系统仿真,即用一数据处理系统来全部或部分地模拟某一数据处理系统,以致于模仿的系统能想被模仿的系统一样接受同样的数据、执行同样的程序、获得同样的结果。</p>
<h2>解释三</h2>
<hr />
<p> <strong>Emulation</strong>:When one system performs in exactly the same way as another, though perhaps not at the same speed. A typical example would be emulation of one computer by ( a program running on) another. You migh use emulation as a replacement for a system whereas you would use a simulation if you just wanted to analyse it and make predictions about it.</p>
<p> <strong>Simulation</strong>:Attempting to predict aspects of the behaviour of some system by creating an approximate (mathematical) model of it. This can be done by physical modelling, by writing a special-purpose computer program or using a more general simulation package, probably still aimed at a particular kind of simulation (e.g. structural engineering, fluid flow). Typical examples are aricraft flight simulators or electronic circuit simulators. A great many simulation languages exist, e.g. {Simula}.</p>
<h2>解释四</h2>
<hr />
<p> 仿真器(Emulator),又称仿真程序,在软件工程中指可以使计算机或者其他多媒体平台(掌上电脑,手机)能够运行其他平台上的程序,常被错误的称为模拟器。仿真器多用于电视游戏和街机,也有一些用于掌上电脑。仿真器一般需要ROM才能执行,ROM的最初来源是一些原平台的ROM芯片,通过一些手段将原程序拷贝下来(这个过程一般称之为“dump”)然后利用仿真器加载这些ROM来实现仿真过程。<br/>
模拟器(simulator),又称模拟程序,在计算机科学技术的软件工程中,是指完全基于主机程序并模拟了目标处理器的功能和指令系统的程序。而仿真器(emulator)具有更强大的硬件模仿功能。
还有很多很多的解释,相信看了上面的四个解释,对Simulator和Emulator的区别有感性的认识。</p>
<h2>把握以下关键的几点,就可以很容易区别出Simulator和Emulator</h2>
<hr />
<ol>
<li>Simulator中文叫模拟器;Emulator中文叫仿真器。</li>
<li>Simulator纯粹以软件来模拟源平台的功能和运行结果;Emulator以软件和硬件来模拟源平台的内部设计、行为和运行结果。</li>
</ol>
<h2>举例来说</h2>
<hr />
<ol>
<li>有使用硬件来模拟的,都是Emulator。比如基于单片机的模拟。(什么是叫使用硬件模拟?比如模拟源平台的Timer/PPU/SPU, 直接使用目标平台的Timer/PPU/SPU,那么就是硬件模拟)。</li>
<li>一般的,在PC上运行的模拟器都叫Simulator,常见的是模拟LCD的显示画面; 在嵌入平台上运行的模拟器都是Emulator,因为在嵌入平台运行的话,为了提高效率,都会以对应的硬件模块来模拟源平台。</li>
<li>PC上的模拟器如果模拟其内部设计、行为,比如读取ROM文件,精确中断、异常,OS等都是Emulator。</li>
</ol>
build retroarch nightly for ios2015-04-16T00:00:00+00:00http://jiaxianhua.github.io/retroarch/2015/04/16/build-retroarch-nightly-for-ios<h2>Download RetroArch</h2>
<hr />
<ul>
<li>open <a href="http://buildbot.libretro.com/nightly/ios/">http://buildbot.libretro.com/nightly/ios/</a> to download the latest RetroArch.</li>
</ul>
<h2>Download Cores</h2>
<hr />
<ul>
<li>open <a href="http://buildbot.libretro.com/nightly/ios/latest/">http://buildbot.libretro.com/nightly/ios/latest/</a> to download latest cores.</li>
</ul>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">http://buildbot.libretro.com/nightly/ios/latest/2048_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/3dengine_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/4do_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/bluemsx_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/bsnes_accuracy_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/bsnes_balanced_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/bsnes_cplusplus98_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/bsnes_mercury_accuracy_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/bsnes_performance_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/catsfc_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/desmume_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/dinothawr_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/dosbox_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/emux_chip8_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/emux_gb_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/emux_nes_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/emux_sms_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/fb_alpha_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/fceumm_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/fmsx_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/fuse_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/gambatte_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/genesis_plus_gx_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/gpsp_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/gw_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/handy_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/hatari_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/lutro_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/mame078_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/mame_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/mednafen_gba_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/mednafen_lynx_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/mednafen_ngp_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/mednafen_pce_fast_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/mednafen_pcfx_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/mednafen_psx_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/mednafen_supergrafx_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/mednafen_vb_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/mednafen_wswan_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/mess_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/meteor_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/mupen64plus_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/nestopia_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/nxengine_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/o2em_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/pcsx_rearmed_interpreter_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/pcsx_rearmed_interpreter_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/pcsx_rearmed_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/picodrive_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/prboom_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/prosystem_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/quicknes_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/scummvm_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/snes9x_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/snes9x_next_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/stella_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/test_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/tgbdual_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/tyrquake_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/ume_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/vba_next_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/vbam_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/vecx_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/virtualjaguar_libretro_ios.dylib.zip
http://buildbot.libretro.com/nightly/ios/latest/yabause_libretro_ios.dylib.zip</code></pre></figure></p>
<h2>download iReSign</h2>
<hr />
<p><a href="https://github.com/maciekish/iReSign">https://github.com/maciekish/iReSign</a></p>
<p><code>git clone https://github.com/maciekish/iReSign</code></p>
<h2>move files</h2>
<hr />
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>unzip RetroArch_xxxx-xx-xx_ios.zip
<span class="gp">$ </span>unzip <span class="k"><em></span>.dylib.zip<br/>
<span class="gp">$ </span>mkdir Payload<br/>
<span class="gp">$ </span>mv RetroArch.app Payload
<span class="gp">$ </span>mv <span class="k"></em></span>.dylib Payload/RetroArch.app/modules/</code></pre></figure></p>
<h2>code sign cores.</h2>
<hr />
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$ </span>cat codesign</p>
<p><span class="c">#!/bin/bash</span></p>
<p><span class="nv">CODE_SIGN_IDENTITY</span><span class="o">=</span><span class="s2">"iPhone Developer: Firstname Lastname (XXXXXXXX)"</span>
<span class="nv">BUILD_PATH</span><span class="o">=</span><span class="s2">"</span><span class="nv">$PWD</span><span class="s2">"</span></p>
<p><span class="nb">echo</span> <span class="s2">"CODESIGNING DYNAMIC LIBRARIES AND BUILDING IPA"</span>
codesign -fs <span class="s2">"</span><span class="nv">$CODE_SIGN_IDENTITY</span><span class="s2">"</span> <span class="nv">$BUILD_PATH</span>/Payload/RetroArch.app/modules/<span class="se">*</span>.dylib</p>
<p><span class="gp">$ </span>./codesign</code></pre></figure></p>
<h2>archieve <strong>RetroArch.ipa</strong></h2>
<hr />
<p><code>$ zip -r RetroArch.ipa Payload</code></p>
<h2>resign RetroArch.ipa use <strong>iReSign.app</strong></h2>
<hr />
<p>open <em>iReSign.app</em></p>
<ol>
<li>Drag your <em>RetroArch.ipa</em> file to the first box, or use the browse button.</li>
<li>Drag your <em>RetroArch.mobileprovision</em> to the second box, or use the browse button.</li>
<li>Select <em>modify ID</em>, input the <em>com.xxx.RetroArch</em> to the fourth box.</li>
<li>Select full certificate name from Keychain Access, for example "iPhone Developer: Firstname Lastname (XXXXXXXXXX)" in the bottom box.</li>
<li>Click ReSign! and wait. The resigned file will be saved in the same folder as the original file.</li>
</ol>
<h2>install RetroArch.ipa use iTunes</h2>
<hr />
<ol>
<li>Donnection you iPhone to iTunes.</li>
<li>Drag your <em>RetroArch-resigned.ipa</em> to iTunes in <strong>App</strong> tab.</li>
<li>Click <strong>Synchronization</strong>.</li>
</ol>
<h2>copy games</h2>
<hr />
<ol>
<li>Open <strong>iFunBox</strong> app.</li>
<li>Drag game to your <strong>iPhone</strong> -> <strong>shared</strong> -> <strong>RetroArch</strong> folder.</li>
</ol>
<h2>play game</h2>
<hr />
<ol>
<li>oepn <strong>RetroArch</strong> on your iPhone.</li>
<li>select <em>Core Selection</em> -> <em>App</em> -> <em>modules</em> -> <em>xxx.dylib</em>.(ex. <em>fcemu_libretro_ios.dylib</em>).</li>
<li>select <em>Load Content</em> -> <em>Home</em> -> <em>Document</em> -> <em>xxx.zip</em>.</li>
</ol>
<h2>set gamepad</h2>
<hr />
<ol>
<li>if you have a GamePad. Connect it use Bluetooth.</li>
<li>click <em>top- middle left</em> to stop the game.</li>
<li>select <em>setting</em> -> <em>Input Settings</em> -> <em>User 1 B</em> to config your GamePad.</li>
<li>bind other key.</li>
<li>if need <em>setting</em> -> <em>Input Settings</em> -> <em>Slow motion</em> -> <em>Clear Keyboard</em>.</li>
<li>select left top button <em>resume</em> to resume game.</li>
</ol>
<h2>enjoy game</h2>
AntiRSI2015-04-15T00:00:00+00:00http://jiaxianhua.github.io/software/2015/04/15/antirsi<h1>AntiRSI</h1>
<hr />
<p><a href="http://antirsi.onnlucky.com/">http://antirsi.onnlucky.com/</a></p>
<p>AntiRSI is a program for Mac OS X that helps prevent RSI (repetitive straininjury) and other computer ralated stress. It does so by forcing you to take regular breaks, yet without getting in the way. It also detects natural breaks so it won't force too many breaks on you.</p>
<p><a href="http://itunes.apple.com/us/app/antirsi/id442007571?mt=12" title="AntiRSI">Mac App Store - AntiRSI</a></p>
<p>AntiRSI is a reminder tool that can be helpful, but it is not a medical device. If you experience problems, please consult a doctor.</p>
<h1>New in the app store version</h1>
<hr />
<ul>
<li>AntiRSI can be removed from the Dock and appear in the menu bar.</li>
<li>Break windows appear on all connected displas.</li>
<li>Larger break window, sreens are no longer 17 inch.</li>
<li>Possibility to disable micro pause or work breaks.</li>
<li>Improved compatibility with media players.</li>
<li>A natural break window (experimental). Helps with reading a lot from the the computer screen. (Feedback appreciated.)</li>
</ul>
<p><img src="/assets/img/software/antirsi.png" title="antirsi" alt="AntiRSI" /></p>
<h1>Old version</h1>
<hr />
<p>Version 2.1 is still available <a href="http://tech.inhelsinki.nl/antirsi/">http://tech.inhelsinki.nl/antirsi/</a>. Incase you don't care much for the new features. Both are written by me (onnlucky). Notice 2.1 is released under the open-source GPL license <a href="http://github.com/onnlucky/antirsi">http://github.com/onnlucky/antirsi</a></p>
<p>If you have ever donated to AntiRSI, please let me know, and I will send you a promo code to get the appstore version for free, as long as I have those available.</p>
<h1>FAQ</h1>
<hr />
<h2>Micro Pause process keeps on resetting</h2>
<p>Usually there can be two causes:</p>
<ol>
<li><p>Media Players provent the screensaver from blanking, unfortunatly macosx doesn't have good support for this, so players inject fake events. AntiRSI cannot distingusish these from real events. But most do only one event every few seconds, this AntiRSI can handle. Some do this many times per second. This AntiRSI cannot handle. You can test for this situation by quitting any media player and see if the problem persist.</p></li>
<li><p>Using an older mouse, or not using a good surface for your mouse. Usually a mouse mat is a good solution. Sometimes speakers can vibrate the surface and move the mouse. You can test for this by turning your mouse upside down, and see if that helps. Or stop the music, see if that helps.</p></li>
</ol>
<h2>I don't think the time between workbreaks is always the same</h2>
<p>Corrent! Because AntiRSI detects natural breaks, and nobody works non-stop for a whole hour, usually the Work Break starts a bit later, taking these small pauses into account.</p>
<h2>Question not answered here?</h2>
<p>Please email <a href="mailto:antirsi@onnlucky.com">antirsi@onnlucky.com</a> with your question. Feedback is also appriciated!</p>
git clone error: RPC failed; result=18, HTTP code = 2002015-04-14T00:00:00+00:00http://jiaxianhua.github.io/git/2015/04/14/git-clone-error-rpc-failed-result18-http-code--200<p><code>git config --global http.postBuffer 524288000</code></p>
iOS开发60分钟入门2015-04-13T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/04/13/ios-60-minutes<p>原文: <a href="https://github.com/qinjx/30min_guides/blob/master/ios.md">https://github.com/qinjx/30min_guides/blob/master/ios.md</a></p>
<h1>iOS开发60分钟入门</h1>
<p>本文面向已有其它语言(如Java,C,PHP,Javascript)编程经验的iOS开发初学者,初衷在于让我的同事一小时内了解如何开始开发iOS App,学习目标包括:</p>
<ul>
<li>能使用Xcode IDE、模拟器</li>
<li>能修改、调试已有iOS App</li>
<li>能在已有应用内创建新模块</li>
<li>能创建新应用</li>
<li>能发布应用到App Store</li>
</ul>
<p>本文不包含任何高级的iOS开发知识,已学会iOS开发的同学不要看,看完这篇文章学会了的同学也不用再看了。</p>
<h2>不仅是学习一门新语言</h2>
<p>有过脚本开发经验的人(如Javascript,PHP,Shell)在刚开始学习iOS开发的时候,会觉得iOS开发的学习曲线比脚本语言要高,是的,这种感觉是对的。因为学iOS开发,不仅是学习一门新语言,它包括:</p>
<ul>
<li>一门语言:Objective-C</li>
<li>一个框架:Cocoa Touch</li>
<li>一个IDE:Xcode</li>
</ul>
<p>初学脚本语言通常不会来绘制图形界面、与人交互,iOS如果不做图形界面,像脚本语言一样处理文本操作数据库,就没啥意思了。</p>
<p>所以,过去我写别的新手入门教程,通常都是写《XXX入门15分钟教程》,而iOS就要花数倍的时间来写了。</p>
<h2>环境准备</h2>
<p>做iOS开发一定要有苹果的软件环境:Mac OS操作系统、Objective-C编译器、设备模拟器等,开发工具倒不一定要用Xcode,只要是个源代码编辑工具就行(vim都行,只是没Xcode那么多功能)。</p>
<h3>Mac OS</h3>
<p>拥有Mac OS环境最简单的方法是找一台苹果电脑,包括iMac, MacBook Pro, MacBook Air, Mac Mini,但不包括苹果的移动设备(iPod Touch, iPhone, iPad, iPad Mini,它们运行的是iOS系统,不是Mac OS),苹果电脑在出厂的时候就会预装Mac OS,目前最新版本是Mac OS X 10.8,主流的版本还有Mac OS X 10.6、Max OS X 10.7。</p>
<p>如果囊中羞涩,可以借一台,或者上淘宝买个二手的。</p>
<h4>黑苹果</h4>
<p>提到iOS开发入门,似乎没办法不说黑苹果。所谓黑苹果,就是把Mac OS改造后安装在非苹果的硬件上,这是违反DMCA法案的,黑苹果的更多资料,<a href="http://en.wikipedia.org/wiki/OSx86">可以在维基上找到</a></p>
<p>苹果电脑价格高,国内软件开发者生存压力大,所以黑苹果在国内也有一些真实的存在,国外当然也有啦。</p>
<p>黑苹果基本可以胜任iOS开发,但有一些问题:</p>
<ul>
<li>安装黑苹果是非法的</li>
<li>个人行为苹果公司一般不会追究,但会遭同行的鄙视</li>
<li>黑苹果超级难装,挑硬件。即使完全相同的型号,相同的批次,也有可能A机器装上了,B机器装不上</li>
<li>黑苹果系统多少都存在一些使用上的问题,像驱动Bug啦、待机恢复蓝屏啦、上网浏览有问题啦</li>
<li>黑苹果不能随意升级,可能升级一次safari就导致整个系统崩溃了</li>
</ul>
<p>上面这些虽然不会直接影响Xcode写代码、模拟器测试,但写着写着想上网查个东西的时候,safari不能翻页,确实挺影响心情的。所以,钱包允许的前提下,还是搞个苹果电脑省心一些。</p>
<h3>Xcode 和 模拟器</h3>
<p>Xcode可以在苹果官网免费下载:<a href="https://developer.apple.com/Xcode/index.php">Xcode下载地址</a></p>
<p>安装Xcode时会自动安装iOS SDK和模拟器。</p>
<p>这么强大的IDE居然是免费的,还是挺让人开心的。</p>
<h2>从改一个现成的应用开始吧</h2>
<p>学一门新软件开发技能,能够第一时间做出一个可运行的产品非常重要,有助于给自己正面激励,我上大学的时候,有很多次想学一门新语言,往往花了半个月,还沉浸在数据类型和语法字典里,连第一个Hello World都没做出来。</p>
<p>这一次,就让我们从改一个现成的应用开始吧。</p>
<h3>下载</h3>
<p>首先,我们从苹果开发者中心下载一个示例代码回来。我选了<a href="https://developer.apple.com/library/ios/samplecode/ToolbarSearch/ToolbarSearch.zip">ToolBarSearch</a>。</p>
<p>在本文档的末尾,还有一些其它的网址可以下载开源iOS产品或者代码段,但我试了一下,还是Apple Sample Code最容易成功。</p>
<p>下载回来的zip文件最好保存在"下载"或者"文稿"目录里,因为在Mac OS 10.8以前,有些目录(例如/var/private/tmp)在Finder中是看不到的,要通过Finder的“前往 > 前往文件夹”功能才能进入。</p>
<h3>打开</h3>
<p>有三种方式可以打开一个iOS Project</p>
<h4>双击project文件</h4>
<p>打开Finder,进入刚刚下载解压的ToolBarSearch目录,找到ToolBarSearch.Xcodeproj文件,双击之,Xcode会自动启动,并打开这个项目</p>
<h4>在Xcode里选择Project打开</h4>
<ul>
<li><p>在Xcode没启动的情况下(如果Xcode已经启动了,就先按Command Q退出),启动Xcode,会弹出“Welcome to Xcode”的欢迎页,点击左下角的“Open Other”按钮,找到ToolBarSearch目录,双击ToolBarSearch目录,或者双击ToolBarSearch.Xcodeproj文件都可以</p></li>
<li><p>如果Xcode处于打开状态,可以点击其菜单栏的File -> Open,或者File -> Open Recent,然后再选择要打开的项目</p></li>
</ul>
<h4>通过命令行打开</h4>
<p>在Mac OS 10.8以前,有些目录(例如/var/private/tmp),在Finder和Xcode的File > Open对话框中,点击鼠标是找不到的,这时候就要通过命令行终端来打开了。</p>
<p>打开终端,执行:</p>
<pre><code>cd /ToolBarSearch的父目录/ToolBarSearch
open -a Xcode
</code></pre>
<p>open -a是mac os的系统命令,除了iOS项目,别的项目也可以这样打开。</p>
<h3>运行刚下载的应用</h3>
<p>点击Xcode左上角的Run按钮(或者同时按下Comman和R键),Xcode会编译源码并在模拟器中运行这个应用。</p>
<p>编译成功会在屏幕上淡淡地显示“Build Succeeded”。反之,失败就显示“Build Failed”且不启动模拟器。</p>
<h3>修改</h3>
<p>在模拟器上看到“Performed search using…”了吧,下面我们改掉它。</p>
<ul>
<li><p>在Xcode左上角的Run按钮下方,有一排小按钮,从左到右第三个是一个放大镜图标,鼠标移上去会显示“Show the Search Navigator”,点一下它,打开搜索界面,在它下方出现的Find输入框中输入“performed”</p></li>
<li><p>搜索结果只有一条:ToolbarSearchViewController.m,点文件名下方被高亮的“Performed”字串,右侧代码编辑区会自动打开这个文件,并滚动屏幕,使包含“Performed”的这一行出现在编辑区的中间。</p></li>
<li><p>修改双引号里的字串,随便改成啥,然后按“Command S”保存。</p></li>
</ul>
<p>当然,这些操作,你也可以在终端下通过grep和vim完成。</p>
<h3>运行修改后的应用</h3>
<p>按Command R运行,看看,是不是看到效果啦?</p>
<p>是的,修改一个应用就这么简单。</p>
<h2>Objective-C</h2>
<p>Objective-C是苹果应用软件(包括苹果电脑上的Mac OS App和移动设备上的iOS App)的开发语言。它是一种面向对象的编程语言。</p>
<p>苹果公司还提供了一个软件,叫Interface Builder,简称IB,用于可视化的界面制作,就像用Dreamweaver做网页,或者像Visual Basic做桌面软件一样。后来IB就整合进了Xcode,成了Xcode的一部分。这篇文档不讲IB,只讲Objective-C,因为:</p>
<ul>
<li>基本上,每一本讲iOS开发的书(纸质书、电子书),都有大量的截图一步一步教如何用IB开发iOS应用,而讲Objective-C开发应用的书却没有那么多。</li>
<li>IB可以用来直观方便地画界面、设置控件属性、建立代码与控件的联系,但后台的业务逻辑和数据处理仍然要靠Objective-C,可见,不管用不用IB,Objective-C都是绕不过去的。</li>
</ul>
<h3>C的超集</h3>
<p>Objective-C扩展了ANSI C,是C的超集,也就是说:</p>
<ul>
<li>任何C源程序,不经修改,即可通过Objective-C编译器成功编译</li>
<li>Objective-C源程序中可以直接使用任何C语言代码</li>
</ul>
<p>除了面向对象有语法是SmallTalk风格的(下面会讲到),其它非面向对象的语法、数据类型,与C完全相同,所以本文就不再赘述。
来看一个经典的Hello World示例吧:</p>
<pre><code>#import <Foundation/Foundation.h>
int main(int argc, char *argv[]){
@autoreleasepool{
NSLog(@"Hello World!");
}
return 0;
}
</code></pre>
<p>是不是仿佛穿越回了大一学习C语言的时代,看起来和C几乎没有区别,是吧?是的,因为还没用到它的面向对象特性,哈哈!</p>
<h3>SmallTalk的消息传递语法风格</h3>
<p>Objective-C的面向对象语法源自SmallTalk,消息传递(Message Passing)风格。在源码风格方面,这是它与C Family语言(包括C/C++、Java、PHP)差别最大的地方。</p>
<p>在Java、C++世界,我们调用一个对象的某方法,在Objective-C里,这称作给类型发送一个消息,这可不仅仅是文字游戏,他们的技术细节也是不同的。</p>
<p>在Java里,对象和方法关系非常严格,一个方法必须属于一个类/对象,否则编译是要报错的。而在Objective-C里,类型和消息的关系比较松散,消息处理到运行时(runtime)才会动态决定,给类型发送一个它无法处理的消息,也只会抛出一个异常,而不会挂掉。</p>
<pre><code>[obj undefinedMethod];
</code></pre>
<p>在代码里调用没定义的方法(这是Java世界的习惯说法啊,专业的叫法是,给obj对象传递它无法处理的消息),Xcode会警告,但编译能成功,运行的时候会出错。它会输出这样一个错误:</p>
<pre><code>Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSObject undefinedMethod]: unrecognized selector sent to instance 0x8871710'
</code></pre>
<h3>类似Java的OOP概念</h3>
<p>Objective-C中一些面向对象的概念,也可以在Java中找到类似的实现(只能说是类似,不是完全相同),我的读者基本都是Java和PHP程序员,我会在下文中尽量用Java的概念来类比。</p>
<p>GoogleCode上有人整理了Java和Objective-C的概念、数据类型对应表,<a href="http://code.google.com/p/j2objc/wiki/JavaConversions">参见这里</a></p>
<h3>字符串</h3>
<p>Objective-C里有字符串是由双引号包裹,并在引号前加一个@符号,例如:</p>
<pre><code>title = @"Hello";
if(title == @"hello") {}
</code></pre>
<p>PHP程序员要注意,在这里不能用单引号,即使只有一个字符也不能用。Objective-C与Java、C一样,双引号表示字符串。</p>
<h3>函数调用</h3>
<p>前文述及,不涉及面向对象时,它和C是完全一样的。以下是几个函数调用的示例:</p>
<h4>不带参数</h4>
<pre><code>startedBlock();
</code></pre>
<h4>带参数</h4>
<pre><code>NSLog(@"decrypted string: %@", str);
CGRectMake(0,0,0,0);
</code></pre>
<h3>传递消息给类/实例方法</h3>
<h4>不带参数</h4>
<pre><code>[obj method];
</code></pre>
<p>对应的Java版本</p>
<pre><code>obj.method();
</code></pre>
<h4>带一个参数:</h4>
<pre><code>[counter increase:1];
</code></pre>
<p>对应的Java版本</p>
<pre><code>counter.increase(1);
</code></pre>
<h4>带多个参数</h4>
<p>对C Family程序员来说,这是最难接受的,最反人类的:</p>
<pre><code>- (void) setColorToRed: (float)red Green: (float)green Blue:(float)blue {...} //定义方法
[myObj setColorToRed: 1.0 Green: 0.8 Blue: 0.2]; //调用方法
</code></pre>
<p>对应的Java版</p>
<pre><code>public void setColorToRedGreenBlue(float red, float green, float blue) {...}
myObj.setColorToRedGreenBlue(1.0, 0.8, 0.2);
</code></pre>
<h4>消息嵌套</h4>
<pre><code>UINavigationBar *bar = [[[UINavigationBar alloc] init] autorelease];
</code></pre>
<p>对应的Java版</p>
<pre><code>UINavigationBar bar = UINavigationBar.alloc().init().autorelease();//Java没有指针,所以星号去掉了
</code></pre>
<h3>类</h3>
<h4>接口和实现</h4>
<p>Objective-C的类分为接口定义和实现两个部分。接口定义(Interface)放在头文件中,文件扩展名是.h,实现(implementation)放在实现文件中,文件扩展名是.m(也有.mm的扩展名,表示Objective-C和C++混编的代码)。</p>
<p><code>接口定义也可以写在.m文件中,但最好不要这么干</code></p>
<p>需要注意的是,与Objective-C的interface概念最接近的是C和C++里的头文件,它与implementation是成双成对出现的,作用是声明类的成员变量和方法。它与Java的interface概念完全不同:</p>
<ul>
<li>Objective-C里,interface有且只有一个实现,Java的interface可以有0-N个实现</li>
<li>Objective-C里,interface可以定义成员属性,Java里不可以</li>
</ul>
<p>在Objective-C里,和Java的Interface概念相似的是Protocol,下文会讲到。</p>
<p>请看示例:</p>
<p>Interface</p>
<pre><code>@interface MyClass {
int memberVar1;
id memberVar2;
}
-(return_type) instance_method1;
-(return_type) instance_method2: (int) p1;
-(return_type) instance_method3: (int) p1 andPar: (int) p2;
@end
</code></pre>
<p>Implementation</p>
<pre><code>@implementation MyClass {
int memberVar3;
}
-(return_type) instance_method1 {
....
}
-(return_type) instance_method2: (int) p1 {
....
}
-(return_type) instance_method3: (int) p1 andPar: (int) p2 {
....
}
@end
</code></pre>
<p>接口和实现以@interface、@implementation开头,都以@end结束。“@”符号在Objective-C中是个很神奇的符号。</p>
<p>冒号也是方法名的一部分,method和method:是两个不同的方法名,不是overload,第二个带参数。</p>
<p>上述代码对应的Java版:</p>
<pre><code>public class MyClass {
protected int memberVar1;
protected pointer memberVar2;
private int memberVar3;
public (return_type) instance_method1() {
....
}
public (return_type) instance_method2(int p1) {
....
}
public (return_type) instance_method3andPar(int p1, int p2) {
....
}
}
</code></pre>
<h4>私有方法和公开方法</h4>
<p>写在.h头文件里的方法都是公开的,Objective-C里没有私有方法的概念(没有你说个蛋啊,哈哈哈哈)。</p>
<p>官方并没有提到Objective-C怎么实现私有方法,我查阅了stackoverflow,统一的答案是,要实现私有方法的效果只能借助Category,不过,根据我的测试,即使采用了Category,也不能阻止外部的代码调用这个“私有方法”,只是Xcode不支持“私有方法”的自动完成,并会有警告提示,运行的时候,还是会成功的。各位看官知道有这么回事就可以了,这里不深讲。</p>
<h4>变量和属性</h4>
<h4>类方法和实例方法</h4>
<h5>类方法</h5>
<p>类方法就是Java、PHP里的Static Method,不用实例化就能调。类方法有一个加号前缀。
示例:</p>
<p>类定义</p>
<pre><code>@interface MyClass
+(void) sayHello;
@end
@implementation MyClass
+(void) sayHello {
NSLog(@"Hello, World");
}
@end
</code></pre>
<p>使用</p>
<pre><code>[MyClass sayHello];
</code></pre>
<h5>实例方法</h5>
<p>实例方法就是Java、PHP里的普通方法,必须实例化才能调。实例方法有一个减号前缀。
示例:</p>
<p>类定义</p>
<pre><code>@interface MyClass : NSObject
-(void) sayHello;
@end
@implementation MyClass
-(void) sayHello {
NSLog(@"Hello, World");
}
@end
</code></pre>
<p>使用</p>
<pre><code>mycls = [MyClass new];
[mycls sayHello];
</code></pre>
<h4>Selector</h4>
<p>selector就是一个方法指针,类似PHP里的动态方法名:</p>
<pre><code><?php
class Hello {
public function sayHello() {}
public function test() {
$fun_name = "sayHello";
$this->$fun_name();
}
}
</code></pre>
<p>在Objective-C里,selector主要用来做两类事情:</p>
<h5>绑定控件触发的动作</h5>
<pre><code>@implementation DemoViewController
- (void)downButtonPressed:(id)sender {//响应“按钮被按下事件”的方法
UIButton *button = (UIButton*)sender;
[button setSelected:YES];
}
- (void)drawAnButton {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = _frame;
btn.tag = 1;
btn.backgroundColor = [UIColor clearColor];
[btn addTarget: self
action: @selector(downButtonPressed:)
forControlEvents: UIControlEventTouchUpInside];//当这个按钮被按下时,触发downButtonPressed:方法
}
@end
</code></pre>
<h5>延时异步执行</h5>
<pre><code>@implementation ETHotDealViewController
- (void)viewDidLoad {
//获取数据源
HotDealDataSource *ds = [[HotDealDataSource alloc]init];
[ds reload];
_items = ds.items;
[self performSelector: @selector(refreshTable)
withObject: self
afterDelay: 0.5];//延迟0.5秒调用refreshTable方法
}
-(void)refreshTable
{
[self.tableView reloadData];
}
@end
</code></pre>
<p>这个例子中,获取数据源是通过ASIHTTP组件异步调用服务端HTTP接口,refreshTable要用到数据源返回回来的数据,如果不延迟0.5秒,就会立刻执行,执行的时候数据还在路上呢,页面就要变空白了。</p>
<h3>继承</h3>
<p>继承是写在Interface定义里面的。语法为:子类名在左,父类名在右,中间用冒号分隔。
示例:</p>
<pre><code>@interface MyClass : NSObject
@end
</code></pre>
<p>对应的Java版本是:</p>
<pre><code>public class MyClass extends NSObject {
}
</code></pre>
<h3>协议(Protocol)</h3>
<p>就是Java、PHP里的Interface。</p>
<h4>协议的定义</h4>
<p>协议的定义用@protocol关键字:</p>
<pre><code>@protocol Printable
-(void)print:(NSString)str;
@end
</code></pre>
<p>对应的Java版本是:</p>
<pre><code>publilc interface Printable {
public void print(String str);
}
</code></pre>
<h5>协议的继承</h5>
<p>协议本身也可以继承别的协议:</p>
<pre><code>@protocol Printable <NSObject>
-(void)print:(NSString)str;
@end
</code></pre>
<p>对应的Java版本:</p>
<pre><code>public interface Printable extends NSObject {
public void print (String str);
}
</code></pre>
<h5>可选方法</h5>
<p>协议可以包含可选方法,顾名思义,可选方法可以不被类实现:</p>
<pre><code>@protocol Printable
@optional
-(void)print:(NSString)str;
@end
</code></pre>
<p>加了@optional关键字,一个类在implements这个协议时,便可以不实现print:方法。</p>
<p>Java里没有类似的实现,除了Collection里会有一些方法带有optional的注释,但Collection是个特例。</p>
<h4>协议的实现</h4>
<p>一个类实现某些协议是写在Interface定义里面的。语法为:协议名用尖括号包裹,多个协议名用逗号隔开,协议写在父类的右边(如果没有父类就直接写在子类右边)。</p>
<p>示例:</p>
<pre><code>@interface class MyClass : NSObject <Printable, Drawable>
@end
</code></pre>
<p>Printable, Drawablw就是两个协议。</p>
<p>对应的Java版本是:</p>
<pre><code>public class MyClass extends NSObject implements Printable, Drawable {
}
</code></pre>
<h3>分类(Category)</h3>
<p>分类可以给一个已经存在的类增加方法,而不用去改它的源码。Java和PHP中都没有类似的特性。</p>
<p>比如说,NSObject是一个Objective-C内置的系统类,我们想给它增加toJson方法,就像这样:</p>
<p>头文件:NSObject+Json.h</p>
<pre><code>@interface NSObject (Json)
-(NSString)toJson;
@end
</code></pre>
<p>实现文件:NSObject+Json.m</p>
<pre><code>@implementation NSObject (Json)
-(NSString)toJson {
//...
}
@end
</code></pre>
<p>使用的时候,只要包含NSObject+Json.h,实例化NSObject类,就可以使用toJson方法了:</p>
<pre><code>import "NSObject+Json.h"
@implatementation XYZController
-(void)test {
NSObject *obj = [[NSObject alloc]init];
NSString *str = [obj toJson];
}
@end
</code></pre>
<p>当然了,NSObject本来的那些方法依然还是可以用的,什么都没变,只是多了个toJson方法。看起来是不是和继承没太多差别呢(除了使用的时候实例化的是NSObject,而不是JsonObject)?再看一个继承实现不了的例子:</p>
<p>头文件:NSObject+Json+XML.h</p>
<pre><code>@interface NSObject (Json)
-(NSString)toJson;
@end
@interface NSObject (XML)
-(NSString)toXML;
@end
</code></pre>
<p>实现文件:NSObject+Json+XML.m</p>
<pre><code>@implementation NSObject (Json)
-(NSString)toJson {
//...
}
@end
@implementation NSObject (XML)
-(NSString)toXML {
//...
}
@end
</code></pre>
<p>使用:</p>
<pre><code>import "NSObject+Json+XML.h"
@implatementation XYZController
-(void)test {
NSObject *obj = [[NSObject alloc]init];
NSString *json = [obj toJson];
NSString *xml = [obj toXML];
}
@end
</code></pre>
<h2>Cocoa Touch</h2>
<p>Cocoa是Mac OS App的开发框架,Cocoa Touch是iOS开发用的框架,Cocoa Touch和Cocoa大部分是一样的,只是Cocoa Touch多了一些移动设备特有的东西,如:触摸屏、加速度传感器、GPS定位。Cocoa中多任务、多窗口的特性,在Cocoa Touch中也是没有的(或者跟Cocoa不完全一样的)。</p>
<p>就像学了Java语言还要再学一些Spring、Hibernate、Struts(或者其它类似的Java类库)才能开始做J2EE应用一样,学过Objective-C语言之后,也要再学习Cocoa Touch框架才能顺利地开发iOS应用。</p>
<h3>最常用设计模式之Delegate</h3>
<p>Cocoa Touch大量使用Delegate(委派)设计模式。</p>
<h3>常用控件:按钮、文本块、图片、输入框</h3>
<h3>TableView</h3>
<h3>WebView</h3>
<h3>导航条</h3>
<h2>Xcode</h2>
<h3>运行</h3>
<p>快捷键:Comman R</p>
<h3>搜索</h3>
<h4>搜索文本</h4>
<h4>搜索文件</h4>
<h3>新建文件/目录</h3>
<p>推荐在Finder中新建好的再添加进来</p>
<h3>断点</h3>
<h2>模拟器和真机测试</h2>
<h3>模拟器测试</h3>
<p>在Xcode中打开你的项目,在Xcode顶部工具栏的Stop按钮(Run按钮右边那个黑色正方形按钮)右边,有个下拉菜单,显示着 “ToolBarSearch > iPhone 5.0 Simulator” (即 你的应用英文名 > 当前选中的调试 ),点击这个下拉菜单,选中iPhone 5.0 Simulator(这里的5.0是指iOS版本,不是iPhone5的意思,如果你的项目是iPad应用,请选iPad 5.0 Simulator),再按“Run”按钮,Xcode就会自动把当前正在编辑开发的应用编译并安装到模拟器上。</p>
<p>在模拟器上操作时,如果执行过程中遇到了你在Xcode里设置的断点,模拟器会暂停运行,并将当前活动窗口切换回Xcode,供你调试。</p>
<p>在Xcode里增加或者取消了断点,不需要重新编译和安装应用即可生效。</p>
<h4>切换被模拟的设备</h4>
<p>模拟器的“硬件”菜单,可以选择想要模拟什么设备,有iPad、iPhone可选。</p>
<ul>
<li>Retina:表示视网膜屏,iPhone(Retina)代表iPhone4,iPhone4S</li>
<li>4-Inch:表示4英寸的iPhone,iPhone(Retina 4-Inch)就是iPhone 5</li>
</ul>
<h4>切换模拟的iOS版本</h4>
<p>在模拟器的“版本”菜单,可以选择要模拟什么版本的iOS。设备和版本是彼此独立的,iPhone 4S可以有5.0,5.1,6.1几种iOS版本,当然了,iPhone 5不可能有4.3的iOS版本。</p>
<h4>触摸屏</h4>
<p>用鼠标点击(不区分左右键)模拟器上的iPhone、iPad屏幕,就是在模拟用手指触摸iPhone,iPad的屏幕,可以实现一些触摸效果比如:</p>
<ul>
<li>鼠标单击 等于 手指轻触</li>
<li>鼠标长按 等于 手指长按(例如你可以在模拟器上长按应用icon调出删除应用的确认框)</li>
<li>鼠标按住拖动 等于 手指拖动</li>
<li>双击和单击模拟器的Home键也等于双击和单击真机的Home键</li>
</ul>
<h5>多指手势</h5>
<p>多指手势比较复杂,在白苹果笔记本上可以模拟简单的双指手势,白苹果的触控板天然支持多指触摸,但要定位到模拟器的区域再响应多指手势就需要借助一些额外的键啦:</p>
<ul>
<li>按住Option键,再用两个手指去操作触摸板,可模拟双指拖动、旋转</li>
<li>按住Option+Shift,可模拟双指合拢</li>
</ul>
<h4>输入法和键盘</h4>
<h5>输入中文</h5>
<p>手机上特有的输入法(比如九宫格输入法)不能模拟。模拟器默认的iOS软键盘只有英文输入,在测试应用的时候,我们要用到中文,有两个办法:</p>
<ul>
<li>使用剪贴板,在Mac OS里复制,再到模拟器运行的应用中的输入框上长按鼠标(模拟手指长按)3秒以上,等弹出“粘贴”的时候选择之,即可。</li>
<li>在模拟器里,按Home键,找到Setting那个App icon(不是Mac OS顶部的模拟器菜单啊,那里没有Setting的),打开被模拟iOS设备的设置,依次点击”General - Keyboard - International Keyboards - Add New Keyboard…”,加个中文键盘,以后就可以使用被模拟iOS设备软件盘输入中文了,跟在iPhone/iPad真机上一样。</li>
</ul>
<h4>使用Mac电脑的键盘</h4>
<p>如果要输入大量文本,使用模拟器里的软键盘效率太低,这时候可以使用物理键盘,方法是:在Mac OS顶部的模拟器菜单栏,点击”硬件”菜单,勾选下拉菜单中的“模拟硬件键盘”。以后再用模拟器运行iOS应用时,点击iOS应用中的输入框,软键盘就不弹出来了,可直接使用Mac电脑的物理键盘输入。</p>
<p><em>注意</em>:</p>
<ul>
<li>模拟器中的iOS接管了物理键盘输入,所以,调用的是模拟器iOS的输入法,不是你的Mac电脑的输入法。打个比方,你的Mac OS装的是搜狗五笔,模拟器中iOS加了个拼音输入法(Add New Keyboard),那么,在iOS应用中输入中文会调用拼音输入法。</li>
<li>要切换模拟器中iOS的中英文输入法,也只能按iOS设备软键盘上的小地球图标,按Mac电脑上的Command+空格键是不行的。</li>
</ul>
<h4>地理位置</h4>
<p>但Mac电脑没有定位用的硬件(GPS)和软件基础,因此模拟器不能自动获得当前的地理位置,不能用模拟器测试定位功能。(注意,虽然WiFi也可以独立定位——iPad WiFi版没有GPS也可以定位,但Mac电脑的WiFi不具备定位相关的软件)</p>
<p>要在模拟器里测试依赖地理位置的功能(如”我附近的xx”),可以手工指定一个经纬度给模拟器,方法:在Mac电脑顶部的模拟器菜单,点击”调试 - 位置 - 自定位置”,会弹出一个对话框,在弹出的框内填入经纬度即可。</p>
<p>如何获得经纬度?
上谷歌地图(ditu.google.cn),在地图上找到你想要的位置(比如你想知道杭州大厦的位置,就在通过搜索框找到杭州大厦),点击右键,选择“这儿是什么”,搜索框中就会出现这个位置的经纬度了,前面是纬度,后面是经度。咱们天朝的版图,都是北纬和东经。</p>
<h4>摄像头</h4>
<p>Mac电脑有摄像头,但Mac OS没有设计API给iOS模拟器调用,所以,不能用模拟器测试对焦闪光灯等功能。</p>
<p>要在模拟器上测试依赖照片的功能,可以在代码里做一个workaround,即当代码检测到摄像头不可用时,弹出一个照片选择器,让测试人员从相册里选择一幅照片,来进行后续的操作(如照片美化、人脸识别、条码扫描)。</p>
<h3>真机测试</h3>
<p>模拟器能验证你开发的iOS应用的大部分功能,但有些Mac设备上不具备的硬件,模拟器是不能模拟的。前文提到了一个绕过这些限制的办法,但获取当前位置、拍照、加速度感应这些是模拟不了的,一款应用发布给消费者之前,必须要在真实设备上验证过。</p>
<p>将未提交App Store审核通过的应用安装到iOS设备上测试,有三种办法:</p>
<ul>
<li>加入苹果的Developer Program,成为付费会员,有了这个付费会员资格,就可以直接在Xcode中点击”Run”将刚刚改过的代码编译打包安装到开发测试用的iOS设备上。在iOS真机上操作被测试的程序能激活Xcode中设置的断点。</li>
<li>越狱iOS设备。将iPhone和iPad越狱后,可以通过SSH直接上传Xcode编译好的ipa包(一个iOS App本质上就是一个ipa包)。</li>
<li>越狱的iOS设备,配合破解过的Xcode,甚至可以实现和付费开发者计划一样的功能:在Xcode上点击”Run”,就自动编译安装到iOS设备上去运行了</li>
<li>企业部署方案。就像阿里巴巴的<a href="http://xyj.im">轩辕剑</a>一样,用iPhone/iPad访问这个网址,点击里面的轩辕剑链接就可以安装轩辕剑这个应用了。</li>
</ul>
<p>破解Xcode是违法行为(越狱是合法的),而且挑版本挑得厉害,不是所有Xcode版本都能破解,也不是所有Xcode的破解版都能和越狱的iOS配合好。越狱+SSH上传跟企业部署一样效率低(部署效率低,无法激活Xcode中的断点),只能用于QA验收,不适合开发自测。综上所述,最适合开发实时测试的就是第一个正规途径了。下面重点讲这个:</p>
<h4>拥有一个开发者账号</h4>
<p>苹果的Developer Program分为个人开发者和公司开发者,分别是每年99美元和每年299美元,分别可以注册100台和500台苹果测试设备。这个台数限制在一个付费年度内不会清空,比如说,2013年4月1日付费成功的,付费会员资格在2014年3月31日之前有效,这期间,注册一台就少一个名额,哪怕这个设备注册进来用了之后一分钟马上又删掉了,减少的这个名额也不会回来。</p>
<p>在交钱之前,最好问一下,周围的同事,有没有已经交了钱的。如果有,你只需要注册一个免费的Apple ID(就是你在App Store安装软件用的Apple ID),请他发个邀请邮件给你,把你的Apple ID加入他的团队就可以了,苹果会认为你们两个人是一个团队的,你们分别用自己的账号,共享100台设备的限额,这是合法的。</p>
<h4>安装证书和私钥</h4>
<h5>证书</h5>
<p>不想看下面各种点击各种页面跳转的直接用浏览器访问<a href="https://developer.apple.com/ios/manage/certificates/team/index.action">证书管理</a>,你要登录你就用Apple ID登录(前提是交过钱,或者找交了钱的人把你加入团队了)。</p>
<blockquote><p>不嫌烦,或者想知道下次没我这个文档的时候怎么进证书管理吗?按这个步骤操作:</p></blockquote>
<ul>
<li>进入 <a href="https://developer.apple.com/">苹果开发者中心</a></li>
<li>点击iOS Dev Center</li>
<li>点蓝色“Login”按钮,用你的Apple ID登录,登录成功会跳到 <a href="https://developer.apple.com/devcenter/ios/index.action">开发者首页</a></li>
<li>点击右上角的<a href="https://developer.apple.com/ios/manage/overview/index.action">iOS Provisioning Portal</a>(别找了,直接Command F搜索多好)</li>
<li>点左侧菜单栏里的<a href="https://developer.apple.com/ios/manage/certificates/team/index.action">Certificates</a></li>
</ul>
<p>页面上有一个“Your Certificate”区域,下方有个Download圆角按钮,这是你的个人证书,下载下来。再下面一行,有一句“If you do not have the WWDR intermediate certificate installed, <a href="https://developer.apple.com/certificationauthority/AppleWWDRCA.cer">click here to download now</a>”,这个是苹果的公共证书,也下下来。</p>
<p>双击下载回来的证书,装证书时,会提示你输入密码,这是【钥匙串访问工具】在问你要你的Mac OS账号开机密码(相当于linux里面的sudo),不是Apple ID的密码,不要搞错了。</p>
<h5>安装私钥</h5>
<p>如果你是和其它同事公用的账号,让他给你一个私钥即可,就是一个扩展名为p12的文件,双击之,钥匙串访问会自动出来,需要你输入一个密码,这个密码问给你p12文件的人要,不是你的Mac OS系统开机密码,也不是你的Apple ID密码。</p>
<h4>将设备注册到Provisioning Portal</h4>
<ul>
<li>打开Xcode,从Xcode的Window菜单中找到Organizer,打开之(Shift Command 2)。</li>
<li>把iOS设备连上电脑,Organizer会自动识别出你的设备,并显示在左侧边栏。</li>
<li>在Organizer左侧边栏找到你的设备,右键,点击“Add Device to Provisioning Portal”,然后等Organizer提示你操作成功即可。(选中设备后,右边设备详情区域会显示一个按钮“Use for Development”,点它也可以)。</li>
</ul>
<h4>到iOS真机上运行测试版程序</h4>
<p>回到Xcode主界面,在Stop按钮(Run按钮右边那个黑色正方形按钮)右边,有个下拉菜单,显示着 “ToolBarSearch > iPhone 5.0 Simulator” (即 你的应用英文名 > 当前选中的调试 ),点击这个下拉菜单,选中你的真机设备名,再按“Run”按钮,Xcode就会自动把当前正在编辑开发的应用编译并安装到真机上测试啦!</p>
<h4>发布到App Store</h4>
<h4>打IPA包</h4>
<p>IPA包本质上是一个ZIP压缩包,只不过它有着特殊的目录结构,扩展名是ipa,制作方法如下:</p>
<ul>
<li>在Xcode中Build项目,快捷键Command B</li>
<li>在左侧项目导航器中,展开Products文件夹,找到你要打包的应用,你的应用名.app,右键,选择show in finder</li>
<li>到Finder中Copy这个.app目录(选中,按Command C),复制到一个你新建的名为Payload(区分大小写)的文件夹中</li>
<li>找到你的应用Logo,即一个512 * 512像素的PNG文件,copy到与Payload一起(与Payload并列,不要放进Payload了),并重命名为iTunesArtwork(区分大小写,没有扩展名)</li>
<li>将Payload目录、ItunesArtwork文件打成一个zip包,并更改扩展名为ipa</li>
<li>双击这个ipa文件,会用iTunes打开,如果打开成功,且在iTunes里有应用Logo显示,就成功了</li>
</ul>
<h4>批量自动打包</h4>
<p>除App Store外,还有许多其它的iOS应用市场(如91助手,同步推等等),如果一个应用需要发布到很多个应用市场,且他们的代码略有不同(比如说,统计代码不同),按上述方法手工修改源码再打包,再还原,比较容易出错。好消息是,Xcode是有命令行的,我们可以写一个shell脚本,先用se自动修改源码,再调用Xcode的命令行来编译以得到your——app.app目录,最后调用zip、mv等命令把上一个章节讲的ipa打包动作自动执行。</p>
<h2>阅读应用代码</h2>
<h2>从头新建一个应用:Hello World</h2>
<h2>其它</h2>
<h3>代码里的控件尺寸</h3>
<p>iOS App里的控件尺寸和字体大小都是指Point,Retina设备(iPhone 4,4S,5;the new Pad)和非Retina设备(iPhone 3GS,iPad,iPad 2)的Point数是一样的,尽管iPhone 4的分辨率是3GS的2倍。比如说,10point在Retina设备里是20 pixel,在非Retina设备(iPhone 3G)上则是10 pixel。</p>
<p>项目成员间交流时,应使用Point,不要使用pixel。</p>
<h3>SVN操作含有@符号的文件</h3>
<p>iOS应用中经常出现xxxx@2x.png这样的文件名,它们是给retina设备用的高分辨率大图,用svn命令行操作它们的时候会被@符号干扰,解决方案是在svn命令末尾加上一个@符号,如:</p>
<pre><code>svn del icon@2x.png@
svn info Default@2x.png@
</code></pre>
<p>如果一次移动了几十个png文件再svn commit的,可以用shell批处理:</p>
<pre><code>svn status | awk '($1=="!"){print $2}' | grep -v @ | xargs svn del
</code></pre>
<p>上面这个命令是将文件名不包含@符号的,且已经不在硬盘上的文件从svn version controll中删掉</p>
<pre><code>for file in `svn status | awk '($1=="!"){print $2}' `; do svn del $file"@"; done
</code></pre>
<p>上面这个命令是将文件名包含@符号的,且已经不在硬盘上的文件从svn version controll中删掉</p>
<p>svn add同上, 如法炮制即可.</p>
<h3>Xcode中的代码结构与操作系统上的文件系统并不一致</h3>
<p>推荐在Finder里建好目录再到Xcode的Project Navigator中点击“Add Files to”添加到项目中</p>
<h3>iPhone 5适配</h3>
<p>iPhone 5与之前的iPhone不一样,采用了4寸Retina屏,所以它的Point数变成了320 * 568 points</p>
<h3>开源代码</h3>
<ul>
<li><a href="https://developer.apple.com/library/ios/navigation/#section=Resource%20Types&topic=Sample%20Code">Apple官方的Sample Code</a></li>
<li><a href="http://en.wikipedia.org/wiki/List_of_free_and_open_source_iOS_applications">维基百科上的开源iOS App</a></li>
<li><a href="http://www.iosopensource.com/">iOS Opensource</a> --Domain Parking了,以前可以下载Twitter和Wordpress客户端的</li>
<li><a href="http://code4app.com/">code 4 app</a></li>
<li><a href="http://ui4app.com/">UI 4 app</a>, code4app的姐妹站</li>
</ul>
<h3>Objective-C教程</h3>
<ul>
<li><a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html">Apple官方教程</a></li>
<li><a href="http://cocoadevcentral.com/d/learn_objectivec/">Cocoa Dev Center</a></li>
<li><a href="http://zh.wikipedia.org/wiki/Objective-C">维基上的Objective-C语言简介</a> --中文,十分钟可读完,推荐</li>
</ul>
unix 缩写风格2015-04-12T00:00:00+00:00http://jiaxianhua.github.io/linux/2015/04/12/unix-abbreviation<p>原文: <a href="http://i.linuxtoy.org/docs/guide/ch02s02.html">http://i.linuxtoy.org/docs/guide/ch02s02.html</a></p>
<h1>缩写习惯</h1>
<hr />
<p>构建于图表界面之上的操作系统,使用鼠标作为主输入设备,是否使用缩写并不重要。比如Windows系统中的目录,几乎都是全称。。。点击两次鼠标进入文件夹pf,并不意味着点击13次才能进入文件夹Program Files</p>
<p>而构建于命令行之上的操作系统,如Linux,只要3个字母以上的单词,几乎都要缩写。例如:cd命令是Change Directory的缩写。作为常用命令,如果使用它的全称Change Directory绝对是无聊和乏味的工作。</p>
<h2>最常见的缩写,取每个单词的首字母,如</h2>
<p><strong>cd</strong> Change Directory<br/>
<strong>dd</strong> Disk Dump<br/>
<strong>df</strong> Disk Free<br/>
<strong>du</strong> Disk Usage <br/>
<strong>pwd</strong> Print Working Directory <br/>
<strong>ps</strong> Processes Status <br/>
<strong>PS</strong> Prompt Strings <br/>
<strong>su</strong> Substitute User <br/>
<strong>rc</strong> Run Command <br/>
<strong>Tcl</strong> Tool Command Language <br/>
<strong>cups</strong> Common Unix Printing System <br/>
<strong>apt</strong> Advanced Packageing Tool <br/>
<strong>bg</strong> BackGround <br/>
<strong>ping</strong> Packet InterNet Grouper</p>
<h2>如果首字母后为<strong>h</strong>,通常保留</h2>
<p><strong>chsh</strong> CHange SHell<br/>
<strong>chmod</strong> CHange MODe<br/>
<strong>chown</strong> CHange OWNer<br/>
<strong>chgrp</strong> CHange GRoup<br/>
<strong>bash</strong> Bourne Again SHell<br/>
<strong>zsh</strong> Z SHell<br/>
<strong>ksh</strong> Korn SHell
<strong>ssh</strong> Secure SHell</p>
<h2>递归缩写也属于这一类,如:</h2>
<p><strong>GNU</strong> GNU's Not Unix<br/>
<strong>PHP</strong> PHP: Hypertext Preprocessor<br/>
<strong>RPM</strong> RPM Package Manager<br/>
<strong>WINE</strong> WINE Is Not an Emulator<br/>
<strong>PNG</strong> PNG's Not Gif<br/>
<strong>nano</strong> Nano's ANOther editor</p>
<p>** 有些缩写可能有多种定义,如:</p>
<blockquote><p><strong>rpm</strong></p>
<p>RPM Package Manager</p>
<p>RetHad Package Manager</p>
<p><strong>bc</strong></p>
<p>Basic Calculator</p>
<p>Better Calculator</p></blockquote>
<h2>这方面Emacs可谓独领风骚:</h2>
<pre><code>Emacs May Allow Customized Screwups
Emacs Manuals Are Cryptic and Surreal
Eventually Munches All Computer Storage
Eight Megabytes And Constantly Swapping
Elsewhere Maybe All Commands are Simple
Excellent Manuals Are Clearly Suppressed
Emacs May Alienate Clients and Supporters
Except by Middle Aged Computer Scientists
Extended Macros Are Considered Superfluous
Every Mode Accelerates Creation of Software
Each Manual's Audience is Completely Stupefied
Exceptionally Mediocre Algorithm for Computer Scientists
Easily Maintained with the Assistance of Chemical Solutions
Eradication of Memory Accomplished with Complete Simplicity
</code></pre>
<h2>如果只有一个单词,通常取每个章节的首字母:</h2>
<p><strong>cp</strong> CoPy<br/>
<strong>ln</strong> LiNk<br/>
<strong>ls</strong> LiSt<br/>
<strong>mv</strong> MoVe<br/>
<strong>rm</strong> ReMove</p>
<h2>对于目录,通常使用前几个字母作为缩写:</h2>
<p><strong>bin</strong> BINaries<br/>
<strong>dev</strong> DEVices<br/>
<strong>etc</strong> ETCetera<br/>
<strong>lib</strong> LIBrary<br/>
<strong>var</strong> VARiable<br/>
<strong>proc</strong> PROCesses<br/>
<strong>sbin</strong> Superuser BINaries<br/>
<strong>tmp</strong> TeMPorary<br/>
<strong>usr</strong> Unix Shared Resources</p>
<h2>这种缩写的其它情况</h2>
<p><strong>diff</strong> DIFFerences<br/>
<strong>cal</strong> CALendar<br/>
<strong>cat</strong> CATenate<br/>
<strong>ed</strong> EDitor<br/>
<strong>exec</strong> EXECute<br/>
<strong>tab</strong> TABle<br/>
<strong>regexp</strong> REGular EXPression</p>
<h2>如果某种缩写比较深入人心,例如<strong>mesg</strong>代表<strong>message</strong>,在新的复合缩写中,将沿用这种缩写方式</h2>
<p><strong>dmesg</strong> Disgnostic MESsaGe<br/>
<strong>sed</strong> Stream EDitor<br/>
<strong>stty</strong> Set TTY<br/>
<strong>fstab</strong> FileSystem TABle<br/>
<strong>passwd</strong> PASSWorD</p>
<h2>有些缩写中,第一个字母<strong>g</strong>,代表<strong>GNU</strong></h2>
<p><strong>awk</strong> Aho Weiberger and Kernighan<br/>
<strong>gawk</strong> GNU AWK<br/>
<strong>gpg</strong> GNU Privacy Guard<br/>
<strong>grep</strong> GNU Regular Expression Print<br/>
<strong>egrep</strong> Extended GREP</p>
<hr />
<p>定义中包含自身缩写,如 GNU:</p>
<blockquote><p>GNU's Not Unix</p></blockquote>
<p>使用这个定义来解释定义中的缩写:</p>
<blockquote><p>(GNU's Not Unix)'s Not Unix</p></blockquote>
<p>这意味着它是可以无限递归的:</p>
<blockquote><p>(((((GNU's Not Unix)'s Not Unix)'s Not Unix)'s Not Unix)'s Not Unix)'s Not Unix ……</p></blockquote>
Functional Programming For The Rest of Us en2015-04-11T00:00:00+00:00http://jiaxianhua.github.io/book/2015/04/11/functional-programming-for-the-rest-of-us-en<p><a href="https://github.com/justinyhuang/Functional-Programming-For-The-Rest-of-Us-Cn">https://github.com/justinyhuang/Functional-Programming-For-The-Rest-of-Us-Cn</a></p>
<h1>Functional Programming For The Rest of Us</h1>
<p>Monday, June 19, 2006</p>
<h3>Introduction</h3>
<p>Programmers are procrastinators. Get in, get some coffee, check the mailbox, read the RSS feeds, read the news, check out latest articles on techie websites, browse through political discussions on the designated sections of the programming forums. Rinse and repeat to make sure nothing is missed. Go to lunch. Come back, stare at the IDE for a few minutes. Check the mailbox. Get some coffee. Before you know it, the day is over.</p>
<p>The only thing, every once in a while challenging articles actually do pop up. If you're looking at the right places you'll find at least one of these every couple of days. These articles are hard to get through and take some time, so they start piling up. Before you know it, you have a list of links and a folder full of PDF files and you wish you had a year in a small hut in the middle of the forest with nobody around for miles so you could catch up. Would be nice if someone came in every morning while you're taking a walk down the river to bring some food and take out the garbage.</p>
<p>I don't know about your list, but a large chunk of the articles in mine are about functional programming. These generally are the hardest to get through. Written in a dry academic language, even the "ten year Wall Street industry veterans" don't understand what functional programming (also referred to as FP) articles are all about. If you ask a project manager in Citi Group or in Deutsche Bank<sup>1</sup> why they chose to use JMS instead of Erlang they'll say they can't use academic languages for industrial strength applications. The problem is, some of the most complex systems with the most rigid requirements are written using functional programming elements. Something doesn't add up.</p>
<p>It's true that FP articles and papers are hard to understand, but they don't have to be. The reasons for the knowledge gap are purely historical. There is nothing inherently hard about FP concepts. Consider this article "an accessible guide to FP", a bridge from our imperative minds into the world of FP. Grab a coffee and keep on reading. With any luck your coworkers will start making fun of you for your FP comments in no time.</p>
<p>So what is FP? How did it come about? Is it edible? If it's as useful as its advocates claim, why isn't it being used more often in the industry? Why is it that only people with PhDs tend to use it? Most importantly, why is it so damn hard to learn? What is all this closure, continuation, currying, lazy evaluation and no side effects business? How can it be used in projects that don't involve a university? Why does it seem to be so different from everything good, and holy, and dear to our imperative hearts? We'll clear this up very soon. Let's start with explaining the reasons for the huge gap between the real world and academic articles. The answer is as easy as taking a walk in the park.</p>
<h3>A Walk In The Park</h3>
<p>Fire up the time machine. Our walk in the park took place more than two thousand years ago, on a beautiful sunny day of a long forgotten spring in 380 B.C. Outside the city walls of Athens, under the pleasant shade of olive trees Plato was walking towards the Academy with a beautiful slave boy. The weather was lovely, the dinner was filling, and the conversation turned to philosophy.</p>
<p>"Look at these two students", said Plato carefully picking words to make the question educational. "Who do you think is taller?" The slave boy looked towards the basin of water where two men were standing. "They're about the same height", he said. "What do you mean 'about the same'?", asked Plato. "Well, they look the same from here but I'm sure if I were to get closer I'd see that there is some difference."</p>
<p>Plato smiled. He was leading the boy in the right direction. "So you would say that there is nothing perfectly equal in our world?" After some thinking the boy replied: "I don't think so. Everything is at least a little different, even if we can't see it." The point hit home! "Then if nothing is perfectly equal in this world, how do you think you understand the concept of 'perfect' equality?" The slave boy looked puzzled. "I don't know", he replied.</p>
<p>So was born the first attempt to understand the nature of mathematics. Plato suggested that everything in our world is just an approximation of perfection. He also realized that we understand the concept of perfection even though we never encountered it. He came to conclusion that perfect mathematical forms must live in another world and that we somehow know about them by having a connection to that "alternative" universe. It's fairly clear that there is no perfect circle that we can observe. But we also understand what a perfect circle is and can describe it via equations. What is mathematics, then? Why is the universe described with mathematical laws? Can all of the phenomena of our universe be described by mathematics?<sup>2</sup></p>
<p>Philosophy of mathematics is a very complex subject. Like most philosophical disciplines it is far more adept at posing questions rather than providing answers. Much of the consensus revolves around the fact that mathematics is really a puzzle: we set up a set of basic non-conflicting principles and a set of rules on how to operate with these principles. We can then stack these rules together to come up with more complex rules. Mathematicians call this method a "formal system" or a "calculus". We can effectively write a formal system for Tetris if we wanted to. In fact, a working implementation of Tetris is a formal system, just specified using an unusual representation.</p>
<p>A civilization of furry creatures on Alpha Centauri would not be able to read our formalisms of Tetris and circles because their only sensory input might be an organ that senses smells. They likely will never find out about the Tetris formalism, but they very well might have a formalism for circles. We probably wouldn't be able to read it because our sense of smell isn't that sophisticated, but once you get past the representation of the formalism (via various sensory instruments and standard code breaking techniques to understand the language), the concepts underneath are understandable to any intelligent civilization.</p>
<p>Interestingly if no intelligent civilization ever existed in the universe the formalisms for Tetris and circles would still hold water, it's just that nobody would be around to find out about them. If an intelligent civilization popped up, it would likely discover some formalisms that help describe the laws of our universe. They also would be very unlikely to ever find out about Tetris because there is nothing in the universe that resembles it. Tetris is one of countless examples of a formal system, a puzzle, that has nothing to do with the real world. We can't even be sure that natural numbers have full resemblance to the real world, after all one can easily think of a number so big that it cannot describe anything in our universe since it might actually turn out to be finite.</p>
<h3>A Bit of History</sup>3</h3>
<p>Let's shift gears in our time machine. This time we'll travel a lot closer, to the 1930s. The Great Depression was ravaging the New and the Old worlds. Almost every family from every social class was affected by the tremendous economic downturn. Very few sanctuaries remained where people were safe from the perils of poverty. Few people were fortunate enough to be in these sanctuaries, but they did exist. Our interest lies in mathematicians in Princeton University.</p>
<p>The new offices constructed in gothic style gave Princeton an aura of a safe haven. Logicians from all over the world were invited to Princeton to build out a new department. While most of America couldn't find a piece of bread for dinner, high ceilings, walls covered with elaborately carved wood, daily discussions by a cup of tea, and walks in the forest were some of the conditions in Princeton.
The new offices constructed in gothic style gave Princeton an aura of a safe haven. Logicians from all over the world were invited to Princeton to build out a new department. While most of America couldn't find a piece of bread for dinner, high ceilings, walls covered with elaborately carved wood, daily discussions by a cup of tea, and walks in the forest were some of the conditions in Princeton.</p>
<p>One mathematician living in such lavish lifestyle was a young man named Alonzo Church. Alonzo received a B.S. degree from Princeton and was persuaded to stay for graduate school. Alonzo felt the architecture was fancier than necessary. He rarely showed up to discuss mathematics with a cup of tea and he didn't enjoy the walks in the woods. Alonzo was a loner: he was most productive when working on his own. Nevertheless Alonzo had regular contacts with other Princeton inhabitants. Among them were Alan Turing, John von Neumann, and Kurt Gödel.</p>
<p>The four men were interested in formal systems. They didn't pay much heed to the physical world, they were interested in dealing with abstract mathematical puzzles instead. Their puzzles had something in common: the men were working on answering questions about computation. If we had machines that had infinite computational power, what problems would we be able to solve? Could we solve them automatically? Could some problems remain unsolved and why? Would various machines with different designs be equal in power?</p>
<p>In cooperation with other men Alonzo Church developed a formal system called lambda calculus. The system was essentially a programming language for one of these imaginary machines. It was based on functions that took other functions as parameters and returned functions as results. The function was identified by a Greek letter lambda, hence the system's name</sup>4. Using this formalism Alonzo was able to reason about many of the above questions and provide conclusive answers.</p>
<p>Independently of Alonzo Church, Alan Turing was performing similar work. He developed a different formalism (now referred to as the Turing machine), and used it to independently come to similar conclusions as Alonzo. Later it was shown that Turing machines and lambda calculus were equivalent in power.</p>
<p>This is where the story would stop, I'd wrap up the article, and you'd navigate to another page, if not for the beginning of World War II. The world was in flames. The U.S. Army and Navy used artillery more often than ever. In attempts to improve accuracy the Army employed a large group of mathematicians to continuously calculate differential equations required for solving ballistic firing tables. It was becoming obvious that the task was too great for being solved manually and various equipment was developed in order to overcome this problem. The first machine to solve ballistic tables was a Mark I built by IBM - it weighed five tons, had 750,000 parts and could do three operations per second.</p>
<p>The race, of course, wasn't over. In 1949 an Electronic Discrete Variable Automatic Computer (EDVAC) was unveiled and had tremendous success. It was a first example of von Neumann's architecture and was effectively a real world implementation of a Turing machine. For the time being Alonzo Church was out of luck.</p>
<p>In late 1950s an MIT professor John McCarthy (also a Princeton graduate) developed interest in Alonzo Church's work. In 1958 he unveiled a List Processing language (Lisp). Lisp was an implementation of Alonzo's lambda calculus that worked on von Neumann computers! Many computer scientists recognized the expressive power of Lisp. In 1973 a group of programmers at MIT's Artificial Intelligence Lab developed hardware they called a Lisp machine - effectively a native hardware implementation of Alonzo's lambda calculus!</p>
<h3>Functional Programming</h3>
<p>Functional programming is a practical implementation of Alonzo Church's ideas. Not all lambda calculus ideas transform to practice because lambda calculus was not designed to work under physical limitations. Therefore, like object oriented programming, functional programming is a set of ideas, not a set of strict guidelines. There are many functional programming languages, and most of them do many things very differently. In this article I will explain the most widely used ideas from functional languages using examples written in Java (yes, you could write functional programs in Java if you felt particularly masochistic). In the next couple of sections we'll take Java as is, and will make modifications to it to transform it into a useable functional language. Let's begin our quest.</p>
<p>Lambda calculus was designed to investigate problems related to calculation. Functional programming, therefore, primarily deals with calculation, and, surprisingly, uses functions to do so. A function is a very basic unit in functional programming. Functions are used for almost everything, even the simplest of calculations. Even variables are replaced with functions. In functional programming variables are simply aliases for expressions (so we don't have to type everything on one line). They cannot be modified. All variables can only be assigned to once. In Java terms this means that every single variable is declared as final (or const if we're dealing with C++). There are no non-final variables in FP.</p>
<pre><code class="java">final int i = 5;
final int j = i + 3;
</code></pre>
<p>Since every variable in FP is final two fairly interesting statements can be made. It does not make sense to always write the keyword final and it does not make sense to call variables, well... variables. We will now make two modifications to Java: every variable declared in our functional Java will be final by default, and we will refer to variables as symbols.</p>
<p>By now you are probably wondering how you could possibly write anything reasonably complicated in our newly created language. If every symbol is non-mutable we cannot change the state of anything! This isn't strictly true. When Alonzo was working on lambda calculus he wasn't interested in maintaining state over periods of time in order to modify it later. He was interested in performing operations on data (also commonly referred to as "calculating stuff"). However, it was proved that lambda calculus is equivalent to a Turing machine. It can do all the same things an imperative programming language can. How, then, can we achieve the same results?</p>
<p>It turns out that functional programs can keep state, except they don't use variables to do it. They use functions instead. The state is kept in function parameters, on the stack. If you want to keep state for a while and every now and then modify it, you write a recursive function. As an example, let's write a function that reverses a Java string. Remember, every variable we declare is final by default<sup>5</sup>.</p>
<pre><code class="java">String reverse(String arg) {
if(arg.length == 0) {
return arg;
}
else {
return reverse(arg.substring(1, arg.length)) + arg.substring(0, 1);
}
}
</code></pre>
<p>This function is slow because it repeatedly calls itself<sup>6</sup>. It's a memory hog because it repeatedly allocates objects. But it's functional in style. You may be interested why someone would want to program in this manner. Well, I was just about to tell you.</p>
<h3>Benefits of FP</h3>
<p>You're probably thinking that there's no way I can rationalize the monstrosity of a function above. When I was learning functional programming I was thinking that too. I was wrong. There are very good arguments for using this style. Some of them are subjective. For example, people claim that functional programs are easier to understand. I will leave out these arguments because every kid on the block knows that ease of understanding is in the eye of the beholder. Fortunately for me, there are plenty of objective arguments.</p>
<h4>Unit Testing</h4>
<p>Since every symbol in FP is final, no function can ever cause side effects. You can never modify things in place, nor can one function modify a value outside of its scope for another function to use (like a class member or a global variable). That means that the only effect of evaluating a function is its return value and the only thing that affects the return value of a function is its arguments.</p>
<p>This is a unit tester's wet dream. You can test every function in your program only worrying about its arguments. You don't have to worry about calling functions in the right order, or setting up external state properly. All you need to do is pass arguments that represent edge cases. If every function in your program passes unit tests you can be a lot more confident about quality of your software than if you were using an imperative language. In Java or C++ checking a return value of a function is not sufficient - it may modify external state that we would need to verify. Not so in a functional language.</p>
<h4>Debugging</h4>
<p>If a functional program doesn't behave the way you expect it to, debugging it is a breeze. You will always be able to reproduce your problem because a bug in a functional program doesn't depend on unrelated code paths that were executed before it. In an imperative program a bug resurfaces only some of the time. Because functions depend on external state produced by side effects from other functions you may have to go through a series of steps in no way related to the bug. In a functional program this isn't the case - if a return value of a function is wrong, it is always wrong, regardless of what code you execute before running the function.</p>
<p>Once you reproduce the problem, getting to the bottom of it is trivial. It is almost pleasant. You break the execution of your program and examine the stack. Every argument in every function call in the stack is available for your inspection, just like in an imperative program. Except in an imperative program that's not enough because functions depend on member variables, global variables, and the state of other classes (which in turn depend on these very same things). A function in a functional program depends only on its arguments, and that information is right before your eyes! Furthermore, in an imperative program examining a return value of a function will not give you a good idea of whether the function behaves properly. You need to hunt down dozens of objects outside its scope to verify that it performed correct actions. In a functional program all you have to do is look at the return value!</p>
<p>Walking through the stack you look at arguments passed to functions and their return values. The minute a return value doesn't make sense you step into the offending function and walk through it. You repeat this recursively until the process leads you to the source of the bug!</p>
<h4>Concurrency</h4>
<p>A functional program is ready for concurrency without any further modifications. You never have to worry about deadlocks and race conditions because you don't need to use locks! No piece of data in a functional program is modified twice by the same thread, let alone by two different threads. That means you can easily add threads without ever giving conventional problems that plague concurrency applications a second thought!</p>
<p>If this is the case, why doesn't anybody use functional programs for highly concurrent applications? Well, it turns out that they do. Ericsson designed a functional language called Erlang for use in its highly tolerant and scalable telecommunication switches. Many others recognized the benefits provided by Erlang and started using it. We're talking about telecommunication and traffic control systems that are far more scalable and reliable than typical systems designed on Wall Street. Actually, Erlang systems are not scalable and reliable. Java systems are. Erlang systems are simply rock solid.</p>
<p>The concurrency story doesn't stop here. If your application is inherently single threaded the compiler can still optimize functional programs to run on multiple CPUs. Take a look at the following code fragment:</p>
<pre><code class="java">String s1 = somewhatLongOperation1();
String s2 = somewhatLongOperation2();
String s3 = concatenate(s1, s2);
</code></pre>
<p>In a functional language the compiler could analyze the code, classify the functions that create strings s1 and s2 as potentially time consuming operations, and run them concurrently. This is impossible to do in an imperative language because each function may modify state outside of its scope and the function following it may depend on it. In functional languages automatic analysis of functions and finding good candidates for concurrent execution is as trivial as automatic inlining! In this sense functional style programs are "future proof" (as much as I hate buzzwords, I'll indulge this time). Hardware manufacturers can no longer make CPUs run any faster. Instead they increase the number of cores and attribute quadruple speed increases to concurrency. Of course they conveniently forget to mention that we get our money's worth only on software that deals with parallelizable problems. This is a very small fraction of imperative software but 100% of functional software because functional programs are all parallelizable out of the box.</p>
<h4>Hot Code Deployment</h4>
<p>In the old days of Windows in order to install updates it was necessary to restart the computer. Many times. After installing a newer version of a media players. With Windows XP the situation has improved significantly, yet it still isn't ideal (I ran Windows Update at work today and now an annoying system tray icon won't go away until I restart). Unix systems have had a better model for a while. In order to install an update you only need to stop relevant components, not the whole OS. While it is a better situation, for a large class of server applications it still isn't acceptable. Telecommunication systems need to be up 100% of the time because if dialing emergency is not available due to upgrades, lives may be lost. There is no reason Wall Street firms need to bring down their systems to install software updates over the weekend.</p>
<p>An ideal situation is updating relevant parts of the code without stopping any part of the system at all. In an imperative world this isn't possible. Consider unloading a Java class at runtime and reloading a new definition. If we were to do that every instance of a class would become unusable because the state it holds would be lost. We would need to resort to writing tricky version control code. We'd need to serialize all running instances of the class, destroy them, create instances of the new class, try to load serialized data into them hoping the loading code properly migrates the data to work with the new instance. On top of that, every time we change something we'd have to write our migration code manually. And our migration code would have to take special care not to break relationships between objects. Nice in theory, but would never work well in practice.</p>
<p>In a functional program all state is stored on the stack in the arguments passed to functions. This makes hot deployment significantly easier! In fact, all we'd really have to do is run a diff between the code in production and the new version, and deploy the new code. The rest could be done by language tools automatically! If you think this is science fiction, think again. Erlang engineers have been upgrading live systems without stopping them for years.</p>
<h4>Machine Assisted Proofs and Optimizations</h4>
<p>An interesting property of functional languages is that they can be reasoned about mathematically. Since a functional language is simply an implementation of a formal system, all mathematical operations that could be done on paper still apply to the programs written in that language. The compiler could, for example, convert pieces of code into equivalent but more efficient pieces with a mathematical proof that two pieces of code are equivalent<sup>7</sup>. Relational databases have been performing these optimizations for years. There is no reason the same techniques can't apply to regular software.</p>
<p>Additionally, you can use these techniques to prove that parts of your program are correct. It is even possible to create tools that analyze code and generate edge cases for unit tests automatically! This functionality is invaluable for rock solid systems. If you are designing pace makers and air traffic control systems such tools are almost always a requirement. If you are writing an application outside of truly mission critical industries, these tools can give you a tremendous edge over your competitors.</p>
<h3>Higher Order Functions</h3>
<p>I remember learning about the benefits I outlined above and thinking "that's all very nice but it's useless if I have to program in a crippled language where everything is final." This was a misconception. Making all variables final is crippled in a context of an imperative language like Java but it isn't in a context of functional languages. Functional languages offer a different kind of abstraction tools that make you forget you've ever liked modifying variables. One such tool is capability to work with higher order functions.</p>
<p>A function in such languages is different from a function in Java or C. It is a superset - it can do all the things a Java function can do, and more. We create a function in the same manner we do in C:</p>
<pre><code class="c">int add(int i, int j) {
return i + j;
}
</code></pre>
<p>This means something different from equivalent C code. Let's extend our Java compiler to support this notation. When we type something like this our compiler will convert it to the following Java code (don't forget, everything is final):</p>
<pre><code class="java">class add_function_t {
int add(int i, int j) {
return i + j;
}
}
add_function_t add = new add_function_t();
</code></pre>
<p>The symbol add isn't really a function. It is a small class with one function as its member. We can now pass add around in our code as an argument to other functions. We can assign it to another symbol. We can create instances of add_function_t at runtime and they will be garbage collected when we no longer need them. This makes functions first class objects no different from integers or strings. Functions that operate on other functions (accept them as arguments) are called higher order functions. Don't let this term intimidate you, it's no different from Java classes that operate on each other (we can pass class instances to other classes). We can call them "higher order classes" but nobody cares to because there is no strong academic community behind Java.</p>
<p>How, and when, do you use higher order functions? Well, I'm glad you asked. You write your program as a big monolithic blob of code without worrying about class hierarchies. When you see that a particular piece of code is repeated, you break it out into a function (fortunately they still teach this in schools). If you see that a piece of logic within your function needs to behave differently in different situations, you break it out into a higher order function. Confused? Here's a real life example from my work.</p>
<p>Suppose we have a piece of Java code that receives a message, transforms it in various ways, and forwards it to another server.</p>
<pre><code class="java">class MessageHandler {
void handleMessage(Message msg) {
// ...
msg.setClientCode("ABCD_123");
// ...
sendMessage(msg);
}
// ...
}
</code></pre>
<p>Now imagine that our system has changed and we now route messages to two servers instead of one. Everything is handled in exactly the same way except the client code - the second server wants it in a different format. How do we handle this situation? We could check where the message is headed and format the client code differently, like this:</p>
<pre><code class="java">class MessageHandler {
void handleMessage(Message msg) {
// ...
if(msg.getDestination().equals("server1") {
msg.setClientCode("ABCD_123");
} else {
msg.setClientCode("123_ABC");
}
// ...
sendMessage(msg);
}
// ...
}
</code></pre>
<p>This approach, however, isn't scalable. If more servers are added our function will grow linearly and we'll have a nightmare updating it. An object oriented approach is to make MessageHandler a base class and specialize the client code operation in derived classes:</p>
<pre><code class="java">abstract class MessageHandler {
void handleMessage(Message msg) {
// ...
msg.setClientCode(getClientCode());
// ...
sendMessage(msg);
}
abstract String getClientCode();
// ...
}
class MessageHandlerOne extends MessageHandler {
String getClientCode() {
return "ABCD_123";
}
}
class MessageHandlerTwo extends MessageHandler {
String getClientCode() {
return "123_ABCD";
}
}
</code></pre>
<p>We can now instantiate an appropriate class for each server. Adding servers becomes much more maintainable. That's a lot of code for such a simple modification though. We have to create two new types just to support different client codes! Now let's do the same thing in our language that supports higher order functions:</p>
<pre><code class="java">class MessageHandler {
void handleMessage(Message msg, Function getClientCode) {
// ...
Message msg1 = msg.setClientCode(getClientCode());
// ...
sendMessage(msg1);
}
// ...
}
String getClientCodeOne() {
return "ABCD_123";
}
String getClientCodeTwo() {
return "123_ABCD";
}
MessageHandler handler = new MessageHandler();
handler.handleMessage(someMsg, getClientCodeOne);
</code></pre>
<p>We've created no new types and no class hierarchy. We simply pass appropriate functions as a parameter. We've achieved the same thing as the object oriented counterpart with a number of advantages. We don't restrict ourselves to class hierarchies: we can pass new functions at runtime and change them at any time with a much higher degree of granularity with less code. Effectively the compiler has written object oriented "glue" code for us! In addition we get all the other benefits of FP. Of course the abstractions provided by functional languages don't stop here. Higher order functions are just the beginning.</p>
<h3>Currying</h3>
<p>Most people I've met have read the Design Patterns book by the Gang of Four. Any self respecting programmer will tell you that the book is language agnostic and the patterns apply to software engineering in general, regardless of which language you use. This is a noble claim. Unfortunately it is far removed from the truth.</p>
<p>Functional languages are extremely expressive. In a functional language one does not need design patterns because the language is likely so high level, you end up programming in concepts that eliminate design patterns all together. Once such pattern is an Adapter pattern (how is it different from Facade again? Sounds like somebody needed to fill more pages to satisfy their contract). It is eliminated once a language supports a technique called currying.</p>
<p>Adapter pattern is best known when applied to the "default" abstraction unit in Java - a class. In functional languages the pattern is applied to functions. The pattern takes an interface and transforms it to another interface someone else expects. Here's an example of an adapter pattern:</p>
<pre><code class="java">int pow(int i, int j);
int square(int i)
{
return pow(i, 2);
}
</code></pre>
<p>The code above adapts an interface of a function that raises an integer to an integer power to an interface of a function that squares an integer. In academic circles this trivial technique is called currying (after a logician Haskell Curry who performed mathematical acrobatics necessary to formalize it). Because in FP functions (as opposed to classes) are passed around as arguments, currying is used very often to adapt functions to an interface that someone else expects. Since the interface to functions is its arguments, currying is used to reduce the number of arguments (like in the example above).</p>
<p>Functional languages come with this technique built in. You don't need to manually create a function that wraps the original, functional languages will do that for you. As usual, let's extend our language to support this technique.</p>
<p>square = int pow(int i, 2);
This will automatically create a function square for us with one argument. It will call pow function with the second argument set to 2. This will get compiled to the following Java code:</p>
<pre><code class="java">class square_function_t {
int square(int i) {
return pow(i, 2);
}
}
square_function_t square = new square_function_t();
</code></pre>
<p>As you can see, we've simply created a wrapper for the original function. In FP currying is just that - a shortcut to quickly and easily create wrappers. You concentrate on your task, and the compiler writes the appropriate code for you! When do you use currying? This should be easy. Any time you'd like to use an adapter pattern (a wrapper).</p>
<h4>Lazy Evaluation</h4>
<p>Lazy (or delayed) evaluation is an interesting technique that becomes possible once we adopt a functional philosophy. We've already seen the following piece of code when we were talking about concurrency:</p>
<pre><code class="java">String s1 = somewhatLongOperation1();
String s2 = somewhatLongOperation2();
String s3 = concatenate(s1, s2);
</code></pre>
<p>In an imperative language the order of evaluation would be clear. Because each function may affect or depend on an external state it would be necessary to execute them in order: first somewhatLongOperation1, then somewhatLongOperation2, followed by concatenate. Not so in functional languages.</p>
<p>As we saw earlier somewhatLongOperation1 and somewhatLongOperation2 can be executed concurrently because we're guaranteed no function affects or depends on global state. But what if we don't want to run the two concurrently, do we need to run them in order? The answer is no. We only need to run these operations when another function depends on s1 and s2. We don't even have to run them before concatenate is called - we can delay their evaluation until they're required within concatenate. If we replace concatenate with a function that has a conditional and uses only one of its two parameters we may never evaluate one of the parameters at all! Haskell is an example of a delayed evaluation language. In Haskell you are not guaranteed that anything will be executed in order (or at all) because Haskell only executes code when it's required.</p>
<p>Lazy evaluation has numerous advantages as well as disadvantages. We will discuss the advantages here and will explain how to counter the disadvantages in the next section.</p>
<h4>Optimization</h4>
<p>Lazy evaluation provides a tremendous potential for optimizations. A lazy compiler thinks of functional code exactly as mathematicians think of an algebra expression - it can cancel things out and completely prevent execution, rearrange pieces of code for higher efficiency, even arrange code in a way that reduces errors, all guaranteeing optimizations won't break the code. This is the biggest benefit of representing programs strictly using formal primitives - code adheres to mathematical laws and can be reasoned about mathematically.</p>
<h4>Abstracting Control Structures</h4>
<p>Lazy evaluation provides a higher order of abstraction that allows implementing things in a way that would otherwise be impossible. For example consider implementing the following control structure:</p>
<pre><code class="java">unless(stock.isEuropean()) {
sendToSEC(stock);
}
</code></pre>
<p>We want sendToSEC executed unless the stock is European. How can we implement unless? Without lazy evaluation we'd need some form of a macro system, but in a language like Haskell that's unnecessary. We can implement unless as a function!</p>
<pre><code class="java">void unless(boolean condition, List code) {
if(!condition)
code;
}
</code></pre>
<p>Note that code is never evaluated if the condition is true. We cannot reproduce this behavior in a strict language because the arguments would be evaluated before unless is entered.</p>
<h4>Infinite Data Structures</h4>
<p>Lazy languages allow for definition of infinite data structures, something that's much more complicated in a strict language. For example, consider a list with Fibonacci numbers. We clearly can't compute and infinite list in a reasonable amount of time or store it in memory. In strict languages like Java we simply define a Fibonacci function that returns a particular member from the sequence. In a language like Haskell we can abstract it further and simply define an infinite list of Fibonacci numbers. Because the language is lazy, only the necessary parts of the list that are actually used by the program are ever evaluated. This allows for abstracting a lot of problems and looking at them from a higher level (for example, we can use list processing functions on an infinite list).</p>
<h4>Disadvantages</h4>
<p>Of course there ain't no such thing as a free lunch(tm). Lazy evaluation comes with a number of disadvantages. Mainly that it is, well, lazy. Many real world problems require strict evaluation. For example consider the following:</p>
<pre><code class="java">System.out.println("Please enter your name: ");
System.in.readLine();
</code></pre>
<p>In a lazy language you have no guarantee that the first line will be executed before the second! This means we can't do IO, can't use native functions in any meaningful way (because they need to be called in order since they depend on side effects), and can't interact with the outside world! If we were to introduce primitives that allow ordered code execution we'd lose the benefits of reasoning about our code mathematically (which would take all of the benefits of functional programming with it). Fortunately not all is lost. Mathematicians got to work and developed a number of tricks to ensure code gets executed in particular order in a functional setting. We get the best of both worlds! These techniques include continuations, monads, and uniqueness typing. In this article we'll only deal with continuations. We'll leave monads and uniqueness typing for another time. Interestingly, continuations are useful for many things other than enforcing a particular order of evaluation. We'll talk about that as well.</p>
<h3>Continuations</h3>
<p>Continuations to programming are what Da Vinci Code is to human history: an amazing revelation of the greatest cover-up known to man. Well, may be not, but they're certainly revealing of deceit in the same sense as square roots of negative numbers.</p>
<p>When we learned about functions we only learned half truths based on a faulty assumption that functions must return their value to the original caller. In this sense continuations are a generalization of functions. A function must not necessarily return to its caller and may return to any part of the program. A "continuation" is a parameter we may choose to pass to our function that specifies where the function should return. The description may be more complicated than it sounds. Take a look at the following code:</p>
<pre><code class="java">int i = add(5, 10);
int j = square(i);
</code></pre>
<p>The function add returns 15 to be assigned to i, the place where add was originally called. After that the value of i is used to call square. Note that a lazy compiler can't rearrange these lines of code because the second line depends on successful evaluation of the first. We can rewrite this code block using Continuation Passing Style or CPS, where the function add doesn't return to the original caller but instead returns its result to square.</p>
<pre><code class="java">int j = add(5, 10, square);
</code></pre>
<p>In this case add gets another parameter - a function that add must call with its result upon completion. In this case square is a continuation of add. In both cases j will equal 225.</p>
<p>Here lays the first trick to force a lazy language to evaluate two expressions in order. Consider the following (familiar) IO code:</p>
<pre><code class="java">System.out.println("Please enter your name: ");
System.in.readLine();
</code></pre>
<p>The two lines don't depend on each other and the compiler is free to rearrange them as it wishes. However, if we rewrite this code in CPS, there will be a dependency and the compiler will be forced to evaluate the two lines in order!</p>
<p> System.out.println("Please enter your name: ", System.in.readLine);
In this case println needs to call readLine with its result and return the result of readLine. This allows us to ensure that the two lines are executed in order and that readLine is evaluated at all (because the whole computation expects the last value as a result). In case of Java println returns void but if it were to return an abstract value (that readLine would accept), we'd solve our problem! Of course chaining function calls like that will quickly become unreadable, but it isn't necessary. We could add syntactic sugar to the language that will allow us to simply type expressions in order, and the compiler would chain the calls for us automatically. We can now evaluate expressions in any order we wish without losing any of the benefits of FP (including the ability to reason about our programs mathematically)! If this is still confusing, remember that a function is just an instance of a class with one member. Rewrite above two lines so that println and readLine are instances of classes and everything will become clear.</p>
<p>I would now wrap up this section, except that we've only scratched the surface of continuations and their usefulness. We can write entire programs in CPS, where every function takes an extra continuation argument and passes the result to it. We can also convert any program to CPS simply by treating functions as special cases of continuations (functions that always return to their caller). This conversion is trivial to do automatically (in fact, many compilers do just that).</p>
<p>Once we convert a program to CPS it becomes clear that every instruction has some continuation, a function it will call with the result, which in a regular program would be a place it must return to. Let's pick any instruction from above code, say add(5, 10). In a program written in CPS style it's clear what add's continuation is - it's a function that add calls once it's done. But what is it in a non-CPS program? We could, of course, convert the program to CPS, but do we have to?</p>
<p>It turns out that we don't. Look carefully at our CPS conversion. If you try to write a compiler for it and think about it long enough you'll realize that the CPS version needs no stack! No function ever "returns" in the traditional sense, it just calls another function with the result instead. We don't need to push function arguments on the stack with every call and then pop them back, we can simply store them in some block of memory and use a jump instruction instead. We'll never need the original arguments - they'll never be used again since no function ever returns!</p>
<p>So, programs written in CPS style have no stack but have an extra argument with a function to call. Programs not written in CPS style have no argument with a function to call, but have the stack instead. What does the stack contain? Simply the arguments, and a pointer to memory where the function should return. Do you see a light bulb? The stack simply contains continuation information! The pointer to the return instruction in the stack is essentially the same thing as the function to call in CPS programs! If you wanted to find out what continuation for add(5, 10) is, you'd simply have to examine the stack at the point of its execution!</p>
<p>So that was easy. A continuation and a pointer to the return instruction in the stack are really the same thing, only a continuation is passed explicitly, so that it doesn't need to be the same place where the function was called from. If you remember that a continuation is a function, and a function in our language is compiled to an instance of a class, you'll realize that a pointer to the return instruction in the stack and the continuation argument are really the same thing, since our function (just like an instance of a class) is simply a pointer. This means that at any given point in time in your program you can ask for a current continuation (which is simply the information on the stack).</p>
<p>Ok, so we know what a current continuation is. What does it mean? When we get a current continuation and store it somewhere, we end up storing the current state of our program - freezing it in time. This is similar to an OS putting itself into hibernation. A continuation object contains the information necessary to restart the program from the point where the continuation object was acquired. An operating system does this to your program all the time when it context switches between the threads. The only difference is that it keeps all the control. If you ask for a continuation object (in Scheme this is done by calling call-with-current-continuation function) you'll get an object that contains the current continuation - the stack (or in a CPS case the function to call next). You can store this object in a variable (or alternatively, on disk). When you choose to "restart" your program with this continuation object you will "transform" to the state of the program when you grabbed the continuation object. It's the same thing as switching back to a suspended thread or waking up an OS from hibernation, except you can do it again and again. When an OS wakes up, the hibernation information is destroyed. If it wasn't, you'd be able to wake up from the same point over and over again, almost like going back in time. You have that control with continuations!</p>
<p>In what situations are continuations useful? Usually when you're trying to simulate state in an application of inherently stateless nature to ease your life. A great application of continuations are web applications. Microsoft's ASP.NET goes to tremendous lengths to try and simulate state so that you can write your application with less hassle. If C# supported continuations half of ASP.NET's complexity would disappear - you'd simply store a continuation and restart it when a user makes the web request again. To a programmer of the web application there would be no interruption - the program would simply start from the next line! Continuations are an incredibly useful abstraction for some problems. Considering that many of the traditional fat clients are moving to the web, continuations will become more and more important in the future.</p>
<h3>Pattern Matching</h3>
<p>Pattern matching is not a new or innovative feature. In fact, it has little to do with functional programming. The only reason why it's usually attributed to FP is that functional languages have had pattern matching for some time, while modern imperative languages still don't.</p>
<p>Let's dive into pattern matching with an example. Here's a Fibonacci function in Java:</p>
<pre><code class="java">int fib(int n) {
if(n == 0) return 1;
if(n == 1) return 1;
return fib(n - 2) + fib(n - 1);
}
</code></pre>
<p>And here's an example of a Fibonacci function in our Java-derived language that supports pattern matching:</p>
<pre><code class="java">int fib(0) {
return 1;
}
int fib(1) {
return 1;
}
int fib(int n) {
return fib(n - 2) + fib(n - 1);
}
</code></pre>
<p>What's the difference? The compiler implements branching for us.</p>
<p>What's the big deal? There isn't any. Someone noticed that a large number of functions contain very complicated switch statements (this is particularly true about functional programs) and decided that it's a good idea to abstract that away. We split the function definition into multiple ones, and put patterns in place of some arguments (sort of like overloading). When the function is called, the compiler compares the arguments with the definitions at runtime, and picks the correct one. This is usually done by picking the most specific definition available. For example, int fib(int n) can be called with n equal to 1, but it isn't because int fib(1) is more specific.</p>
<p>Pattern matching is usually more complex than our example reveals. For example, an advanced pattern matching system will allow us to do the following:</p>
<pre><code class="java">int f(int n < 10) { ... }
int f(int n) { ... }
</code></pre>
<p>When is pattern matching useful? In a surprisingly large number of cases! Every time you have a complex structure of nested ifs, pattern matching can do a better job with less code on your part. A good function that comes to mind is a standard WndProc function that all Win32 applications must provide (even though it's often abstracted away). Usually a pattern matching system can examine collections as well as simple values. For example, if you pass an array to your function you could pick out all arrays in which the first element is equal to 1 and the third element is greater than 3.</p>
<p>Another benefit of pattern matching is that if you need to add or modify conditions, you don't have to go into one huge function. You simply add (or modify) appropriate definitions. This eliminates the need for a whole range of design patterns from the GoF book. The more complex your conditions are, the more pattern matching will help you. Once you're used to it, you start wondering how you ever got through your day without it.</p>
<h3>Closures</h3>
<p>So far we've discussed features in the context of "pure" functional languages - languages that are implementations of lambda calculus and don't include features that conflict with Church's formalism. However, many of the features of functional languages are useful outside of lambda calculus framework. While an implementation of an axiomatic system is useful because it allows thinking about programs in terms of mathematical expressions, it may or may not always be practical. Many languages choose to incorporate functional elements without strictly adhering to functional doctrine. Many such languages (like Common Lisp) don't require variables to be final - you can modify things in place. They also don't require functions to depend only on their arguments - functions are allowed to access state outside of their boundaries. But they do include functional features - like higher order functions. Passing functions around in impure languages is a little bit different than doing it in the confines of lambda calculus and requires support for an interesting feature often referred to as lexical closure. Let's take a look at some sample code. Remember, in this case variables aren't final and functions can refer to variables outside of their scope:</p>
<pre><code class="java">Function makePowerFn(int power) {
int powerFn(int base) {
return pow(base, power);
}
return powerFn;
}
Function square = makePowerFn(2);
square(3); // returns 9
</code></pre>
<p>The function make-power-fn returns a function that takes a single argument and raises it to a certain power. What happens when we try to evaluate square(3)? The variable power isn't anywhere in scope of powerFn because makePowerFn has returned and its stack is long gone. How can square work, then? The language must, somehow, store the value of power somewhere for square to work. What if we create another function, cube, that raises something to the third power? The runtime must now store two copies of power, one for each function we generated using make-power-fn. The phenomenon of storing these values is called a closure. Closures don't only store arguments of a host function. For example, a closure can look like this:</p>
<pre><code class="java">Function makeIncrementer() {
int n = 0;
int increment() {
return ++n;
}
}
Function inc1 = makeIncrementer();
Function inc2 = makeIncrementer();
inc1(); // returns 1;
inc1(); // returns 2;
inc1(); // returns 3;
inc2(); // returns 1;
inc2(); // returns 2;
inc2(); // returns 3;
</code></pre>
<p>The runtime manages to store n, so incrementers can access it. Furthermore, it stores various copies, one for each incrementer, even though they're supposed to disappear when makeIncrementer returns. What does this code compile to? How do closures work behind the scenes? Fortunately, we have a back stage pass.</p>
<p>A little common sense goes a long way. The first observation is that local variables are no longer limited to simple scope rules and have an undefined lifetime. The obvious conclusion is that they're no longer stored on the stack - they must be stored on the heap instead<sup>8</sup>. A closure, then, is implemented just like a function we discussed earlier, except that it has an additional reference to the surrounding variables:</p>
<pre><code class="java">class some_function_t {
SymbolTable parentScope;
// ...
}
</code></pre>
<p>When a closure references a variable that's not in its local scope, it consults this reference for a parent scope. That's it! Closures bring functional and OO worlds closer together. Every time you create a class that holds some state and pass it to somewhere else, think of closures. A closure is just an object that creates "member variables" on the fly by grabbing them from the scope, so you don't have to!</p>
<h3>What's next?</h3>
<p>This article only scratches the surface of functional programming. Sometimes a small scratch can progress to something bigger and in our case it's a good thing. In the future I plan to write about category theory, monads, functional data structures, type systems in functional languages, functional concurrency, functional databases and much more. If I get to write (and in the process learn) about half of these topics my life will be complete. In the meantime, Google is our friend.</p>
<h3>Comments?</h3>
<p>If you have any questions, comments, or suggestions, please drop a note at coffeemug@gmail.com. I'll be glad to hear your feedback.</p>
<p><sup>1</sup>When I was looking for a job in the fall of 2005 I often did ask this question. It's quite amusing how many blank stares I got. You would think that at about $300,000 a piece these people would at least have a good understanding of most tools available to them. <br/>
<sup>2</sup>This appears to be a controversial question. Physicists and mathematicians are forced to acknowledge that it isn't at all clear whether everything in the universe obeys the laws that can be described by mathematics. <br/>
<sup>3</sup>I've always hated history lessons that offer a dry chronology of dates, names, and events. To me history is about the lives of people who changed the world. It is about their private reasons behind their actions, and the mechanisms by which they affected millions of souls. For this reason this history section is hopelessly incomplete. Only very relevant people and events are discussed. <br/>
<sup>4</sup>When I was learning about functional programming I was very annoyed by the term "lambda" because I couldn't really understand what it really means. In this context lambda is a function, the single Greek letter was just easier to write in a mathematical notation. Every time you hear "lambda" when talking about functional programming just translate it in your mind to "function". <br/>
<sup>5</sup>Interestingly Java strings are immutable anyway. It's rather interesting to explore the reasons for this treachery, but that would distract us from our goal. <br/>
<sup>6</sup>Most functional language compilers optimize recursive functions by transforming them to their iterative alternatives whenever possible. This is called a tail call optimization. <br/>
<sup>7</sup>The opposite isn't always true. While it is sometimes possible to prove that two pieces of code are equivalent, it isn't possible in all situations. <br/>
<sup>8</sup>This is actually no slower than storing on the stack because once you introduce a garbage collector, memory allocation becomes an O(1) operation.</p>
傻瓜函数式编程2015-04-11T00:00:00+00:00http://jiaxianhua.github.io/functional%20programming/2015/04/11/functional-programming-for-the-rest-of-us-cn<p><a href="https://github.com/justinyhuang/Functional-Programming-For-The-Rest-of-Us-Cn/">https://github.com/justinyhuang/Functional-Programming-For-The-Rest-of-Us-Cn/</a></p>
<h1>傻瓜函数式编程</h1>
<p>2006年6月19日,星期一</p>
<h3>开篇</h3>
<p>我们这些码农做事都是很拖拉的。每天例行报到后,先来点咖啡,看看邮件还有RSS订阅的文章。然后翻翻新闻还有那些技术网站上的更新,再过一遍编程论坛口水区里那些无聊的论战。最后从头把这些再看一次以免错过什么精彩的内容。然后就可以吃午饭了。饭饱过后,回来盯着IDE发一会呆,再看看邮箱,再去搞杯咖啡。光阴似箭,可以回家了…… <br/>
(在被众人鄙视之前)我唯一想说的是,在这些拖拉的日子里总会时不时读到一些<a href="http://www.baike.com/wiki/%E4%B8%8D%E6%98%8E%E8%A7%89%E5%8E%89">不明觉厉</a>的文章。如果没有打开不应该打开的网站,每隔几天你都可以看到至少一篇这样的东西。它们的共性:难懂,耗时,于是这些文章就慢慢的堆积成山了。很快你就会发现自己已经累积了一堆的收藏链接还有数不清的PDF文件,此时你只希望隐入一个杳无人烟的深山老林里什么也不做,用一年半载好好的消化这些私藏宝贝。当然,我是说最好每天还是能有人来给送吃的顺带帮忙打扫卫生倒垃圾,哇哈哈。</p>
<p>我不知道你都收藏了些什么,我的阅读清单里面相当大部分都是函数式编程相关的东东:基本上是最难啃的。这些文章充斥着无比枯燥的教科书语言,我想就连那些在华尔街浸淫10年以上的大牛都无法搞懂这些函数式编程(简称FP)文章到底在说什么。你可以去花旗集团或者德意志银行找个项目经理来问问<sup>1</sup>:你们为什么要选JMS而不用Erlang?答案基本上是:我认为这个学术用的语言还无法胜任实际应用。可是,现有的一些系统不仅非常复杂还需要满足十分严苛的需求,它们就都是用函数式编程的方法来实现的。这,就说不过去了。 <br/>
关于FP的文章确实比较难懂,但我不认为一定要搞得那么晦涩。有一些历史原因造成了这种知识断层,可是FP概念本身并不难理解。我希望这篇文章可以成为一个“FP入门指南”,帮助你从<a href="http://zh.wikipedia.org/zh/%E6%8C%87%E4%BB%A4%E5%BC%8F%E7%B7%A8%E7%A8%8B">指令式编程</a>走向<a href="http://zh.wikipedia.org/zh/%E5%87%BD%E6%95%B8%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80">函数式编程</a>。先来点咖啡,然后继续读下去。很快你对FP的理解就会让同事们刮目相看了。</p>
<p>什么是函数式编程(Functional Programming,FP)?它从何而来?可以吃吗?倘若它真的像那些鼓吹FP的人说的那么好,为什么实际应用中那么少见?为什么只有那些在读博士的家伙想要用它?而最重要的是,它母亲的怎么就那么难学?那些所谓的closure、continuation,currying,lazy evaluation还有no side effects都是什么东东(译者:本着保留专用术语的原则,此处及下文类似情形均不译)?如果没有那些大学教授的帮忙怎样把它应用到实际工程里去?为什么它和我们熟悉的万能而神圣的指令式编程那么的不一样? <br/>
我们很快就会解开这些谜团。刚才我说过实际工程和学术界之间的知识断层是有其历史原因的,那么就先让我来解释一下这个问题。答案,就在接下来的一次公园漫步中:</p>
<h3>公园漫步</h3>
<p>时间机器启动……我们来到公元前380年,也就是2000多年前的雅典城外。这是一个阳光明媚的久违的春天,<a href="http://zh.wikipedia.org/zh/%E6%9F%8F%E6%8B%89%E5%9B%BE">柏拉图</a>和一个帅气的小男仆走在一片橄榄树荫下。他们正准备前往一个学院。天气很好,吃得很饱,渐渐的,两人的谈话转向了哲学。</p>
<p>“你看那两个学生,哪一个更高一些?”,柏拉图小心的选择用字,以便让这个问题更好的引导眼前的这个小男孩。 <br/>
小男仆望向水池旁边的两个男生,“他们差不多一样高。”。 <br/>
“‘差不多一样高’是什么意思?”柏拉图问。 <br/>
“嗯……从这里看来他们是一样高的,但是如果走近一点我肯定能看出差别来。” <br/>
柏拉图笑了。他知道这个小孩已经朝他引导的方向走了。“这么说来你的意思是世界上没有什么东西是完全相同的咯?” <br/>
思考了一会,小男孩回答:“是的。万物之间都至少有一丁点差别,哪怕我们无法分辨出来。” <br/>
说到点子上了!“那你说,如果世界上没有什么东西是完全相等的,你怎么理解‘完全相等’这个概念?” <br/>
小男仆看起来很困惑。“这我就不知道了。”</p>
<p>这是人类第一次试图了解数学的本质。柏拉图认为我们所在的世界中,万事万物都是完美模型的一个近似。他同时意识到虽然我们不能感受到完美的模型,但这丝毫不会阻止我们了解完美模型的概念。柏拉图进而得出结论:完美的数学模型只存在于另外一个世界,而因为某种原因我们却可以通过联系着这两个世界的一个纽带来认识这些模型。一个简单的例子就是完美的圆形。没有人见过这样的一个圆,但是我们知道怎样的圆是完美的圆,而且可以用公式把它描述出来。</p>
<p>如此说来,什么是数学呢?为什么可以用数学法则来描述我们的这个宇宙?我们所处的这个世界中万事万物都可以用数学来描述吗?<sup>2</sup>
数理哲学是一门很复杂的学科。它和其他多数哲学一样,更着重于提出问题而不是给出答案。数学就像拼图一样,很多结论都是这样推导出来的:先是确立一些互不冲突的基础原理,以及一些操作这些原理的规则,然后就可以把这些原理以及规则拼凑起来形成新的更加复杂的规则或是定理了。数学家把这种方法称为“形式系统”或是“演算”。如果你想做的话,可以用形式系统描述俄罗斯方块这个游戏。而事实上,俄罗斯方块这个游戏的实现,只要它正确运行,就是一个形式系统。只不过它以一种不常见的形式表现出来罢了。</p>
<p>如果<a href="http://zh.wikipedia.org/wiki/%E5%8D%8A%E4%BA%BA%E9%A9%AC%E5%BA%A7%CE%B1">半人马阿尔法</a>上有文明存在的话,那里的生物可能无法解读我们的俄罗斯方块形式系统甚至是简单的圆形的形式系统,因为它们感知世界的唯一器官可能只有鼻子(译者:偶的妈你咋知道?)也许它们是无法得知俄罗斯方块的形式系统了,但是它们很有可能知道圆形。它们的圆形我们可能没法解读,因为我们的鼻子没有它们那么灵敏(译者:那狗可以么?)可是只要越过形式系统的表示方式(比如通过使用“超级鼻子”之类的工具来感知这些用味道表示的形式系统,然后使用标准的解码技术把它们翻译成人类能理解的语言),那么任何有足够智力的文明都可以理解这些形式系统的本质。 <br/>
有意思的是,哪怕宇宙中完全不存在任何文明,类似俄罗斯方块还有圆形这样的形式系统依旧是成立的:只不过没有智慧生物去发现它们而已。这个时候如果忽然一个文明诞生了,那么这些具有智慧的生物就很有可能发现各种各样的形式系统,并且用它们发现的系统去描述各种宇宙法则。不过它们可能不会发现俄罗斯方块这样的形式系统,因为在它们的世界里没有俄罗斯方块这种东西嘛。有很多像俄罗斯方块这样的形式系统是与客观世界无关的,比如说自然数,很难说所有的自然数都与客观世界有关,随便举一个超级大的数,这个数可能就和世界上任何事物无关,因为这个世界可能不是无穷大的。</p>
<h3>历史回眸<sup>3</sup></h3>
<p>再次启动时间机……这次到达的是20世纪30年代,离今天近了很多。无论<a href="http://zh.wikipedia.org/wiki/%E6%96%B0%E5%A4%A7%E9%99%B8">新</a><a href="http://zh.wikipedia.org/wiki/%E8%88%8A%E5%A4%A7%E9%99%B8">旧</a>大陆,经济大萧条都造成了巨大的破坏。社会各阶层几乎每一个家庭都深受其害。只有极其少数的几个地方能让人们免于遭受穷困之苦。几乎没有人能够幸运的在这些避难所里度过危机,注意,我说的是几乎没有,还真的有这么些幸运儿,比如说当时普林斯顿大学的数学家们。</p>
<p>新建成的哥特式办公楼给普林斯顿大学带来一种天堂般的安全感。来自世界各地的逻辑学者应邀来到普林斯顿,他们将组建一个新的学部。正当大部分美国人还在为找不到一片面包做晚餐而发愁的时候,在普林斯顿却是这样一番景象:高高的天花板和木雕包覆的墙,每天品茶论道,漫步丛林。
一个名叫<a href="http://zh.wikipedia.org/zh/%E9%98%BF%E9%9A%86%E4%BD%90%C2%B7%E9%82%B1%E5%A5%87">阿隆佐·邱奇</a>(Alonzo Church)的年轻数学家就过着这样优越的生活。阿隆佐本科毕业于普林斯顿后被留在研究院。他觉得这样的生活完全没有必要,于是他鲜少出现在那些数学茶会中也不喜欢到树林里散心。阿隆佐更喜欢独处:自己一个人的时候他的工作效率更高。尽管如此他还是和普林斯顿学者保持着联系,这些人当中有<a href="http://zh.wikipedia.org/zh/%E8%89%BE%E4%BC%A6%C2%B7%E5%9B%BE%E7%81%B5">艾伦·图灵</a>、<a href="http://zh.wikipedia.org/zh/%E7%BA%A6%E7%BF%B0%C2%B7%E5%86%AF%C2%B7%E8%AF%BA%E4%BC%8A%E6%9B%BC">约翰·冯·诺伊曼</a>、<a href="http://zh.wikipedia.org/zh-hant/%E5%BA%93%E5%B0%94%E7%89%B9%C2%B7%E5%93%A5%E5%BE%B7%E5%B0%94">库尔特·哥德尔</a>。 <br/>
这四个人都对形式系统感兴趣。相对于现实世界,他们更关心如何解决抽象的数学问题。而他们的问题都有这么一个共同点:都在尝试解答关于计算的问题。诸如:如果有一台拥有无穷计算能力的超级机器,可以用来解决什么问题?它可以自动的解决这些问题吗?是不是还是有些问题解决不了,如果有的话,是为什么?如果这样的机器采用不同的设计,它们的计算能力相同吗? <br/>
在与这些人的合作下,阿隆佐设计了一个名为<a href="http://zh.wikipedia.org/wiki/%CE%9B%E6%BC%94%E7%AE%97">lambda演算</a>的形式系统。这个系统实质上是为其中一个超级机器设计的编程语言。在这种语言里面,函数的参数是函数,返回值也是函数。这种函数用希腊字母lambda(<a href="http://en.wikipedia.org/wiki/Lambda">λ</a>),这种系统因此得名<sup>4</sup>。有了这种形式系统,阿隆佐终于可以分析前面的那些问题并且能够给出答案了。 <br/>
除了阿隆佐·邱奇,艾伦·图灵也在进行类似的研究。他设计了一种完全不同的系统(后来被称为<a href="http://zh.wikipedia.org/zh/%E5%9B%BE%E7%81%B5%E6%9C%BA">图灵机</a>),并用这种系统得出了和阿隆佐相似的答案。到了后来人们证明了图灵机和lambda演算的能力是一样的。</p>
<p>如果二战没有发生,这个故事到这里就应该结束了,我的这篇小文没什么好说的了,你们也可以去看看有什么其他好看的文章。可是二战还是爆发了,整个世界陷于火海之中。那时的美军空前的大量使用炮兵。为了提高轰炸的精度,军方聘请了大批数学家夜以继日的求解各种差分方程用于计算各种火炮发射数据表。后来他们发现单纯手工计算这些方程太耗时了,为了解决这个问题,各种各样的计算设备应运而生。IBM制造的Mark一号就是用来计算这些发射数据表的第一台机器。Mark一号重5吨,由75万个零部件构成,每一秒可以完成3次运算。 <br/>
战后,人们为提高计算能力而做出的努力并没有停止。1949年第一台电子离散变量自动计算机诞生并取得了巨大的成功。它是<a href="http://zh.wikipedia.org/zh/%E5%86%AF%C2%B7%E8%AF%BA%E4%BC%8A%E6%9B%BC%E7%BB%93%E6%9E%84">冯·诺伊曼设计架构</a>的第一个实例,也是一台现实世界中实现的图灵机。相比他的这些同事,那个时候阿隆佐的运气就没那么好了。 <br/>
到了50年代末,一个叫John McCarthy的MIT教授(他也是普林斯顿的硕士)对阿隆佐的成果产生了兴趣。1958年他发明了一种列表处理语言(Lisp),这种语言是一种阿隆佐lambda演算在现实世界的实现,而且它能在冯·诺伊曼计算机上运行!很多计算机科学家都认识到了Lisp强大的能力。1973年在MIT人工智能实验室的一些程序员研发出一种机器,并把它叫做Lisp机。于是阿隆佐的lambda演算也有自己的硬件实现了!</p>
<h3>函数式编程</h3>
<p>函数式编程是阿隆佐思想的在现实世界中的实现。不过不是全部的lambda演算思想都可以运用到实际中,因lambda演算在设计的时候就不是为了在各种现实世界中的限制下工作的。所以,就像面向对象的编程思想一样,函数式编程只是一系列想法,而不是一套严苛的规定。有很多支持函数式编程的程序语言,它们之间的具体设计都不完全一样。在这里我将用Java写的例子介绍那些被广泛应用的函数式编程思想(没错,如果你是受虐狂你可以用Java写出函数式程序)。在下面的章节中我会在Java语言的基础上,做一些修改让它变成实际可用的函数式编程语言。那么现在就开始吧。</p>
<p>Lambda演算在最初设计的时候就是为了研究计算相关的问题。所以函数式编程主要解决的也是计算问题,而出乎意料的是,是用函数来解决的!(译者:请理解原作者的苦心,我想他是希望加入一点调皮的风格以免读者在中途睡着或是转台……)。函数就是函数式编程中的基础元素,可以完成几乎所有的操作,哪怕最简单的计算,也是用函数完成的。我们通常理解的变量在函数式编程中也被函数代替了:在函数式编程中变量仅仅代表某个表达式(这样我们就不用把所有的代码都写在同一行里了)。所以我们这里所说的‘变量’是不能被修改的。所有的变量只能被赋一次初值。在Java中就意味着每一个变量都将被声明为final(如果你用C++,就是const)。在FP中,没有非final的变量。</p>
<pre><code class="java">final int i = 5;
final int j = i + 3;
</code></pre>
<p>既然FP中所有的变量都是final的,可以引出两个规定:一是变量前面就没有必要再加上final这个关键字了,二是变量就不能再叫做‘变量’了……于是现在开始对Java做两个改动:所有Java中声明的变量默认为final,而且我们把所谓的‘变量’称为‘符号’。 <br/>
到现在可能会有人有疑问:这个新创造出来的语言可以用来写什么有用的复杂一些的程序吗?毕竟,如果每个符号的值都是不能修改的,那么我们就什么东西都不能改变了!别紧张,这样的说法不完全正确。阿隆佐在设计lambda演算的时候他并不想要保留状态的值以便稍后修改这些值。他更关心的是基于数据之上的操作(也就是更容易理解的“计算”)。而且,lambda演算和图灵机已经被证明了是具有同样能力的系统,因此指令式编程能做到的函数式编程也同样可以做到。那么,怎样才能做到呢? <br/>
事实上函数式程序是可以保存状态的,只不过它们用的不是变量,而是函数。状态保存在函数的参数中,也就是说在栈上。如果你需要保存一个状态一段时间并且时不时的修改它,那么你可以编写一个递归函数。举个例子,试着写一个函数,用来反转一个Java的字符串。记住咯,这个程序里的变量都是默认为final的<sup>5</sup>。</p>
<pre><code class="java">String reverse(String arg) {
if(arg.length == 0) {
return arg;
}
else {
return reverse(arg.substring(1, arg.length)) + arg.substring(0, 1);
}
}
</code></pre>
<p>这个方程运行起来会相对慢一些,因为它重复调用自己<sup>6</sup>。同时它也会大量的消耗内存,因为它会不断的分配创建内存对象。无论如何,它是用函数式编程思想写出来的。这时候可能有人要问了,为什么要用这种奇怪的方式编写程序呢?嘿,我正准备告诉你。</p>
<h3>FP之优点</h3>
<p>你大概已经在想:上面这种怪胎函数怎么也不合理嘛。在我刚开始学习FP的时候我也这样想的。不过后来我知道我是错的。使用这种方式编程有很多好处。其中一些是主观的。比如说有人认为函数式程序更容易理解。这个我就不说了,哪怕街上随便找个小孩都知道‘容易理解’是多么主观的事情。幸运的是,客观方面的好处还有很多。</p>
<h4>单元测试</h4>
<p>因为FP中的每个符号都是final的,于是没有什么函数会有副作用。谁也不能在运行时修改任何东西,也没有函数可以修改在它的作用域之外修改什么值给其他函数继续使用(在指令式编程中可以用类成员或是全局变量做到)。这意味着决定函数执行结果的唯一因素就是它的返回值,而影响其返回值的唯一因素就是它的参数。 <br/>
这正是单元测试工程师梦寐以求的啊。现在测试程序中的函数时只需要关注它的参数就可以了。完全不需要担心函数调用的顺序,也不用费心设置外部某些状态值。唯一需要做的就是传递一些可以代表边界条件的参数给这些函数。相对于指令式编程,如果FP程序中的每一个函数都能通过单元测试,那么我们对这个软件的质量必将信心百倍。反观Java或者C++,仅仅检查函数的返回值是不够的:代码可能修改外部状态值,因此我们还需要验证这些外部的状态值的正确性。在FP语言中呢,就完全不需要。</p>
<h4>调试查错</h4>
<p>如果一段FP程序没有按照预期设计那样运行,调试的工作几乎不费吹灰之力。这些错误是百分之一百可以重现的,因为FP程序中的错误不依赖于之前运行过的不相关的代码。而在一个指令式程序中,一个bug可能有时能重现而有些时候又不能。因为这些函数的运行依赖于某些外部状态, 而这些外部状态又需要由某些与这个bug完全不相关的代码通过某个特别的执行流程才能修改。在FP中这种情况完全不存在:如果一个函数的返回值出错了,它一直都会出错,无论你之前运行了什么代码。 <br/>
一旦问题可以重现,解决它就变得非常简单,几乎就是一段愉悦的旅程。中断程序的运行,检查一下栈,就可以看到每一个函数调用时使用的每一个参数,这一点和指令式代码一样。不同的是指令式程序中这些数据还不足够,因为函数的运行还可能依赖于成员变量,全局变量,还有其他类的状态(而这些状态又依赖于类似的变量)。FP中的函数只依赖于传给它的参数,而这些参数就在眼前!还有,对指令式程序中函数返回值的检查并不能保证这个函数是正确运行的。还要逐一检查若干作用域以外的对象以确保这个函数没有对这些牵连的对象做出什么越轨的行为(译者:好吧,翻译到这里我自己已经有点激动了)。对于一个FP程序,你要做的仅仅是看一下函数的返回值。 <br/>
把栈上的数据过一遍就可以得知有哪些参数传给了什么函数,这些函数又返回了什么值。当一个返回值看起来不对头的那一刻,跳进这个函数看看里面发生了什么。一直重复跟进下去就可以找到bug的源头!</p>
<h4>并发执行</h4>
<p>不需要任何改动,所有FP程序都是可以并发执行的。由于根本不需要采用锁机制,因此完全不需要担心死锁或是并发竞争的发生。在FP程序中没有哪个线程可以修改任何数据,更不用说多线程之间了。这使得我们可以轻松的添加线程,至于那些祸害并发程序的老问题,想都不用想! <br/>
既然是这样,为什么没有人在那些高度并行的那些应用程序中采用FP编程呢?事实上,这样的例子并不少见。爱立信开发了一种FP语言,名叫Erlang,并应用在他们的电信交换机上,而这些交换机不仅容错度高而且拓展性强。许多人看到了Erlang的这些优势也纷纷开始使用这一语言。在这里提到的电信交换控制系统远远要比华尔街上使用的系统具有更好的扩展性也更可靠。事实上,用Erlang搭建的系统并不具备可扩展性和可靠性,而Java可以提供这些特性。Erlang只是像岩石一样结实不容易出错而已。 <br/>
FP关于并行的优势不仅于此。就算某个FP程序本身只是单线程的,编译器也可以将其优化成可以在多CPU上运行的并发程序。以下面的程序为例:</p>
<pre><code class="java">String s1 = somewhatLongOperation1();
String s2 = somewhatLongOperation2();
String s3 = concatenate(s1, s2);
</code></pre>
<p>如果是函数式程序,编译器就可以对代码进行分析,然后可能分析出生成字符串s1和s2的两个函数可能会比较耗时,进而安排它们并行运行。这在指令式编程中是无法做到的,因为每一个函数都有可能修改其外部状态,然后接下来的函数又可能依赖于这些状态的值。在函数式编程中,自动分析代码并找到适合并行执行的函数十分简单,和分析C的内联函数没什么两样。从这个角度来说用FP风格编写的程序是“永不过时”的(虽然我一般不喜欢说大话空话,不过这次就算个例外吧)。硬件厂商已经没办法让CPU运行得再快了。他们只能靠增加CPU核的数量然后用并行来提高运算的速度。这些厂商故意忽略一个事实:只有可以并行的软件才能让你花大价钱买来的这些硬件物有所值。指令式的软件中只有很小一部分能做到跨核运行,而所有的函数式软件都能实现这一目标,因为FP的程序从一开始就是可以并行运行的。</p>
<h4>热部署</h4>
<p>在Windows早期,如果要更新系统那可是要重启电脑的,而且还要重启很多次。哪怕只是安装一个新版本的播放器。到了XP的时代这种情况得到比较大的改善,尽管还是不理想(我工作的时候用的就是Windows,就在现在,我的系统托盘上就有个讨厌的图标,我不重启机子就不消失)。这一方面Unix好一些,曾经。只需要暂停一些相关的部件而不是整个操作系统,就可以安装更新了。虽然是要好一些了,对很多服务器应用来说这也还是不能接受的。电信系统要求的是100%的在线率,如果一个救急电话因为系统升级而无法拨通,成千上万的人就会因此丧命。同样的,华尔街的那些公司怎么也不能说要安装软件而在整个周末停止他们系统的服务。 <br/>
最理想的情况是更新相关的代码而不用暂停系统的其他部件。对指令性程序来说是不可能的。想想看,试着在系统运行时卸载掉一个Java的类然后再载入这个类的新的实现,这样做的话系统中所有该类的实例都会立刻不能运行,因为该类的相关状态已经丢失了。这种情况下可能需绞尽脑汁设计复杂的版本控制代码,需要将所有这种类正在运行的实例<a href="http://zh.wikipedia.org/wiki/%E5%BA%8F%E5%88%97%E5%8C%96">序列化</a>,逐一销毁它们,然后创建新类的实例,将现有数据也序列化后装载到这些新的实例中,最后希望负责装载的程序可以正确的把这些数据移植到新实例中并正常的工作。这种事很麻烦,每次有新的改动都需要手工编写装载程序来完成更新,而且这些装载程序还要很小心,以免破坏了现有对象之间的联系。理论上是没问题,可是实际上完全行不通。 <br/>
FP的程序中所有状态就是传给函数的参数,而参数都是储存在栈上的。这一特性让软件的热部署变得十分简单。只要比较一下正在运行的代码以及新的代码获得一个diff,然后用这个diff更新现有的代码,新代码的热部署就完成了。其它的事情有FP的语言工具自动完成!如果还有人认为这只存在于科幻小说中,他需要再想想:多年来Erlang工程师已经使用这种技术对它们的系统进行升级而完全不用暂停运行了。</p>
<h4>机器辅助优化及证明</h4>
<p>FP语言有一个特性很有意思,那就是它们是可以用数学方法来分析的。FP语言本身就是形式系统的实现,只要是能在纸上写出来的数学运算就可以用这种语言表述出来。于是只要能够用数学方法证明两段代码是一致的,编译器就可以把某段代码解析成在数学上等同的但效率又更高的另外一段代码<sup>7</sup>。 关系数据库已经用这种方法进行优化很多年了。没有理由在常规的软件行业就不能应用这种技术。
另外,还可以用这种方法来证明代码的正确性,甚至可以设计出能够自动分析代码并为单元测试自动生成边缘测试用例的工具出来!对于那些对缺陷零容忍的系统来说,这一功能简直就是无价之宝。例如心脏起搏器,例如飞行管控系统,这几乎就是必须满足的需求。哪怕你正在开发的程序不是为了完成什么重要核心任务,这些工具也可以帮助你写出更健壮的程序,直接甩竞争对手n条大街。</p>
<h3>高阶函数</h3>
<p>我还记得在了解到FP以上的各种好处后想到:“这些优势都很吸引人,可是,如果必须非要用这种所有变量都是final的蹩脚语言,估计还是不怎么实用吧”。其实这样的想法是不对的。对于Java这样的指令式语言来说,如果所有的变量都是必须是final的,那么确实很束手束脚。然而对函数式语言来说,情况就不一样了。函数式语言提供了一种特别的抽象工具,这种工具将帮助使用者编写FP代码,让他们甚至都没想到要修改变量的值。高阶函数就是这种工具之一。 <br/>
FP语言中的函数有别于Java或是C。可以说这种函数是一个<a href="http://zh.wikipedia.org/wiki/%E5%85%A8%E9%9B%86">全集</a>:Java函数可以做到的它都能做,同时它还有更多的能力。首先,像在C里写程序那样创建一个函数:</p>
<pre><code class="c">int add(int i, int j) {
return i + j;
}
</code></pre>
<p>看起来和C程序没什么区别,但是很快你就可以看出区别来。接下来我们扩展Java的编译器以便支持这种代码,也就是说,当我们写下以上的程序编译器会把它转化成下面的Java程序(别忘了,所有的变量都是final的):</p>
<pre><code class="java">class add_function_t {
int add(int i, int j) {
return i + j;
}
}
add_function_t add = new add_function_t();
</code></pre>
<p>在这里,符号add并不是一个函数,它是只有一个函数作为其成员的简单的类。这样做有很多好处,可以在程序中把add当成参数传给其他的函数,也可以把add赋给另外一个符号,还可以在运行时创建add_function_t的实例然后在不再需要这些实例的时候由系统回收机制处理掉。这样做使得函数成为和integer或是string这样的<a href="http://zh.wikipedia.org/zh/%E7%AC%AC%E4%B8%80%E9%A1%9E%E7%89%A9%E4%BB%B6">第一类对象</a>。对其他函数进行操作(比如说把这些函数当成参数)的函数,就是所谓的高阶函数。别让这个看似高深的名字吓倒你(译者:好死不死起个这个名字,初一看还准备搬出已经尘封的高数教材……),它和Java中操作其他类(也就是把一个类实例传给另外的类)的类没有什么区别。可以称这样的类为“高阶类”,但是没人会在意,因为Java圈里就没有什么很强的学术社团。(译者:这是高级黑吗?) <br/>
那么什么时候该用高阶函数,又怎样用呢?我很高兴有人问这个问题。设想一下,你写了一大堆程序而不考虑什么类结构设计,然后发现有一部分代码重复了几次,于是你就会把这部分代码独立出来作为一个函数以便多次调用(所幸学校里至少会教这个)。如果你发现这个函数里有一部分逻辑需要在不同的情况下实现不同的行为,那么你可以把这部分逻辑独立出来作为一个高阶函数。搞晕了?下面来看看我工作中的一个真实的例子。</p>
<p>假设有一段Java的客户端程序用来接收消息,用各种方式对消息做转换,然后发给一个服务器。</p>
<pre><code class="java">class MessageHandler {
void handleMessage(Message msg) {
// ...
msg.setClientCode("ABCD_123");
// ...
sendMessage(msg);
}
// ...
}
</code></pre>
<p>再进一步假设,整个系统改变了,现在需要发给两个服务器而不再是一个了。系统其他部分都不变,唯独客户端的代码需要改变:额外的那个服务器需要用另外一种格式发送消息。应该如何处理这种情况呢?我们可以先检查一下消息要发送到哪里,然后选择相应的格式把这个消息发出去:</p>
<pre><code class="java">class MessageHandler {
void handleMessage(Message msg) {
// ...
if(msg.getDestination().equals("server1") {
msg.setClientCode("ABCD_123");
} else {
msg.setClientCode("123_ABC");
}
// ...
sendMessage(msg);
}
// ...
}
</code></pre>
<p>可是这样的实现是不具备扩展性的。如果将来需要增加更多的服务器,上面函数的大小将呈线性增长,使得维护这个函数最终变成一场噩梦。面向对象的编程方法告诉我们,可以把MessageHandler变成一个基类,然后将针对不同格式的消息编写相应的子类。</p>
<pre><code class="java">abstract class MessageHandler {
void handleMessage(Message msg) {
// ...
msg.setClientCode(getClientCode());
// ...
sendMessage(msg);
}
abstract String getClientCode();
// ...
}
class MessageHandlerOne extends MessageHandler {
String getClientCode() {
return "ABCD_123";
}
}
class MessageHandlerTwo extends MessageHandler {
String getClientCode() {
return "123_ABCD";
}
}
</code></pre>
<p>这样一来就可以为每一个接收消息的服务器生成一个相应的类对象,添加服务器就变得更加容易维护了。可是,这一个简单的改动引出了很多的代码。仅仅是为了支持不同的客户端行为代码,就要定义两种新的类型!现在来试试用我们刚才改造的语言来做同样的事情,注意,这种语言支持高阶函数:</p>
<pre><code class="java">class MessageHandler {
void handleMessage(Message msg, Function getClientCode) {
// ...
Message msg1 = msg.setClientCode(getClientCode());
// ...
sendMessage(msg1);
}
// ...
}
String getClientCodeOne() {
return "ABCD_123";
}
String getClientCodeTwo() {
return "123_ABCD";
}
MessageHandler handler = new MessageHandler();
handler.handleMessage(someMsg, getClientCodeOne);
</code></pre>
<p>在上面的程序里,我们没有创建任何新的类型或是多层类的结构。仅仅是把相应的函数作为参数进行传递,就做到了和用面向对象编程一样的事情,而且还有额外的好处:一是不再受限于多层类的结构。这样做可以做运行时传递新的函数,可以在任何时候改变这些函数,而且这些改变不仅更加精准而且触碰的代码更少。这种情况下编译器其实就是在替我们编写面向对象的“粘合”代码(译者:又称胶水代码,粘接代码)!除此之外我们还可以享用FP编程的其他所有优势。函数式编程能提供的抽象服务还远不止于此。高阶函数只不过是个开始。</p>
<h3>Currying</h3>
<p>我遇见的大多数码农都读过“<a href="http://baike.baidu.com/view/66964.htm#2">四人帮</a>”的那本《设计模式》。任何稍有自尊心的码农都会说这本书和语言无关,因此无论你用什么编程语言,当中提到的那些模式大体上适用于所有软件工程。听起来很厉害,然而事实却不是这样。 <br/>
函数式语言的表达能力很强。用这种语言编程的时候基本不需要设计模式,因为这种语言层次已经足够高,使得使用者可以以概念编程,从而完全不需要设计模式了。以<a href="http://zh.wikipedia.org/wiki/%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F">适配器模式</a>为例(有人知道这个模式和<a href="http://zh.wikipedia.org/wiki/%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F">外观模式</a>有什么区别吗?怎么觉得有人为了出版合同的要求而硬生生凑页数?)(译者:您不愧是高级黑啊)。对于一个支持currying技术的语言来说,这个模式就是多余的。 <br/>
在Jaya中最有名的适配器模式就是在其“默认”抽象单元中的应用:类。在函数式语言中这种模式其实就是函数。在这个模式中,一个接口被转换成另外一个接口,让不同的用户代码调用。接下来就有一个适配器模式的例子:</p>
<pre><code class="java">int pow(int i, int j);
int square(int i)
{
return pow(i, 2);
}
</code></pre>
<p>上面的代码中square函数计算一个整数的平方,这个函数的接口被转换成计算一个整数的任意整数次幂。在学术圈里这种简单的技术就被叫做currying(因为逻辑学家<a href="http://zh.wikipedia.org/wiki/%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F">哈斯凯尔·加里</a>用其数学技巧将这种技术描述出来,于是就以他的名字来命名了)。在一个FP语言中函数(而不是类)被作为参数进行传递,currying常常用于转化一个函数的接口以便于其他代码调用。函数的接口就是它的参数,于是currying通常用于减少函数参数的数量(见前例)。 <br/>
函数式语言生来就支持这一技术,于是没有必要为某个函数手工创建另外一个函数去包装并转换它的接口,这些函数式语言已经为你做好了。我们继续拓展Java来支持这一功能。</p>
<pre><code class="java">square = int pow(int i, 2);
</code></pre>
<p>上面的语句实现了一个平方计算函数,它只需要一个参数。它会继而调用pow函数并且把第二个参数置为2。编译过后将生成以下Java代码:</p>
<pre><code class="java">class square_function_t {
int square(int i) {
return pow(i, 2);
}
}
square_function_t square = new square_function_t();
</code></pre>
<p>从上面的例子可以看到,很简单的,函数pow的封装函数就创建出来了。在FP语言中currying就这么简单:一种可以快速且简单的实现函数封装的捷径。我们可以更专注于自己的设计,编译器则会为你编写正确的代码!什么时候使用currying呢?很简单,当你想要用适配器模式(或是封装函数)的时候,就是用currying的时候。</p>
<h3><a href="http://zh.wikipedia.org/zh/%E6%83%B0%E6%80%A7%E6%B1%82%E5%80%BC">惰性求值</a></h3>
<p>惰性求值(或是延迟求值)是一种有趣的技术,而当我们投入函数式编程的怀抱后这种技术就有了得以实现的可能。前面介绍并发执行的时候已经提到过如下代码:</p>
<pre><code class="java">String s1 = somewhatLongOperation1();
String s2 = somewhatLongOperation2();
String s3 = concatenate(s1, s2);
</code></pre>
<p>在指令式语言中以上代码执行的顺序是显而易见的。由于每个函数都有可能改动或者依赖于其外部的状态,因此必须顺序执行。先是计算somewhatLongOperation1,然后到somewhatLongOperation2,最后执行concatenate。函数式语言就不一样了。 <br/>
在前面讨论过,somewhatLongOperation1和somewhatLongOperation2是可以并发执行的,因为函数式语言保证了一点:没有函数会影响或者依赖于全局状态。可是万一我们不想要这两个函数并发执行呢?这种情况下是不是也还是要顺序执行这些函数?答案是否定的。只有到了执行需要s1、s2作为参数的函数的时候,才真正需要执行这两个函数。于是在concatenate这个函数没有执行之前,都没有需要去执行这两个函数:这些函数的执行可以一直推迟到concatenate()中需要用到s1和s2的时候。假如把concatenate换成另外一个函数,这个函数中有条件判断语句而且实际上只会需要两个参数中的其中一个,那么就完全没有必要执行计算另外一个参数的函数了!Haskell语言就是一个支持惰性求值的例子。Haskell不能保证任何语句会顺序执行(甚至完全不会执行到),因为Haskell的代码只有在需要的时候才会被执行到。 <br/>
除了这些优点,惰性求值也有缺点。这里介绍了它的优点,我们将在下一章节介绍这些缺点以及如何克服它们。</p>
<h4>代码优化</h4>
<p>惰性求值使得代码具备了巨大的优化潜能。支持惰性求值的编译器会像数学家看待代数表达式那样看待函数式程序:抵消相同项从而避免执行无谓的代码,安排代码执行顺序从而实现更高的执行效率甚至是减少错误。在此基础上优化是不会破坏代码正常运行的。严格使用形式系统的基本元素进行编程带来的最大的好处,是可以用数学方法分析处理代码,因为这样的程序是完全符合数学法则的。</p>
<h4>抽象化控制结构</h4>
<p>惰性求值技术提供了更高阶的抽象能力,这提供了实现程序设计独特的方法。比如说下面的控制结构:</p>
<pre><code class="java">unless(stock.isEuropean()) {
sendToSEC(stock);
}
</code></pre>
<p>程序中只有在stock为European的时候才执行sendToSEC。如何实现例子中的unless?如果没有惰性求值就需要求助于某种形式的宏(译者:用if不行么?),不过在像Haskell这样的语言中就不需要那么麻烦了。直接实现一个unless函数就可以!</p>
<pre><code class="haskell">void unless(boolean condition, List code) {
if(!condition)
code;
}
</code></pre>
<p>请注意,如果condition值为真,那就不会计算code。在其他严格语言(见<a href="http://zh.wikipedia.org/wiki/%E6%B1%82%E5%80%BC%E7%AD%96%E7%95%A5#.E4.B8.A5.E6.A0.BC.E6.B1.82.E5.80.BC_.28Strict_evaluation.29">严格求值</a>)中这种行为是做不到的,因为在进入unless这个函数之前,作为参数的code已经被计算过了。</p>
<h4>无穷数据结构</h4>
<p>惰性求值技术允许定义无穷数据结构,这要在严格语言中实现将非常复杂。例如一个储存Fibonacci数列数字的列表。很明显这样一个列表是无法在有限的时间内计算出这个无穷的数列并存储在内存中的。在像Java这样的严格语言中,可以定义一个Fibonacci函数,返回这个序列中的某个数。而在Haskell或是类似的语言中,可以把这个函数进一步抽象化并定义一个Fibonacci数列的无穷列表结构。由于语言本身支持惰性求值,这个列表中只有真正会被用到的数才会被计算出来。这让我们可以把很多问题抽象化,然后在更高的层面上解决它们(比如可以在一个列表处理函数中处理无穷多数据的列表)。</p>
<h4>不足之处</h4>
<p>俗话说天下没有免费的午餐™。惰性求值当然也有其缺点。其中最大的一个就是,嗯,惰性。现实世界中很多问题还是需要严格求值的。比如说下面的例子:</p>
<pre><code class="java">System.out.println("Please enter your name: ");
System.in.readLine();
</code></pre>
<p>在惰性语言中没人能保证第一行会中第二行之前执行!这也就意味着我们不能处理IO,不能调用系统函数做任何有用的事情(这些函数需要按照顺序执行,因为它们依赖于外部状态),也就是说不能和外界交互了!如果在代码中引入支持顺序执行的代码原语,那么我们就失去了用数学方式分析处理代码的优势(而这也意味着失去了函数式编程的所有优势)。幸运的是我们还不算一无所有。数学家们研究了不同的方法用以保证代码按一定的顺序执行(in a functional setting?)。这一来我们就可以同时利用到函数式和指令式编程的优点了!这些方法有continuations,monads以及uniqueness typing。这篇文章仅仅介绍了continuations,以后再讨论monads和uniqueness typing。有意思的是呢,coutinuations处理强制代码以特定顺序执行之外还有其他很多出处,这些我们在后面也会提及。</p>
<h3>Continuation</h3>
<p>continuation对于编程,就像是达芬奇密码对于人类历史一样:它揭开了人类有史以来最大的谜团。好吧,也许没有那么夸张,不过它们的影响至少和当年发现负数有平方根不相上下。</p>
<p>我们对函数的理解只有一半是正确的,因为这样的理解基于一个错误的假设:函数一定要把其返回值返回给调用者。按照这样的理解,continuation就是更加广义的函数。这里的函数不一定要把返回值传回给调用者,相反,它可以把返回值传给程序中的任意代码。continuation就是一种特别的参数,把这种参数传到函数中,函数就能够根据continuation将返回值传递到程序中的某段代码中。说得很高深,实际上没那么复杂。直接来看看下面的例子好了:</p>
<pre><code class="java">int i = add(5, 10);
int j = square(i);
</code></pre>
<p>add这个函数将返回15然后这个值会赋给i,这也是add被调用的地方。接下来i的值又会被用于调用square。请注意支持惰性求值的编译器是不能打乱这段代码执行顺序的,因为第二个函数的执行依赖于第一个函数成功执行并返回结果。这段代码可以用Continuation Pass Style(CPS)技术重写,这样一来add的返回值就不是传给其调用者,而是直接传到square里去了。</p>
<pre><code class="java">int j = add(5, 10, square);
</code></pre>
<p>在上例中,add多了一个参数:一个函数,add必须在完成自己的计算后,调用这个函数并把结果传给它。这时square就是add的一个continuation。上面两段程序中j的值都是225。</p>
<p>这样,我们学习到了强制惰性语言顺序执行两个表达式的第一个技巧。再来看看下面IO程序(是不是有点眼熟?):</p>
<pre><code class="java">System.out.println("Please enter your name: ");
System.in.readLine();
</code></pre>
<p>这两行代码彼此之间没有依赖关系,因此编译器可以随意的重新安排它们的执行顺序。可是只要用CPS重写它,编译器就必须顺序执行了,因为重写后的代码存在依赖关系了。</p>
<pre><code class="java"> System.out.println("Please enter your name: ", System.in.readLine);
</code></pre>
<p>这段新的代码中println需要结合其计算结果调用readLine,然后再返回readLine的返回值。这使得两个函数得以保证按顺序执行而且readLine总被执行(这是由于整个运算需要它的返回值作为最终结果)。Java的println是没有返回值的,但是如果它可以返回一个能被readnLine接受的抽象值,问题就解决了!(译者:别忘了,这里作者一开始就在Java的基础上修改搭建自己的语言)当然,如果一直把函数按照这种方法串下去,代码很快就变得不可读了,可是没有人要求你一定要这样做。可以通过在语言中添加<a href="http://zh.wikipedia.org/wiki/%E8%AF%AD%E6%B3%95%E7%B3%96">语法糖</a>的方式来解决这个问题,这样程序员只要按照顺序写代码,编译器负责自动把它们串起来就好了。于是就可以任意安排代码的执行顺序而不用担心会失去FP带来的好处了(包括可以用数学方法来分析我们的程序)!如果到这里还有人感到困惑,可以这样理解,函数只是有唯一成员的类的实例而已。试着重写上面两行程序,让println和readLine编程这种类的实例,所有问题就都搞清楚了。 <br/>
到这里本章基本可以结束了,而我们仅仅了解到continuation的一点皮毛,对它的用途也知之甚少。我们可以用CPS完成整个程序,程序里所有的函数都有一个额外的continuation作为参数接受其他函数的返回值。还可以把任何程序转换为CPS的,需要做的只是把当中的函数看作是特殊的continuation(总是将返回值传给调用者的continuation)就可以了,简单到完全可以由工具自动完成(史上很多编译器就是这样做的)。</p>
<p>一旦将程序转为CPS的风格,有些事情就变得显而易见了:每一条指令都会有一些continuation,都会将它的计算结果传给某一个函数并调用它,在一个普通的程序中这个函数就是该指令被调用并且返回的地方。随便找个之前提到过的代码,比如说add(5,10)好了。如果add属于一个用CPS风格写出的程序,add的continuation很明显就是当它执行结束后要调用的那个函数。可是在一个非CPS的程序中,add的continuation又是什么呢?当然我们还是可以把这段程序转成CPS的,可是有必要这样做吗? <br/>
事实上没有必要。注意观察整个CPS转换过程,如果有人尝试要为CPS程序写编译器并且认真思考过就会发现:CPS的程序是不需要栈的!在这里完全没有函数需要做传统意义上的“返回”操作,函数执行完后仅需要接着调用另外一个函数就可以了。于是就不需要在每次调用函数的时候把参数压栈再将它们从中取出,只要把这些参数存放在一片内存中然后使用跳转指令就解决问题了。也完全不需要保留原来的参数:因为这种程序里的函数都不返回,所以它们不会被用第二次! <br/>
简单点说呢,用CPS风格写出来的程序不需要栈,但是每次调用函数的时候都会要多加一个参数。非CPS风格的程序不需要额外的参数但又需要栈才能运行。栈里面存的是什么?仅仅是参数还有一个供函数运行结束后返回的程序指针而已。这个时候你是不是已经恍然大悟了?对啊,栈里面的数据实际上就是continuation的信息!栈上的程序返回指针实质上就是CPS程序中需要调用的下一个函数!想要知道add(5, 10)的continuation是什么?只要看它运行时栈的内容就可以了。 <br/>
接下来就简单多了。continuation和栈上指示函数返回地址的指针其实是同一样东西,只是continuation是显式的传递该地址并且因此代码就不局限于只能返回到函数被调用的地方了。前面说过,continuation就是函数,而在我们特制的语言中函数就是类的实例,那么可以得知栈上指向函数返回地址的指针和continuation的参数是一样的,因为我们所谓的函数(就像类的一个实例)其实就是指针。这也意味着在程序运行的任何时候,你都可以得到当前的continuation(就是栈上的信息)。</p>
<p>好了,我们已经搞清楚当前的continuation是什么了。接下来要弄明白它的存在有什么意义。只要得到了当前的continuation并将它保存起来,就相当于保存了程序的当前状态:在时间轴上把它冻结起来了。这有点像操作系统进入休眠状态。continuation对象保存了足够的信息随时可以从指定的某个状态继续运行程序。在切换线程的时候操作系统也是这样做的。唯一的区别在于它保留了所有的控制权利。当请求某个continuation对象时(在Scheme语言中是通过调用call-with-current-continuation函数实现的)得到的是一个存有当前continuation的对象,也就是栈对象(在CPS中也就是下一个要执行的函数)。可以把这个对象保存做一个变量中(或者是存在磁盘上)。当以该continuation对象“重启”该程序时,程序的状态就会立即“转换”为该对象中保存的状态。这一点和切换回一个被暂停的线程或是从系统休眠中唤醒很相像,唯一不同的是continuatoin对象可以反复的这样使用。当系统唤醒后,休眠前保存的信息就会销毁,否则你也可以反复的从该点唤醒系统,就像乘时光机回到过去一样。有了continuation你就可以做到这一点!</p>
<p>那么continuation在什么情况下有用呢?有一些应用程序天生就没有状态,如果要在这样的系统中模拟出状态以简化工作的时候,就可以用到continuation。最合适的应用场合之一就是网页应用程序。微软的ASP.NET为了让程序员更轻松的编写应用程序,花了大量的精力去模拟各种状态。假如C#支持continuation的话,那么ASP.NET的复杂度将减半:因为只要把某一时刻的continuation保存起来,下次用户再次发起同样请求的时候,重新载入这个continuation即可。对于网络应用的程序员来说就再也没有中断了:轻轻松松程序就从下一行开始继续运行了!对于一些实际问题来说,continuation是一种非常有用的抽象工具。如今大量的传统胖客户端(见<a href="http://zh.wikipedia.org/wiki/%E7%98%A6%E5%AE%A2%E6%88%B7%E7%AB%AF">瘦客户端</a>)正纷纷走进网络,continuation在未来将扮演越来越重要的角色。</p>
<h3>模式匹配</h3>
<p>模式匹配并不是什么新功能。而事实上它和函数式编程也没有什么太大的关系。它之所以常常被认为是FP的一个特性,是因为在函数式语言已经支持模式匹配很长一段时间后的今天,指令式语言是还没有这个功能。</p>
<p>还是直接用例子来看看什么是模式匹配吧,这是一个用Java写的Fibonacci函数:</p>
<pre><code class="java"> int fib(int n) {
if(n == 0) return 1;
if(n == 1) return 1;
return fib(n - 2) + fib(n - 1);
}
</code></pre>
<p>再看看用我们基于Java修改过的新语言写出来的Fibonacci函数,这种新语言就支持模式匹配:</p>
<pre><code class="java"> int fib(0) {
return 1;
}
int fib(1) {
return 1;
}
int fib(int n) {
return fib(n - 2) + fib(n - 1);
}
</code></pre>
<p>区别在哪里呢?在于后者的编译器替我们实现了程序的分支。 <br/>
这有什么了不起的?确实也没什么。只是有人注意到很多函数中有非常复杂的switch结构(对于函数式程序而言更是如此),于是想到如果能把这层结构也抽象化就更好了。然后就把这个复杂的函数拆分成若干新的函数,并在这些函数的某些参数中应用模式(这和<a href="http://zh.wikipedia.org/wiki/%E5%87%BD%E6%95%B0%E9%87%8D%E8%BD%BD">重载</a>有点类似)。这样依赖当这个函数被调用的时候,编译器会在运行时将调用者传入的参数与各个新函数的参数定义进行比较,找出合适的那个函数来执行。合适的函数往往是参数定义上最具体最接近传入参数的那个函数。在这个例子中,当n为1时,可以用函数int fib(int n),不过真正调用的是int fib(1)因为这个函数更具体更接近调用者的要求。 <br/>
模式匹配一般来说要比这里举的例子更加复杂。比如说,高级模式匹配系统可以支持下面的操作:</p>
<pre><code class="java">int f(int n < 10) { ... }
int f(int n) { ... }
</code></pre>
<p>那么什么情况下模式匹配会有用呢?在需要处理一大堆程序分支的时候!每当需要实现复杂的嵌套if语句的时候,模式匹配可以帮助你用更少的代码更好的完成任务。我所知道的一个这样的函数是标准的WndProc函数,该函数是所有Win32应用程序必须具备的(尽管它经常会被抽象化)。模式匹配系统一般都可以像匹配简单数值一样匹配数据集合。举个例子,对于一个接受数组作为参数的函数,可以通过模式匹配数组中第一个数字为1并且第三个数字大于3的输入。
模式匹配的另外一个好处是每当需要添加或者修改程序分支时,再也不用面对那个庞大臃肿的函数了。只要添加(或者修改)相关的函数定义即可。有了模式匹配就不再需要四人帮的很多设计模式了。程序分支越多越复杂,模式匹配就越有用。而在习惯使用这一技术之后,你可能会怀疑没有它你一天都过不下去了。</p>
<h3>Closure</h3>
<p>目前为止关于函数式编程各种功能的讨论都只局限在“纯”函数式语言范围内:这些语言都是lambda演算的实现并且都没有那些和阿隆佐形式系统相冲突的特性。然而,很多函数式语言的特性哪怕是在lambda演算框架之外都是很有用的。确实,如果一个公理系统的实现可以用数学思维来看待程序,那么这个实现还是很有用的,但这样的实现却不一定可以付诸实践。很多现实中的语言都选择吸收函数式编程的一些元素,却又不完全受限于函数式教条的束缚。很多这样的语言(比如Common Lisp)都不要求所有的变量必须为final,可以修改他们的值。也不要求函数只能依赖于它们的参数,而是可以读写函数外部的状态。同时这些语言又包含了FP的特性,如高阶函数。与在lambda演算限制下将函数作为参数传递不同,在指令式语言中要做到同样的事情需要支持一个有趣的特性,人们常把它称为lexical closure。还是来看看例子。要注意的是,这个例子中变量不是final,而且函数也可以读写其外部的变量:</p>
<pre><code class="java"> Function makePowerFn(int power) {
int powerFn(int base) {
return pow(base, power);
}
return powerFn;
}
Function square = makePowerFn(2);
square(3); // returns 9
</code></pre>
<p>makePowerFn函数返回另一个函数,这个新的函数需要一个整数参数然后返回它的平方值。执行square(3)的时候具体发生了什么事呢?变量power并不在powerFn的域内,因为makePowerFn早就运行结束返回了,所以它的栈也已经不存在了。那么square又是怎么正常工作的呢?这个时候需要语言通过某种方式支持继续存储power的值,以便square后面继续使用。那么如果再定义一个函数,cube,用来计算立方,又应该怎么做呢?那么运行中的程序就必须存储两份power的值,提供给makePowerFn生成的两个函数分别使用。这种保存变量值的方法就叫做closure。closure不仅仅保存宿主函数的参数值,还可以用在下例的用法中:</p>
<pre><code class="java">Function makeIncrementer() {
int n = 0;
int increment() {
return ++n;
}
}
Function inc1 = makeIncrementer();
Function inc2 = makeIncrementer();
inc1(); // returns 1;
inc1(); // returns 2;
inc1(); // returns 3;
inc2(); // returns 1;
inc2(); // returns 2;
inc2(); // returns 3;
</code></pre>
<p>运行中的程序负责存储n的值,以便incrementer稍后可以访问它。与此同时,程序还会保存多份n的拷贝,虽然这些值应该在makeIncrementer返回后就消失,但在这个情况下却继续保留下来给每一个incrementer对象使用。这样的代码编译之后会是什么样子?closure幕后的真正工作机理又是什么?这次运气不错,我们有一个后台通行证,可以一窥究竟。 <br/>
一点小常识往往可以帮大忙。乍一看这些本地变量已经不再受限于基本的域限制并拥有无限的生命周期了。于是可以得出一个很明显的结论:它们已经不是存在栈上,而是堆上了<sup>8</sup>。这么说来closure的实现和前面讨论过的函数差不多,只不过closure多了一个额外的引用指向其外部的变量而已:</p>
<pre><code class="java"> class some_function_t {
SymbolTable parentScope;
// ...
}
</code></pre>
<p>当closure需要访问不在它本地域的变量时,就可以通过这个引用到更外一层的父域中寻找该变量。谜底揭开了!closure将函数编程与面向对象的方法结合了起来。下一次为了保存并传递某些状态而创建类的时候,想想closure。它能在运行时从相应的域中获得变量,从而可以把该变量当初“成员变量”来访问,也因为这样,就不再需要去创建一个成员变量了。</p>
<h3>路在何方?</h3>
<p>这篇文章仅仅涉及到函数式编程的一些皮毛。考虑到有时候星星之火可以燎原,所以如果它能给你一些帮助那就再好不过了。接下来我计划就<a href="http://zh.wikipedia.org/wiki/%E8%8C%83%E7%95%B4%E8%AE%BA">范畴论</a>、monads、函数式编程数据结构、函数式语言中的<a href="http://zh.wikipedia.org/wiki/%E9%A1%9E%E5%9E%8B%E7%B3%BB%E7%B5%B1">类型系统</a>、并行函数式编程、数据库的函数式编程以及更多的话题写些类似的文章。如果我可以写出(在我学习的同时)以上清单的一半,我的人生就完整了。于此同时,Google将是我们的良师益友。</p>
<h3>欢迎联系</h3>
<p>如果您有任何问题,评价或者建议,请发邮件到coffeemug@gmail.com(译者:如果翻译方面的问题/建议请发到yang.huang@ymail.com:))。期待您的回复。</p>
<p>注: <br/>
<sup>1</sup>当我在2005年求职时的的确确经常问别人这个问题。看着那些茫然的面孔实在是很好玩的事情。你们这些年薪30万美金的家伙,至少应该对自己可以利用的工具有个起码的理解嘛。 <br/>
<sup>2</sup>这是个有争议的问题。物理学家和数学家不得不承认目前还无法确定宇宙万物是不是都遵从可以用数学方法描述的各种法则。 <br/>
<sup>3</sup>我一直一来都很讨厌在历史课上罗列一堆枯燥无味的时间、人名、事件。对我来说历史就是关于那些改变世界的人们活生生的故事,是他们行为背后的个人动机,是那些他们用以影响芸芸众生的方法和工具。从这个角度来说,接下来的这堂历史课是不完整的,很遗憾。只有那些非常相关的人和事会被提及。 <br/>
<sup>4</sup>在我学习函数式编程的时候,“lambda”这个术语搞得我很烦,因为我不知道它到底是什么意思。在这里lambda就是一个函数,在数学符号中用这个希腊字母只是因为它更容易写。所以以后在谈及函数式编程的时候只要你听到lambda,把它在脑中翻译为“函数”就可以了。 <br/>
<sup>5</sup>有意思的是不论如何Java中的字符串总是不可修改的。讨论这种背叛Java的设计背后的原因会很有意思,可惜这样会让我们跑题的。 <br/>
<sup>6</sup>大部分函数式语言的编译器都会尽量将迭代函数转换为对等的循环语句。这种做法叫做<a href="http://zh.wikipedia.org/wiki/%E5%B0%BE%E8%B0%83%E7%94%A8">尾调用优化</a>。 <br/>
<sup>7</sup>反之则不一定成立。尽管有时候可以证明两段代码是等价的,但不是在所有的情况下都可以得出这样的结论。 <br/>
<sup>8</sup>实际上这样做并不比栈上存储要慢,因为在引入<a href="http://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8">垃圾回收机制</a>)后,内存分配操作的时间代价仅为O(1)。</p>
RetroArch for iOS2015-04-10T00:00:00+00:00http://jiaxianhua.github.io/retroarch/2015/04/10/retroarch-for-ios<h1>Get RetroArch</h1>
<p><figure class="highlight"><pre><code class="language-bash" data-lang="bash">git clone https://github.com/libretro/RetroArch.git</p>
<p><span class="nb">cd </span>RetroArch</p>
<p>./fetch_submodules</code></pre></figure></p>
<h1>RetroArch for iOS</h1>
<hr />
<p><a href="https://github.com/libretro/RetroArch/tree/master/apple">https://github.com/libretro/RetroArch/tree/master/apple</a></p>
<p>RetroArch for iOS can be run directly on your device without the need for jailbreaking. To do this, you will need a few things:</p>
<ul>
<li>Your own iOS Apple developer account</li>
<li>Your developer account set up on your computer (your certs, etc.)</li>
<li>A Distribution provisioning profile for RetroArch (a wildcard profile is fine and suggested)</li>
</ul>
<p>Once you have all of this stuff, getting RetroArch on a non-jailbroken device is pretty simple.</p>
<h2>Config</h2>
<p>RetroArch needs to know a couple things when building the app. You can configure these under <code>apple/script/build.config</code> inside the RetroArch repo. Once you initially clone down the repo, go into this file and make the changes.</p>
<h3>CODE_SIGN_IDENTITY</h3>
<p>This is the identity that will be used when signing the app after it is built. Under normal circumstance, you shouldn't have to change this. But if you have multiple Apple dev accounts on your computer, you will have to be more specific.</p>
<pre><code>CODE_SIGN_IDENTITY="iPhone Distribution: Bill Cosby"
</code></pre>
<p>Adding the name of the account will make sure it uses the exact codesigning identity.</p>
<h3>PROVISIONING</h3>
<p>Before you build the app, you'll need to download the provisioning profile you want to use for the app into the <code>apple</code> directory. Just drop it in there, and add the file name to the config.</p>
<p>When RetroArch is built into an IPA, it will embed this provisioning profile into the IPA so you can just install the IPA right onto your phone.</p>
<h2>Building</h2>
<p>After you've configured the right things, you're ready for building. You'll want to run these from the <code>apple</code> directory of the RetroArch project.</p>
<h3>Emulator Cores</h3>
<p>Before building the app, you'll need to build the libretro cores.</p>
<p>You'll need to clone down the <a href="https://github.com/libretro/libretro-super">libretro/libretro-super</a> repo into the same directory where you cloned this repo.</p>
<p>Your directories should look like this:</p>
<pre><code>your-repos-dir/libretro-super
your-repos-dir/RetroArch
</code></pre>
<p>Run the libretro-super fetch script to first get the libretro core repos.</p>
<pre><code class="sh">./libretro-fetch.sh
</code></pre>
<p>Run the libretro-super iOS build script to build the libretro cores.</p>
<pre><code class="sh">./libretro-build-ios.sh
</code></pre>
<p>This will clone down their repos, build them into the <code>libretro-super/dist/ios</code> directory. Once this is complete, you need to copy them into the appropriate directory for RetroArch iOS. The path is:</p>
<p><code>RetroArch/apple/iOS/modules</code></p>
<h3>Build RetroArch iOS app</h3>
<p>Now just run:</p>
<pre><code class="sh">script/build
</code></pre>
<p>This will build the iOS app, codesign everything that needs to, and package it into a distributable IPA.</p>
<p>Once completed, you can find the IPA inside the <code>apple/build/Release-iphoneos</code> directory. Excecute the IPA file and then open iTunes, with you device connected, click in your device, then Applications, find de RetroArch in the list, click install and syncronize with you device.</p>
<h2>Roms</h2>
<p>iTunes File Sharing is enabled on RetroArch. You can simply drag your rom files into the RetroArch app. They will be available the next time you launch the app.</p>
<p>Alternatively, you can use something like <a href="http://www.macroplant.com/iexplorer">iExplorer</a> to manually copy files over. Doing this will give you the benefit of being able to use directories, since iTunes File Sharing does not support directories.</p>
<h2>BTstack (Jailbreak only)</h2>
<p>With the BTstack cydia package installed both WiiMote and PS3 Pads are usable. To enable BTstack usage tap the Settings button in the upper left corner and turn on the 'Enable BTstack' option.</p>
<h3>Connecting a WiiMote</h3>
<p>Press both the 1 and 2 buttons on the WiiMote at the same time. The WiiMote is successfully completed when the 4 lights stop flashing.</p>
<h3>Connecting PS3</h3>
<p>Before a PS3 pad can be used it must be manually paired with your device using a PC, USB cable, and a special piece of software.</p>
<p>The pairing software will likely ask for the bluetooth address of your device, this can be found under the 'Diagnostic Log' option in the settings menu.</p>
<p>The line will appear as:
<code>
BTpad: Local address is XX:XX:XX:XX:XX:XX
</code></p>
trip to iOS2015-04-09T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/04/09/trip-to-ios<p><a href="https://github.com/Aufree/trip-to-iOS">https://github.com/Aufree/trip-to-iOS</a></p>
<h1>iOS 学习资料整理</h1>
<hr />
<p>这份学习资料是为 iOS 初学者所准备的, 旨在帮助 iOS 初学者们快速找到适合自己的学习资料, 节省他们搜索资料的时间, 使他们更好的规划好自己的 iOS 学习路线, 更快的入门, 更准确的定位的目前所处的位置.</p>
<p>该文档会持续更新, 同时也欢迎更多具有丰富经验的 iOS 开发者将自己的常用的一些工具, 学习资料, 学习心得等分享上来, 我将定期筛选合并, 文档尚有一些不完善之处, 也请不吝指出, 感谢您对 iOS 所做的贡献, 让我们一起把国内的 iOS 做得更好, 谢谢.</p>
<p><strong>如果您有任何意见或建议也可以通过<a href="mailto:freedomlijinfa@gmail.com">邮件</a>或<a href="http://weibo.com/jinfali">微博</a>联系我</strong></p>
<h2>视频教程(英文)</h2>
<table>
<thead>
<tr>
<th>视频 </th>
<th> 简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://itunes.apple.com/us/course/developing-ios-7-apps-for/id733644550">Developing iOS 7 Apps for iPhone and iPad</a> </td>
<td> 斯坦福开放教程之一, 课程主要讲解了一些 iOS 开发工具和 API 以及 iOS SDK 的使用, 属于 iOS 基础视频</td>
</tr>
<tr>
<td><a href="https://itunes.apple.com/itunes-u/ipad-iphone-application-development/id473757255">iPad and iPhone Application Development</a> </td>
<td> 该课程的讲师 Paul Hegarty 是斯坦福大学软件工程学教授, 视频内容讲解得深入, 权威, 深受好评</td>
</tr>
<tr>
<td><a href="https://itunes.apple.com/itunes-u/advanced-iphone-development/id407243028">Advanced iPhone Development - Fall 2010</a> </td>
<td> iOS 开发的进阶课程, 开始涉及到 Core Animation, Core Data, OpenGL 等框架的应用</td>
</tr>
<tr>
<td><a href="https://developer.apple.com/devcenter/ios/index.action">iOS Dev Center</a> </td>
<td> 苹果官方提供的 iOS 学习视频</td>
</tr>
<tr>
<td><a href="http://www.lynda.com/search?q=ios">Lynda</a> </td>
<td> Lynda 上面 iOS 和 Objective-C 的学习资料比较多, 从初级到高级的都有, 覆盖面比较广, 无论 iOS 走到哪个层次, 都可以在上面挑到适合自己的课程</td>
</tr>
<tr>
<td><a href="https://www.codeschool.com/paths/ios">Code School</a> </td>
<td> CodeSchool 上面的 iOS 不多, 不过质量都不错, 一些课程也挺有趣的</td>
</tr>
<tr>
<td><a href="https://www.udemy.com/topic/learn-objective-c">Udemy</a> </td>
<td> Udemy 帮助初学者规划了视频学习路线, 从新手到高级分的比较详细</td>
</tr>
<tr>
<td><a href="https://itunes.apple.com/us/course/developing-ios-8-apps-swift/id961180099">Developing iOS 8 Apps with Swift</a> </td>
<td> 斯坦福白胡子老爷爷最新的 iOS8 和 Swift 课程, 现在 <a href="https://github.com/x140yu/Developing_iOS_8_Apps_With_Swift">GitHub</a> 上面也有人在翻译</td>
</tr>
</tbody>
</table>
<h2>视频教程(中文)</h2>
<table>
<thead>
<tr>
<th>视频 </th>
<th> 简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://v.163.com/special/opencourse/ios7.html">iOS 7 应用开发</a> </td>
<td> 斯坦福白胡子老爷爷的系列视频, 所有视频皆完成翻译, 视频较新, 翻译质量也很高</td>
</tr>
<tr>
<td><a href="http://v.163.com/special/opencourse/iphonekaifa.html">iPhone 开发教程 2010 年冬</a> </td>
<td> 全部视频翻译完毕, 较为深入的讲解 iPhone 开发, 视频适合给有一定 Objective-C 基础的人观看</td>
</tr>
<tr>
<td><a href="http://www.imooc.com/learn/173">使用 Swift 开发 iOS8 App 实战</a> </td>
<td> 慕课网的视频, 主要讲 Swift 的一些基本使用, 并在讲解的过程中做了几个小 APP, 最后还讲了 Sketch 制作分享按钮</td>
</tr>
<tr>
<td><a href="http://www.imooc.com/learn/218">征战 Objective-C</a> </td>
<td> 视频还未完结, 讲了一些 C 和 Objective-C 的基本语法, 适合零基础的人观看</td>
</tr>
<tr>
<td><a href="https://github.com/x140yu/Developing_iOS_8_Apps_With_Swift">Developing iOS 8 Apps with Swift</a> </td>
<td> GitHub 上正在翻译的斯坦福最新的 iOS8 课程, 目前正在翻译, 未完结</td>
</tr>
</tbody>
</table>
<h2>书籍</h2>
<table>
<thead>
<tr>
<th>书籍名称 </th>
<th> 推荐理由</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://www.amazon.com/dp/032194206X/ref=cm_sw_r_tw_dp_fWrPub0BGRHJ3">Objective-C Programming</a> </td>
<td> 内容不多, 却都是精华, 有了一点 C 语言基础可以快速阅读此书, 大概一天时间就可以看完, 看完后对 iOS 开发能够有个基本的印象, 该书的<a href="http://forums.bignerdranch.com">官方论坛</a>有各个章节习题的解答.</td>
</tr>
<tr>
<td><a href="http://book.douban.com/subject/24538384">iOS Programming</a> </td>
<td> 这本书在 Quora 上被评为 iOS 入门最佳书籍, 具体评价可见豆瓣下方该书籍的评论</td>
</tr>
<tr>
<td><a href="http://book.douban.com/subject/3688896">Cocoa Design Patterns</a> </td>
<td> 适合打算深入了解 Cocoa 的人看</td>
</tr>
<tr>
<td><a href="http://cocoadevcentral.com/d/learn_objectivec">Learn Objective-C</a> </td>
<td> 短小精练, 适合有编程基础的人在半小时内对 Objective-C 有个一定了解</td>
</tr>
<tr>
<td><a href="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html">Programming with Objective-C</a> </td>
<td> 看完 Learn Objective-C 可以接着看这个官方对 Objective-C 更为深入的介绍</td>
</tr>
<tr>
<td><a href="http://www.amazon.cn/dp/B00COG40Y0/ref=cm_sw_r_qz_2_dp_l2OPub0N45R0Q">Objective-C 基础教程</a> </td>
<td> 该书作者 Scott Knaster 是 Mac 开发界的传奇人物, 目前在 Google 出过多数书籍都广受许多程序员好评, 此书适合从初级跳到中级的 iOS 开发者阅读</td>
</tr>
<tr>
<td><a href="http://www.amazon.cn/dp/B00R43XG9S/ref=cm_sw_r_qz_pi_T2A_jdp_fCPPub0VBF67T">iOS 开发进阶</a> </td>
<td> 该书作者唐巧是国内 iOS 开发界的名人, 曾参与多个知名软件的开发, 目前该书尚在预售中, 书本内容由浅入深, 将读者一步一步引入到 iOS 中去, 同样适合初级跳到中级的 iOS 开发者阅读</td>
</tr>
<tr>
<td><a href="http://www.amazon.com/Programming-Objective-C-Edition-Developers-Library/dp/0321967607">Programming in Objective-C</a> </td>
<td> 这本书在亚马逊上面深受欢迎, 有关 Objective-C 的东西讲得非常详细</td>
</tr>
<tr>
<td><a href="http://www.amazon.cn/dp/B00JPVNFKM/ref=cm_sw_r_qz_4_dp_tdPPub14X59PV">iOS 测试指南</a> </td>
<td> 该书作者是豆瓣的员工, 书中写的多数内容都是作者在平时的工作实践当中提炼出来的测试经验, 重点讲述了各个测试阶段的具体实践方法, 并且通过持续集成串联了各个测试阶段的活动。</td>
</tr>
<tr>
<td><a href="http://book.douban.com/subject/6920082">Objective-C 编程之道</a> </td>
<td> 解析 iOS 的开山之作, 详细介绍了 MVC 在 Cocoa Touch 上的运作过程, 该书适用于 iOS 中级开发者阅读</td>
</tr>
<tr>
<td><a href="http://www.amazon.cn/dp/B00DE60G3S/ref=cm_sw_r_qz_2_dp_hdPPub11MFE6G">Objective-C 高级编程</a> </td>
<td> 本书主要介绍 iOS 与 OS X 多线程和内存管理, 深入破析了苹果官方公布的源代码, 告诉你一些苹果公司官方文档中不会出现的知识, 适合中级以上 iOS 开发人员阅读</td>
</tr>
<tr>
<td><a href="http://www.amazon.cn/dp/B00IDSGY06/ref=cm_sw_r_qz_2_dp_A2OPub0CH96YH">Effective Objective C 2.0</a> </td>
<td> 书里写了编写高质量 iOS 与 OS X 代码的 52 个有效方法, 适合 iOS 开发的进阶使用</td>
</tr>
<tr>
<td><a href="http://www.amazon.com/dp/0990402053/ref=cm_sw_r_tw_dp_louPub127Q1YP">Swift Fundamentals</a> </td>
<td> 估计将来这本书会成为 Swift 的经典入门书籍, 它的 Stars 数说明了一切</td>
</tr>
<tr>
<td><a href="http://numbbbbb.gitbooks.io/-the-swift-programming-language-/content">The Swift Programming Language 中文版</a> </td>
<td> 90 后开发者梁杰组织翻译的 Swift 编程语言中文版</td>
</tr>
</tbody>
</table>
<h2>博客</h2>
<table>
<thead>
<tr>
<th>博客地址 </th>
<th> 博主信息</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://onevcat.com/#blog">OneV's Den</a> </td>
<td> 王巍(喵神), 现居日本, 就职于 LINE, 知名 iOS 开发者, 写的文章大多深入浅出, 内容广泛, 目前在维护的 <a href="http://swifter.tips">Swifter</a> 也值得收藏</td>
</tr>
<tr>
<td><a href="http://blog.devtang.com">唐巧的技术博客</a> </td>
<td> 唐巧, 国内知名 iOS 开发者, 现就职于猿题库, 博客推出的 iOS 移动开发周报很受欢迎, 更新频繁</td>
</tr>
<tr>
<td><a href="http://blog.txx.im">txx's blog</a> </td>
<td> 90 后 iOS 开发者, 人称虾神, 文章内容讲解大多浅白易懂, 很值得看</td>
</tr>
<tr>
<td><a href="http://beyondvincent.com">破船之家</a> </td>
<td> 博主也是 iOS 大神一个, 经常更新一些 iOS 教程, 文章的质量都很高, 非常值得看</td>
</tr>
<tr>
<td><a href="http://nshipster.cn">NSHipster</a> </td>
<td> NSHipster 的中文网站, 主要对 NSHipster 的英文网站进行翻译, 博文出自 Mattt 大神之手, 文章大都写得很深入, 详细, 每周一更</td>
</tr>
<tr>
<td><a href="http://blog.leezhong.com">Limboy 无网不剩</a> </td>
<td> 李忠, 知乎前员工, 目前在负责花瓣 iOS 开发, 不少文章里面有介绍博主个人的学习方法, 让读者在学到技术的同时也掌握学习的技巧</td>
</tr>
<tr>
<td><a href="http://nianxi.net">念茜的博客</a> </td>
<td> iOS 圈的女神人物, 写的关于安全问题的文章都值得一看, 由于新博客刚开通不久, 目前文章较少, 可以去看下她以前的<a href="http://blog.csdn.net/yiyaaixuexi">博客</a></td>
</tr>
<tr>
<td><a href="http://weekly.ios-wiki.com/history">iOS技术周报</a> </td>
<td> 吴发伟, 天猫资深软件开发工程师, iOS 技术周报每周一更, 推送一些 iOS 技巧, 代码库, 设计等资讯.</td>
</tr>
<tr>
<td><a href="http://www.iwangke.me">iWangKe.me</a> </td>
<td> 王轲, IndieBros Studio 创始人, 优秀的 iOS 开发工程师, 写的文章深入浅出, 很多问题分析透彻, 非常有条理性</td>
</tr>
<tr>
<td><a href="http://www.jianshu.com/p/99e8b3f6f377">叶孤城</a> </td>
<td> 叶孤城, 优秀 iOS 开发工程师, 发表的文章都有很多干货, 对源码解析类文章写得浅显易懂, 并时常总结一些 iOS 开发技巧, 值得一读</td>
</tr>
<tr>
<td><a href="http://zhowkev.in">Kevin Blog</a> </td>
<td> 周楷雯, 秒视创始人, 知名 iOS 工程师, 做出了 <a href="https://github.com/kevinzhow/PNChart">PNChart</a> 和 <a href="https://github.com/kevinzhow/Waver">Waver</a> 这样的好项目, 在博客中也有谈到具体的实现过程</td>
</tr>
<tr>
<td><a href="http://imtx.me">IMTX</a> </td>
<td> 图拉鼎, 知名 Apple 平台开发者, 曾经的 Ubuntu 平台开发者, 文章有不少干货, 大多讲解技术实现和学习经验</td>
</tr>
<tr>
<td><a href="https://github.com/tangqiaoboy/iOSBlogCN">更多</a> </td>
<td> 唐巧收集的中文 iOS/Mac 开发博客列表, 更新频繁, 值得收藏</td>
</tr>
</tbody>
</table>
<h2>文章</h2>
<table>
<thead>
<tr>
<th>标题 </th>
<th> 内容简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.udemy.com/blog/learn-objective-c">Learn Objective C: The Path to iPhone Development</a> </td>
<td> Udemy 写的文章, 说明了一些学习 Objective-C 的前提条件, Objective-C 的发展历史, 学习方法以及学习资源</td>
</tr>
<tr>
<td><a href="http://lifehacker.com/i-want-to-write-ios-apps-where-do-i-start-1644802175">I Want to Write iOS Apps. Where Do I Start?</a> </td>
<td> 主要对 iOS 的开发环境进行了介绍, 并且涉及到了 Swift 的学习, iOS 上架的注意事项, iOS 的设计, 测试, 代码托管等, 讲解较为广泛, 同时也给出不少学习资源</td>
</tr>
<tr>
<td><a href="http://roadfiresoftware.com/2014/04/how-to-become-a-professional-ios-developer">How to become a professional iOS developer</a> </td>
<td> 文章写的很有条理, 文中多次强调了版本控制系统的重要性, 主要内容是对学习 iOS 开发到就职, 给出了自己的建议</td>
</tr>
<tr>
<td><a href="http://codewithchris.com/learning-ios-programming">Learning iOS Programming</a> </td>
<td> 作者总结了一些自己学习 iOS 的血的教训, 最后给出了一些不错学习建议</td>
</tr>
<tr>
<td><a href="https://www.udacity.com/career-paths/ios-developer">Become an iOS Developer</a> </td>
<td> 作者列举了一些学习 iOS 的方法以及常用的库, 以及自学 iOS 的一些建议</td>
</tr>
<tr>
<td><a href="http://www.devtang.com/blog/2014/07/27/ios-levelup-tips">iOS 开发如何提高</a> </td>
<td> 唐巧写的一篇文章, 主要是对 iOS 技术的提高做的一个总结, 文中不少资源, 工具, 学习方法</td>
</tr>
<tr>
<td><a href="http://limboy.me/ios/2014/12/31/learning-ios.html">自学 iOS 开发的一些经验</a> </td>
<td> 文章从入门到进阶到高级, 分为三个阶段, 有条理的讲出了 iOS 的整个学习过程中开发者可能遇到的问题, 并给出了解决办法, 奉献了不少好工具, 资源还有珍贵的学习经验</td>
</tr>
<tr>
<td><a href="http://readful.com/post/101914515826/0-ios">如何从 0 开始学 iOS 开发</a> </td>
<td> 作者给出了学习 iOS 的流程, 并给出一些不错的学习资源</td>
</tr>
<tr>
<td><a href="http://www.cocoachina.com/programmer/20141128/10353.html">如果我可以重新学习 iOS 开发</a> </td>
<td> 作者在文中给出了学习的一些建议, 也谈到了自己的学习方法</td>
</tr>
<tr>
<td><a href="http://www.cocoachina.com/ios/20141106/10147.html">iOS 开发学习路径的一些建议</a> </td>
<td> 文中谈到了英语的重要性, 以及写博客, 看源代码的好处</td>
</tr>
<tr>
<td><a href="http://www.jianshu.com/p/KSuDqb">iOS 开发入门</a> </td>
<td> 作者分享了自己学习 iOS 的经验和资源</td>
</tr>
<tr>
<td><a href="http://beyondvincent.com/blog/2013/07/18/106">Mac 和 iOS 开发资源汇总</a> </td>
<td> 破船之家发布的资源汇总</td>
</tr>
<tr>
<td><a href="http://www.raywenderlich.com/64546/introduction-to-cocoapods-2">CocoaPods 使用教程</a> </td>
<td> 文章讲解了 CocoaPods 的基本使用, 并且配上 AFNetworking 做出了一个小 Demo, 值得一看</td>
</tr>
</tbody>
</table>
<h2>相关网站</h2>
<table>
<thead>
<tr>
<th>网站 </th>
<th> 简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://tutsplus.com/tutorials/search?utf8=%E2%9C%93&search%5Btopic%5D=&search%5Bterms%5D=ios&button=">tutsplus</a> </td>
<td> 不定时更新一些 iOS 教程</td>
</tr>
<tr>
<td><a href="https://developer.apple.com/videos">WWDC</a> </td>
<td> 苹果官方每年一度的 WWDC 视频, 可以了解历年有关 iOS 发布的内容</td>
</tr>
<tr>
<td><a href="http://asciiwwdc.com">ASCIIwwdc</a> </td>
<td> WWDC 的文字版</td>
</tr>
<tr>
<td><a href="https://swift.zeef.com/robin.eggenkamp">Awesome Swift</a> </td>
<td> 该网站收集了很多关于 Swift 的学习资料, 新闻</td>
</tr>
<tr>
<td><a href="http://www.appcoda.com">Appcoda</a> </td>
<td> 经常发布一些 iOS 编程教程, 更新比较频繁, 想了解更多可以查看该网站的 About 界面</td>
</tr>
<tr>
<td><a href="http://nshipster.com">NSHipster</a> </td>
<td> NSHipster is a journal of the overlooked bits in Objective-C, Swift, and Cocoa. Updated weekly.</td>
</tr>
<tr>
<td><a href="http://www.thinkandbuild.it">Think and Build</a> </td>
<td> Some tutorials about Core Graphic and Core Animation.</td>
</tr>
<tr>
<td><a href="http://www.raywenderlich.com/tutorials">Tutorials</a> </td>
<td> 大把的 Objective-C, Swift, iOS 教程, 且全部免费, Raywenderlich 真是业界良心, 赞!</td>
</tr>
</tbody>
</table>
<h2>社区</h2>
<table>
<thead>
<tr>
<th>社区 </th>
<th> 简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://www.cocoachina.com">CocoaChina</a> </td>
<td> 全球最大苹果开发者中文社区</td>
</tr>
<tr>
<td><a href="http://code4app.com">code4app</a> </td>
<td> 经常更新一些很不错的 iOS 代码片段和一些 iOS 资源</td>
</tr>
<tr>
<td><a href="http://www.objc.io">objc</a> </td>
<td> 定期发布一些有关 Objective-C 的高质量的文章</td>
</tr>
<tr>
<td><a href="http://objccn.io">objc中国</a> </td>
<td> 喵神组织的对 objc.io 的翻译网站, 旨在推进国内技术圈整体水平, 翻译质量非常高</td>
</tr>
<tr>
<td><a href="http://www.devdiv.com">DevDiv</a> </td>
<td> 发布一些 iOS 的最新资讯及教程</td>
</tr>
<tr>
<td><a href="http://discuss.cocos2d-x.org">Cocos2d-x</a> </td>
<td> Cocos2d-x 论坛</td>
</tr>
<tr>
<td><a href="http://iphonedevsdk.com">iPhone Dev SDK</a> </td>
<td> 国外较有名的 iOS 开发者论坛</td>
</tr>
<tr>
<td><a href="http://forum.learncocoa.org">Learn Cocoa and iOS Development Forum</a> </td>
<td> <a href="http://www.amazon.com/Learn-Cocoa-Mac-Jack-Nutting/dp/1430245425">Learn Cocoa on the Mac</a> 和 <a href="http://www.amazon.com/Beginning-iOS-Development-Exploring-SDK/dp/143026022X">Beginning iOS 7 Development</a> 这两本书籍的官方论坛, 用户活跃度较高</td>
</tr>
<tr>
<td><a href="http://devforums.apple.com">Apple Developer Forums</a> </td>
<td> 苹果官方的开发者论坛</td>
</tr>
<tr>
<td><a href="http://www.swiftist.org">Swiftist</a> </td>
<td> Swift 中文社区</td>
</tr>
</tbody>
</table>
<h2>工具/插件</h2>
<table>
<thead>
<tr>
<th>工具/插件 </th>
<th> 简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://cocoapods.org">CocoaPods</a> </td>
<td> 开发 OS X 和 iOS 应用程序的一个第三方库的依赖管理工具, 本身是 Ruby 的一个 Gem, 极大的简化了 Objective-C 的开发流程</td>
</tr>
<tr>
<td><a href="http://alcatraz.io">Alcatraz</a> </td>
<td> Alcatraz 是一款管理 Xcode 插件、模版以及颜色配置的工具</td>
</tr>
<tr>
<td><a href="https://github.com/robbiehanson/XcodeColors">XcodeColors</a> </td>
<td> 使 Xcode 调试控制台色彩更丰富</td>
</tr>
<tr>
<td><a href="https://github.com/facebook/xctool">xctool</a> </td>
<td> Facebook 开源的一个 iOS 编译和测试的工具</td>
</tr>
<tr>
<td><a href="https://github.com/trawor/XToDo">XToDo</a> </td>
<td> 一款注释辅助插件,主要用于收集并列出项目中的<code>TODO</code>, <code>FIXME</code>, <code>???</code>, <code>!!!</code></td>
</tr>
<tr>
<td><a href="https://github.com/ksuther/KSImageNamed-Xcode">KSImageNamed-Xcode</a> </td>
<td> 自动补全图片命名的一款插件</td>
</tr>
<tr>
<td><a href="https://github.com/onevcat/VVDocumenter-Xcode">VVDocumenter</a> </td>
<td> 一个自动生成代码注释的工具</td>
</tr>
<tr>
<td><a href="https://imageoptim.com">ImageOptim</a> </td>
<td> 用于压缩图片一款工具</td>
</tr>
<tr>
<td><a href="https://github.com/KrauseFx/fastlane">fastlane</a> </td>
<td> 开发流程工具,将开发过程流程化,极大提高开发效率</td>
</tr>
<tr>
<td><a href="http://benscheirman.com/2013/08/the-ios-developers-toolbelt">iOS 必备的 75 个工具</a> </td>
<td> 其中包含了非常多好用的工具, 涉及到设计, 分析, 部署等, 总结的十分详细, 有<a href="http://blog.jobbole.com/46799">中文翻译</a></td>
</tr>
<tr>
<td><a href="http://blog.devtang.com/blog/2014/06/29/ios-dev-tools">更多</a> </td>
<td> 唐巧总结的一些图形应用工具, 命令行工具, Xcode 插件, 并介绍了一点基础的用法</td>
</tr>
</tbody>
</table>
<h2>GitHub Top 50 简介</h2>
<p>主要对当前 GitHub 排名前 50 的项目做一个简单的简介, 方便初学者快速了解到当前 Objective-C 在 GitHub 的情况.</p>
<table>
<thead>
<tr>
<th>项目名称 </th>
<th> 项目信息</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. <a href="https://github.com/AFNetworking/AFNetworking">AFNetworking</a> </td>
<td> 作者是 NSHipster 的博主, iOS 开发界的大神级人物, 毕业于卡内基·梅隆大学, 开源了许多牛逼的项目, 这个便是其中之一, AFNetworking 采用 NSURLConnection + NSOperation, 主要方便与服务端 API 进行数据交换, 操作简单, 功能强大, 现在许多人都用它取代 ASIHTTPRequest</td>
</tr>
<tr>
<td>2. <a href="https://github.com/BradLarson/GPUImage">GPUImage</a> </td>
<td> 一款强大的图片滤镜工具, 支持自定义滤镜, 可用来实时处理图片和视频流, 作者是 SonoPlot 公司的 CTO, 在很小的时候便开始接触编程, 他在 <a href="http://stackoverflow.com/users/19679/brad-larson">SO</a> 上面的回答也有很多值得阅读, GPUImage 这个项目从 2012 年开始, 使用 OpenGL 图形程序接口编写, 性能非常好, 现在很多 iOS 程序员都用它来实现 iOS 的模糊效果</td>
</tr>
<tr>
<td>3. <a href="https://github.com/rs/SDWebImage">SDWebImage</a> </td>
<td> 作者 Olivier Poitrey 是 Dailymotion 的 CTO, 拥有多个不错的开源项目, 此项目常用于对从 Web 端接受到的图片进行缓存, 是 UIImageView 的扩展, 应用起来比较简单</td>
</tr>
<tr>
<td>4. <a href="https://github.com/RestKit/RestKit">RestKit</a> </td>
<td> 主要用于 iOS 上网络通信, 允许与 RESTful Web 服务交互, 常用于处理 API, 解析 JSON, 映射响应对象等操作, 简单易用, 方便你把所有精力都放在对数据的操作上</td>
</tr>
<tr>
<td>5. <a href="https://github.com/ReactiveCocoa/ReactiveCocoa">ReactiveCocoa</a> </td>
<td> 由 GitHub 工程师们开发的一个应用于 iOS 和 OS X 开发的函数响应式编程新框架, Matt 称其为 "An open source project that exemplifies this brave new era for Objective-C", 也有人说它是 Cocoa 的未来, 具体可看唐巧写的这篇<a href="http://www.devtang.com/blog/2014/02/11/reactivecocoa-introduction">文章</a></td>
</tr>
<tr>
<td>6. <a href="https://github.com/facebookarchive/three20">three20</a> </td>
<td> 由 Facebook iOS 客户端衍生出的一款 iPhone 框架, 内置许多丰富的功能, 有丰富的界面, 对底层的操作便捷, 为开发者省下了很多时间, 但现在已经停止了更新, 一个 <a href="https://github.com/facebookarchive/three20/pull/832?utm_source=iOS+Dev+Weekly&utm_campaign=46a7deb647-iOS_Dev_Weekly_Issue_100&utm_medium=email&utm_term=0_7bda94b7ca-46a7deb647-299428269">PR</a> 把代码删得干干净净, 不要好奇去点开 Files changed, 我点开后该页面直接卡死, three20 当中的一位作者创建了 <a href="https://github.com/jverkoey/nimbus">Nimbus</a>, 算是 three20 的一个替代品</td>
</tr>
<tr>
<td>7. <a href="https://github.com/jdg/MBProgressHUD">MBProgressHUD</a> </td>
<td> 作者 Matej Bukovinski 是一位全栈工程师, UI/UX 设计师, 此项目是一款提示框第三方库, 帮助开发者快速应用到项目中)</td>
</tr>
<tr>
<td>8. <a href="https://github.com/magicalpanda/MagicalRecord">MagicalRecord</a> </td>
<td> 作者是 Coursera 的 iOS 工程师, 该项目创作灵感来自于 Ruby on Rails 的 Active Record, 主要为方便操作 CoreData 而生, 帮助清除 CoreData 引用的代码, 协助方便 CoreData 的工作</td>
</tr>
<tr>
<td>9. <a href="https://github.com/ccgus/fmdb">FMDB</a> </td>
<td> 一个对 SQLite 进行封装的库, 使用起来方便, 简单</td>
</tr>
<tr>
<td>10. <a href="https://github.com/Mantle/Mantle">Mantle</a> </td>
<td> 作者是 GitHub 的员工, 文档写的很清楚: Mantle makes it easy to write a simple model layer for your Cocoa or Cocoa Touch application, 主要用来将 JSON 数据模型化为 Model 对象, 唱吧在前段时间也改用 Mantle 了.</td>
</tr>
<tr>
<td>11. <a href="https://github.com/Grouper/FlatUIKit">FlatUIKit</a> </td>
<td> 收集了很多扁平化 UI 的 iOS 组件, 方便使用</td>
</tr>
<tr>
<td>12. <a href="https://github.com/pokeb/asi-http-request">ASIHTTPRequest</a> </td>
<td> 一个轻量级的 iOS 网络通信类库, 基于 CFNetwork 框架开发, 但现在已经停止更新, 多数开发者改用 AFNetworking 替代)</td>
</tr>
<tr>
<td>13. <a href="https://github.com/path/FastImageCache">FastImageCache</a> </td>
<td> Path 公司出品的 iOS 库, 作者 Mallory Paine 是苹果前员工, 此类库适用于在滚动时快速显示图像, 高速持久是其最大的特点</td>
</tr>
<tr>
<td>14. <a href="https://github.com/Masonry/Masonry">Masonry</a> </td>
<td> 一个轻量级的布局框架, 同时支持 iOS 和 Mac OS X, 语法优雅, 帮助开发者快速适配不同分辨率的 iOS 设备</td>
</tr>
<tr>
<td>15. <a href="https://github.com/TransitApp/SVProgressHUD">SVProgressHUD</a> </td>
<td> 又一款轻量级的 iOS 第三方控件, 用于显示任务加载时的动画, 非常轻便, 容易使用</td>
</tr>
<tr>
<td>16. <a href="https://github.com/facebook/Shimmer">Shimmer</a> </td>
<td> Facebook 推出的一款具有闪烁效果的第三方控件, 供它旗下一款名为 Paper 的应用使用, 安装使用整个过程都十分简单</td>
</tr>
<tr>
<td>17. <a href="https://github.com/johnezang/JSONKit">JSONKit</a> </td>
<td> 主要用于解析 JSON, 适用于 iOS6 以下环境, 自从 iOS5 开始 Apple 官方给出了 NSJSONSerialization API, 自此大家都用官方的了</td>
</tr>
<tr>
<td>18. <a href="https://github.com/jverkoey/nimbus">Nimbus</a> </td>
<td> 作者 Jeff 曾为 Facebook, Google 做过不少好东西, 也是 three20 的成员之一, three20 停更后, 他创造出这个框架来代替 three20, 文档齐全</td>
</tr>
<tr>
<td>19. <a href="https://github.com/jigish/slate">Slate</a> </td>
<td> 一款窗口管理应用程序, 但在两年前就已经停止更新了</td>
</tr>
<tr>
<td>20. <a href="https://github.com/facebook/facebook-ios-sdk">Facebook SDK for iOS</a> </td>
<td> Facebook 官方的 iOS SDK, 方便开发者集成 Facebook 的一些功能到自己的 iOS APP 里面</td>
</tr>
<tr>
<td>21. <a href="https://github.com/CocoaLumberjack/CocoaLumberjack">CocoaLumberjack</a> </td>
<td> 这是 Mac 和 iOS 的一款强大的日志框架, 配置简单, 多线程, 提供更高级的 log 功能, 可用于代替默认的 NSLog 语句</td>
</tr>
<tr>
<td>22. <a href="https://github.com/facebook/AsyncDisplayKit">AsyncDisplayKit</a> </td>
<td> Facebook 开源的一款 iOS UI 框架, Paper 用的就是该框架, 另外框架还用到了 Facebook 早期开源 Pop 动画引擎</td>
</tr>
<tr>
<td>23. <a href="https://github.com/supermarin/Alcatraz">Alcatraz</a> </td>
<td> Alcatraz 是一款管理 Xcode 插件、模版以及颜色配置的工具, 可以集成到 Xcode 的图形界面中, 安装删除都是几条命令的事, 很方便, 支持自己开发插件并上传</td>
</tr>
<tr>
<td>24. <a href="https://github.com/Inferis/ViewDeck">ViewDeck</a> </td>
<td> 一款开源的 iOS 活动面板组件, 还原 Path 2.0 的侧滑效果, 作者因为时间关系在两年前停止对其更新</td>
</tr>
<tr>
<td>25. <a href="https://github.com/jessesquires/JSQMessagesViewController">JSQMessagesViewController</a> </td>
<td> 优雅的 iOS 消息类库, 常用于聊天应用中, 可定制性高</td>
</tr>
<tr>
<td>26. <a href="https://github.com/Flipboard/FLEX">FLEX</a> </td>
<td> 这是 Flipboard 官方发布的一组专门用于 iOS 开发的应用内调试工具, 开发者无需将其连接到 LLDB/Xcode 或其他远程调试服务器,支持直接在 App 中运行</td>
</tr>
<tr>
<td>27. <a href="https://github.com/facebook/xctool">Xctool</a> </td>
<td> 是 Facebook 开源的一个命令行工具,用来替代苹果的 XcodeBuild 工具, 极大的方便了 iOS 的构建和测试, 输出错误信息也比较友好, 受到许多 iOS 开发者的称赞, 经常与其搭配使用的还有 OCUnit, <a href="https://travis-ci.org">Travis CI</a>, <a href="http://oclint.org">OCLint</a> 等测试工具</td>
</tr>
<tr>
<td>28. <a href="https://github.com/OpenEmu/OpenEmu">OpenEmu</a> </td>
<td> 超强的游戏模拟器, 做游戏开发必备, 官网做得也很不错</td>
</tr>
<tr>
<td>29. <a href="https://github.com/nicklockwood/iCarousel">iCarousel</a> </td>
<td> 作者是英国 Charcoal Design 公司的创始人, 开源领域的贡献颇为卓著, 这个项目就是其中之一, 这是一款可以在 iOS 上实现旋转木马视图切换效果的第三方控件, 并提供多种切换效果</td>
</tr>
<tr>
<td>30. <a href="https://github.com/romaonthego/RESideMenu">RESideMenu</a> </td>
<td> 作者 Roman Efimov 是雅虎的 iOS 工程师, 这个项目实现了 iOS 上的菜单侧滑效果, 创意来源于 Dribbble, 该项目支持 iOS8</td>
</tr>
<tr>
<td>31. <a href="https://github.com/square/PonyDebugger">PonyDebugger</a> </td>
<td> 由 Square 公司推出的一款优秀的 iOS 应用网络调试工具, 用户可以实时看到应用程序的网络请求, 也可以对 iOS 应用程序的核心数据栈进行远程调试</td>
</tr>
<tr>
<td>32. <a href="https://github.com/kevinzhow/PNChart">PNChart</a> </td>
<td> 作者周楷雯是 90 后, 秒视的创始人, 该项目是一个带动画效果的图表控件, 简约易用, 受到不少开发者喜爱</td>
</tr>
<tr>
<td>33. <a href="https://github.com/jverdi/JVFloatLabeledTextField">JVFloatLabeledTextField</a> </td>
<td> 作者是 Thumb Labs 的联合创始人, JVFloatLabeledTextField 是 UITextField 的子类, 主要实现输入框标签浮动效果, 创作灵感来自 Dribbble, 已出现多个移植版本</td>
</tr>
<tr>
<td>34. <a href="https://github.com/CEWendel/SWTableViewCell">SWTableViewCell</a> </td>
<td> UITableViewCell 的子类, 实现了左右滑动显示信息视图并调出按钮</td>
</tr>
<tr>
<td>35. <a href="https://github.com/levey/AwesomeMenu">AwesomeMenu</a> </td>
<td> 作者是一位中国人, 该项目主要是使用 CoreAnimation 还原了 Path menu 的动画效果</td>
</tr>
<tr>
<td>36. <a href="https://github.com/tonymillion/Reachability">Reachability</a> </td>
<td> Reachablity 是用于检测 iOS 设备网络环境的库</td>
</tr>
<tr>
<td>37. <a href="https://github.com/samuelclay/NewsBlur">NewsBlur</a> </td>
<td> 作者独自一个人 Samuel Clay 做出来的一款名为 NewsBlur 的新闻阅读器, 很多人都称其为 Google Reader 的替代品, 这是它的源码</td>
</tr>
<tr>
<td>38. <a href="https://github.com/google/physical-web">The Physical Web</a> </td>
<td> 由 Chrome 团队主导的一个项目, 意在用 URL 连接世界, 方便用户接受数据, 目前尚处在实验阶段</td>
</tr>
<tr>
<td>39. <a href="https://github.com/onevcat/VVDocumenter-Xcode">VVDocumenter-Xcode</a> </td>
<td> 作者是王巍国内著名的 iOS 开发者, 人称喵神, 目前在日本 LINE 公司工作, 该项目帮助开发者轻松的生成注释文档, 节省了不少工作量, 赞</td>
</tr>
<tr>
<td>40. <a href="https://github.com/cocos2d/cocos2d-spritebuilder">Cocos2D-SpriteBuilder</a> </td>
<td> 一个可用于在 iOS, Mac 和 Android 上制作 2D 游戏或其它图形/交互应用的框架, 之前的项目名称为 Cocos Swift, 目前该项目在 GitHub 上更新较为频繁</td>
</tr>
<tr>
<td>41. <a href="https://github.com/TTTAttributedLabel/TTTAttributedLabel">TTTAttributedLabel</a> </td>
<td> UILabel 的替代品, 使 iOS 上的 Label 功能更加丰富, 可支持链接植入等功能</td>
</tr>
<tr>
<td>42. <a href="https://github.com/robbiehanson/CocoaAsyncSocket">CocoaAsyncSocket</a> </td>
<td> 一个功能强大、简单易用的异步 socket 通讯类库, 支持 TCP 和 UDP 协议, 可用于 Mac 和 iOS 设备上, 作者 Robbie Hanson 是 Deusty 的首席软件工程师</td>
</tr>
<tr>
<td>43. <a href="https://github.com/devinross/tapkulibrary">TapkuLibrary</a> </td>
<td> 作者是 Devin Ross, 这是在 iOS 上一款功能强大的 UI 效果类库, 可以实现多种酷炫的效果, 目前仍在更新中</td>
</tr>
<tr>
<td>44. <a href="https://github.com/CanvasPod/Canvas">Canvas</a> </td>
<td> 无需编码实现牛逼的动画效果的库, 连设计师都可以快速上手</td>
</tr>
<tr>
<td>45. <a href="https://github.com/square/SocketRocket">SocketRocket</a> </td>
<td> Square 公司开源的一个 WebSocket 客户端, 稳定并且易用, 做实时应用常会用到, 受广大开发者喜爱</td>
</tr>
<tr>
<td>46. <a href="https://github.com/ECSlidingViewController/ECSlidingViewController">ECSlidingViewController</a> </td>
<td> 一个视图控制器容器, 将子视图处理成两层, 通过滑动来处理层的切换, 创作灵感来自 Facebook 和 Path的 App, 作者是 Cleveland 的员工</td>
</tr>
<tr>
<td>47. <a href="https://github.com/stig/json-framework">Json Framework</a> </td>
<td> 用于解析 JSON 数据的一个框架, 但是在 iOS5 以上版本大多数人都选择使用 NSJSONSerialization 来解析 JSON, 该项目现在在 GitHub 上也几乎没怎么更新了</td>
</tr>
<tr>
<td>48. <a href="https://github.com/facebook/Tweaks">Tweaks</a> </td>
<td> Facebook 开源的一款工具, 旨在帮助 iOS 开发者更快的迭代应用, 方便用户动态的调整参数, 是的, Paper 这个项目也用到了</td>
</tr>
<tr>
<td>49. <a href="https://github.com/realm/realm-cocoa">realm-cocoa</a> </td>
<td> Realm-Cocoa 是 Realm 公司推出一款移动端数据库, 可以运行在手机、平板和可穿戴设备之上, 其目标是取代 CoreData 和 SQLite 数据库</td>
</tr>
<tr>
<td>50. <a href="https://github.com/zwaldowski/BlocksKit">BlocksKit</a> </td>
<td> 一个开源的与 Cocoa 紧密集合的基础性框架</td>
</tr>
</tbody>
</table>
<h2>指南/教程</h2>
<table>
<thead>
<tr>
<th>网址 </th>
<th> 简介</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://developer.apple.com/app-store/review/guidelines">App Store Review Guidelines</a> </td>
<td> iOS 应用商店审核指南, 有<a href="http://www.cocoachina.com/ios/20140227/7892.html">中文翻译版</a></td>
</tr>
<tr>
<td><a href="http://dev.swiftguide.cn">Swift 语言指南</a> </td>
<td> 有很多丰富的 Swift 学习资料, 学习 Swift 有这份资料可以省下很多力气</td>
</tr>
<tr>
<td><a href="http://ourcoders.com/thread/show/117">苹果 Xcode 帮助文档阅读指南</a> </td>
<td> Tinyfool 推出的一篇对于帮助新手阅读官方文档的指南</td>
</tr>
<tr>
<td><a href="https://developer.apple.com/programs/ios/gettingstarted">Get started with your iOS developer pragram</a> </td>
<td> 苹果写的一篇入门指南, 粗略讲解了 iOS 程序从开发到上架的整个流程</td>
</tr>
<tr>
<td><a href="http://blog.teamtreehouse.com/the-beginners-guide-to-objective-c-language-and-variables">Teamtreehouse</a> </td>
<td> 文章主要讲解 Objective-C 的一些语法, 文章内容有趣且通俗易懂</td>
</tr>
<tr>
<td><a href="http://www.appdeveloperatlas.com">A map for iOS development</a> </td>
<td> 一张 iOS 开发地图, 做得很赞, 看完对 iOS 开发流程有一定的认知</td>
</tr>
<tr>
<td><a href="https://developer.apple.com/library/ios/referencelibrary/GettingStarted/RoadMapiOS">Start Developing iOS Apps Today</a> </td>
<td> 苹果官方给出的 iOS 入门教程, 看过之后能够做一个 To-Do 小程序</td>
</tr>
<tr>
<td><a href="http://rypress.com/tutorials/objective-c">Ry’s Objective-C Tutorial</a> </td>
<td> 讲解 Objective-C 的教程, 图文并茂, 适合新手阅读</td>
</tr>
<tr>
<td><a href="https://github.com/raywenderlich/objective-c-style-guide">Objective-C Style Guide</a> </td>
<td> Ray Wenderlich 推出的 Objective-C 风格指南</td>
</tr>
<tr>
<td><a href="http://www.shinobicontrols.com/iOS8DayByDay">iOS8 Day-by-Day</a> </td>
<td> 每日一个 iOS8 的小教程, 所以的 DEMO 都可以在其 <a href="https://github.com/ShinobiControls/iOS8-day-by-day">GitHub</a> 上面的找到相关代码</td>
</tr>
</tbody>
</table>
<h2>邮件订阅</h2>
<ul>
<li><a href="http://iosdevweekly.com">iOS Dev Weekly</a> (每周一期,内容多为这一星期里值得关注的 GitHub 项目、文章、工具等)</li>
<li><a href="http://iosdesign.co">iOS Design Weekly</a> (Tips, news and inspiration delivered each week)</li>
</ul>
<h2>文档</h2>
<ul>
<li><a href="https://developer.apple.com/library/ios/navigation">iOS Developer Library</a> (iOS 开发必看, 有此文档足矣, 内容非常之详细)</li>
</ul>
<h2>Awesome 系列</h2>
<ul>
<li><a href="https://github.com/vsouza/awesome-ios">Awesome iOS</a></li>
<li><a href="https://github.com/matteocrippa/awesome-swift">Awesome-Swift(1)</a></li>
<li><a href="https://github.com/Wolg/awesome-swift">Awesome-Swift(2)</a></li>
</ul>
<h2>知乎上的讨论</h2>
<ul>
<li><a href="http://www.zhihu.com/question/20016551">如何才能系统的学习 iOS 开发,理解一些规则和深层次的机制原理?</a></li>
<li><a href="http://www.zhihu.com/question/19627420">没有 C 和 Objective-C 基础如何快速学习 iOS 开发?</a></li>
<li><a href="http://www.zhihu.com/question/20264108">iOS 开发怎么入门?</a></li>
<li><a href="http://www.zhihu.com/question/20130048">iOS 开发入门需要学习哪些知识,从一无所知到精通需要多长时间?</a></li>
<li><a href="http://www.zhihu.com/question/20919784">12 岁如何入门 iOS 编程?</a></li>
<li><a href="http://www.zhihu.com/question/22914651">GitHub 上都有哪些值得关注学习的 iOS 开源项目?</a></li>
</ul>
<h2>Quora 上的讨论</h2>
<ul>
<li><a href="http://www.quora.com/What-are-the-best-resources-to-learn-iOS-development">What are the best resources to learn iOS development?</a></li>
<li><a href="https://www.quora.com/What-are-the-best-new-resources-for-learning-iOS-development-in-2014">What are the best new resources for learning iOS development in 2014?</a></li>
</ul>
<h2>国内知名的程序员开发日报</h2>
<ul>
<li><a href="http://app.memect.com">App 开发日报</a></li>
<li><a href="http://toutiao.io">开发者头条</a></li>
<li><a href="http://weekly.manong.io">码农周刊</a></li>
</ul>
<h2>贡献者</h2>
<p>点击<a href="https://github.com/Aufree/trip-to-iOS/graphs/contributors">该链接</a>查看该项目的所有贡献者</p>
<h2>License</h2>
<p>以上内容采用 <a href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.zh">CC BY-NC-SA 3.0</a> 进行许可, 转载请注明出处, 版权归本人及所有贡献者所有</p>
CodeSign error: code signing is required for product type Application in SDK iOS 2015-04-09T00:00:00+00:00http://jiaxianhua.github.io/ios/2015/04/09/codesign-error-code-signing-is-required-for-product-type-application-in-sdk-ios<h2>CodeSign error: code signing is required for product type Application in SDK iOS X.X</h2>
<p>Target->Build Settings -> Code Signing -> Code Signing Identity -> Debug -> Any ios SDK => iPhone Developer</p>
免费的编程中文书籍索引2015-04-08T00:00:00+00:00http://jiaxianhua.github.io/book,%20it/2015/04/08/free-programming-books-zh_cn<h1>免费的编程中文书籍索引</h1>
<p><a href="https://github.com/justjavac/free-programming-books-zh_CN">https://github.com/justjavac/free-programming-books-zh_CN</a></p>
<p>免费的编程中文书籍索引,欢迎投稿。</p>
<ul>
<li>国外程序员在 <a href="http://stackoverflow.com/a/1713/343194">stackoverflow</a> 推荐的程序员必读书籍,<a href="http://justjavac.com/other/2012/05/15/qualified-programmer-should-read-what-books.html" title="一个合格的程序员应该读过哪些书">中文版</a>。</li>
<li><a href="http://stackoverflow.com/q/38210/343194">stackoverflow</a> 上的程序员应该阅读的非编程类书籍有哪些? <a href="what-non-programming-books-should-programmers-read.md">中文版</a></li>
<li><a href="https://github.com/vhf/free-programming-books">github</a> 上的一个流行的编程书籍索引 <a href="https://github.com/vhf/free-programming-books/blob/master/free-programming-books-zh.md">中文版</a></li>
</ul>
<p>感谢 <a href="https://github.com/siberiawolf">@siberiawolf</a> 使用 Bootstrap 开发了网页版,地址:http://siberiawolf.com/free_programming/index.html</p>
<h2>参与交流</h2>
<p>欢迎大家将珍藏已久的经典免费书籍共享出来,您可以:</p>
<ul>
<li>使用 <a href="https://github.com/justjavac/free-programming-books-zh_CN/issues">Issues</a> 以及 Pull Request</li>
</ul>
<p>贡献者名单: https://github.com/justjavac/free-programming-books-zh_CN/graphs/contributors</p>
<h2>目录</h2>
<ul>
<li><p><a href="#%E8%AF%AD%E8%A8%80%E6%97%A0%E5%85%B3%E7%B1%BB">语言无关类</a></p>
<ul>
<li><a href="#%E4%BC%98%E8%B4%A8%E5%8D%9A%E5%AE%A2">优质博客</a></li>
<li><a href="#%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F">操作系统</a></li>
<li><a href="#%E6%99%BA%E8%83%BD%E7%B3%BB%E7%BB%9F">智能系统</a></li>
<li><a href="#web%E6%9C%8D%E5%8A%A1%E5%99%A8">WEB服务器</a></li>
<li><a href="#%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6">版本控制</a></li>
<li><a href="#%E7%BC%96%E8%BE%91%E5%99%A8">编辑器</a></li>
<li><a href="#nosql">NoSQL</a></li>
<li><a href="#postgresql">PostgreSQL</a></li>
<li><a href="#mysql">MySQL</a></li>
<li><a href="#%E9%A1%B9%E7%9B%AE%E7%9B%B8%E5%85%B3">项目相关</a></li>
<li><a href="#%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F">设计模式</a></li>
<li><a href="#web">Web</a></li>
<li><a href="#%E5%A4%A7%E6%95%B0%E6%8D%AE">大数据</a></li>
<li><a href="#%E7%BC%96%E7%A8%8B%E8%89%BA%E6%9C%AF">编程艺术</a></li>
<li><a href="#%E5%85%B6%E5%AE%83">其它</a></li>
</ul>
</li>
<li><p><a href="#%E8%AF%AD%E8%A8%80%E7%9B%B8%E5%85%B3%E7%B1%BB">语言相关类</a></p>
<ul>
<li><a href="#awk">AWK</a></li>
<li><a href="#cc">C/C++</a></li>
<li><a href="#css">CSS/HTML</a></li>
<li><a href="#dart">Dart</a></li>
<li><a href="#fortran">Fortran</a></li>
<li><a href="#java">Java</a></li>
<li><a href="#javascript">JavaScript</a></li>
<li><a href="#php">PHP</a></li>
<li><a href="#ios">iOS</a></li>
<li><a href="#android">Android</a></li>
<li><a href="#python">Python</a></li>
<li><a href="#prolog">Prolog</a></li>
<li><a href="#ruby">Ruby</a></li>
<li><a href="#shell">Shell</a></li>
<li><a href="#go">Go</a></li>
<li><a href="#groovy">Groovy</a></li>
<li><a href="#latex">LaTeX</a></li>
<li><a href="#lisp">LISP</a></li>
<li><a href="#lua">Lua</a></li>
<li><a href="#haskell">Haskell</a></li>
<li><a href="#r">R</a></li>
<li><a href="#scala">Scala</a></li>
<li><a href="#swift">Swift</a></li>
<li><a href="#scheme">Scheme</a></li>
</ul>
</li>
<li><p><a href="#%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E5%8F%8A%E5%85%B6%E5%AE%83">读书笔记及其它</a></p></li>
<li><a href="#%E6%B5%8B%E8%AF%95%E7%9B%B8%E5%85%B3">测试相关</a></li>
</ul>
<h2>语言无关类</h2>
<h3>优质博客</h3>
<ul>
<li><a href="http://docs.pythontab.com/">PyTab在线手册中心</a></li>
<li><a href="http://www.importnew.com/">ImportNew</a></li>
<li><a href="http://www.liaoxuefeng.com/">廖雪峰的官方网站</a></li>
<li><a href="http://blogwall.us/zh">程序员博客墙</a></li>
</ul>
<h3>操作系统</h3>
<ul>
<li><a href="http://i.linuxtoy.org/docs/guide/index.html">开源世界旅行手册</a></li>
<li><a href="http://linux.vbird.org/">鸟哥的Linux私房菜</a></li>
<li><a href="http://sourceforge.net/apps/trac/elpi/wiki/ALP">Linux 系统高级编程</a></li>
<li><a href="http://billie66.github.io/TLCL/index.html">The Linux Command Line</a> (中英文版)</li>
<li><a href="http://oss.org.cn/kernel-book/ldd3/index.html">Linux 设备驱动</a> (第三版)</li>
<li><a href="http://www.kerneltravel.net/kernel-book/%E6%B7%B1%E5%85%A5%E5%88%86%E6%9E%90Linux%E5%86%85%E6%A0%B8%E6%BA%90%E7%A0%81.html">深入分析Linux内核源码</a></li>
<li><a href="http://cb.vu/unixtoolbox_zh_CN.xhtml">UNIX TOOLBOX</a></li>
<li><a href="https://github.com/widuu/chinese_docker">Docker中文指南</a></li>
<li><a href="https://github.com/yeasy/docker_practice">Docker —— 从入门到实践</a></li>
<li><a href="http://freeradius.akagi201.org">FreeRADIUS新手入门</a></li>
<li><a href="http://aaaaaashu.gitbooks.io/mac-dev-setup/content/">Mac 开发配置手册</a></li>
<li><a href="https://www.freebsd.org/doc/zh_CN/books/handbook/index.html">FreeBSD 使用手册</a></li>
<li><a href="http://billie66.github.io/TLCL/book/">Linux 命令行(中文版)</a></li>
<li><a href="http://works.jinbuguo.com/lfs/lfs62/index.html">Linux 构建指南</a></li>
<li><a href="https://github.com/me115/linuxtools_rst">Linux工具快速教程</a></li>
<li><a href="https://github.com/tobegit3hub/understand_linux_process">理解Linux进程</a></li>
<li><a href="https://github.com/ryanzz/LFS-systemd-zh_CN">Linux From Scratch systemd 中文翻译</a></li>
</ul>
<h4>智能系统</h4>
<ul>
<li><a href="https://github.com/gmszone/designiot">一步步搭建物联网系统</a></li>
</ul>
<h3>WEB服务器</h3>
<ul>
<li><a href="http://tengine.taobao.org/book/index.html">Nginx开发从入门到精通</a> (淘宝团队出品)</li>
<li><a href="http://www.ttlsa.com/nginx/nginx-stu-pdf/">Nginx教程从入门到精通</a>(PDF版本,运维生存时间出品)</li>
<li><a href="http://works.jinbuguo.com/apache/menu22/index.html">Apache 中文手册</a></li>
<li><a href="http://looly.gitbooks.io/elasticsearch-the-definitive-guide-cn/">Elasticsearch权威指南</a></li>
</ul>
<h3>版本控制</h3>
<ul>
<li><a href="http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000">Git教程</a> (本文由 <a href="http://weibo.com/liaoxuefeng">@廖雪峰</a> 创作,如果觉得本教程对您有帮助,可以去 <a href="https://itunes.apple.com/cn/app/git-jiao-cheng/id876420437">iTunes</a> 购买)</li>
<li><a href="http://rogerdudler.github.io/git-guide/index.zh.html">git - 简易指南</a></li>
<li><a href="http://backlogtool.com/git-guide/cn/">猴子都能懂的GIT入门</a></li>
<li><a href="http://gitref.justjavac.com">Git 参考手册</a></li>
<li><a href="http://git-scm.com/book/zh">Pro Git</a></li>
<li><a href="http://www-cs-students.stanford.edu/~blynn/gitmagic/intl/zh_cn/">Git Magic</a></li>
<li><a href="http://www.worldhello.net/gotgithub/index.html">GotGitHub</a></li>
<li><a href="http://gitbook.liuhui998.com/index.html">Git Community Book 中文版</a></li>
<li><a href="http://mercurial.selenic.com/wiki/ChineseTutorial">Mercurial 使用教程</a></li>
<li><a href="http://bucunzai.net/hginit/">HgInit (中文版)</a></li>
<li><a href="http://igit.linuxtoy.org/">沉浸式学 Git</a></li>
<li><a href="https://github.com/flyhigher139/Git-Cheat-Sheet">Git-Cheat-Sheet</a> (感谢 @flyhigher139 翻译了中文版)</li>
<li><a href="http://snowdream86.gitbooks.io/github-cheat-sheet/content/zh/index.html">GitHub秘籍</a></li>
<li><a href="https://github.com/waylau/github-help">Github帮助文档</a></li>
<li><a href="http://danielkummer.github.io/git-flow-cheatsheet/index.zh_CN.html">git-flow 备忘清单</a></li>
</ul>
<h3>编辑器</h3>
<ul>
<li><a href="http://exvim.github.io/docs-zh/intro/">exvim--vim 改良成IDE项目</a></li>
<li><a href="http://learnvimscriptthehardway.onefloweroneworld.com/">笨方法学Vimscript 中译本</a></li>
<li><a href="https://github.com/vimcn/vimcdoc">Vim中文文档</a></li>
<li><a href="https://github.com/yangyangwithgnu/use_vim_as_ide">所需即所获:像 IDE 一样使用 vim</a></li>
</ul>
<h3>NoSQL</h3>
<ul>
<li><a href="http://www.yankay.com/wp-content/NoSql_Database_Note.html">NoSQL数据库笔谈</a> (<a href="http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/NoSQL%E6%95%B0%E6%8D%AE%E5%BA%93%E7%AC%94%E8%B0%88v2.pdf">PDF</a>)</li>
<li><a href="http://redisbook.com/">Redis 设计与实现</a></li>
<li><a href="http://www.redisdoc.com/">Redis 命令参考</a></li>
<li><a href="https://github.com/huangz1990/redis-3.0-annotated">带有详细注释的 Redis 3.0 代码</a></li>
<li><a href="https://github.com/huangz1990/annotated_redis_source">带有详细注释的 Redis 2.6 代码</a></li>
<li><a href="https://github.com/justinyhuang/the-little-mongodb-book-cn/blob/master/mongodb.md">The Little MongoDB Book</a></li>
<li><a href="https://github.com/JasonLai256/the-little-redis-book/blob/master/cn/redis.md">The Little Redis Book</a></li>
<li><a href="http://docs.neo4j.org.cn/">Neo4j 简体中文手册 v1.8</a></li>
<li><a href="http://neo4j.tw/">Neo4j .rb 中文資源</a></li>
</ul>
<h3>PostgreSQL</h3>
<ul>
<li><a href="http://works.jinbuguo.com/postgresql/menu823/index.html">PostgreSQL 8.2.3 中文文档</a></li>
</ul>
<h3>MySQL</h3>
<ul>
<li><a href="http://www.cnblogs.com/leoo2sk/archive/2011/07/10/mysql-index.html">MySQL索引背后的数据结构及算法原理</a></li>
<li><a href="http://www.cnblogs.com/mr-wid/archive/2013/05/09/3068229.html">21分钟MySQL入门教程</a></li>
</ul>
<h3>项目相关</h3>
<ul>
<li><a href="http://article.yeeyan.org/view/2251/94882">持续集成(第二版)</a> (译言网)</li>
<li><a href="http://www.ibm.com/developerworks/cn/java/j-ap/">让开发自动化系列专栏</a></li>
<li><a href="http://www.ibm.com/developerworks/cn/java/j-cq/">追求代码质量</a></li>
<li><a href="https://github.com/fool2fish/selenium-doc">selenium 中文文档</a></li>
<li><a href="http://local.joelonsoftware.com/wiki/Chinese_(Simplified)">Joel谈软件</a></li>
<li><a href="http://local.joelonsoftware.com/wiki/%E9%A6%96%E9%A0%81">約耳談軟體(Joel on Software)</a></li>
<li><a href="https://github.com/waylau/Gradle-2-User-Guide">Gradle 2 用户指南</a></li>
<li><a href="https://github.com/ecomfe/spec">编码规范</a></li>
<li><a href="http://www.ituring.com.cn/book/1143">开源软件架构</a></li>
</ul>
<h3>设计模式</h3>
<ul>
<li><a href="https://github.com/me115/design_patterns">图说设计模式</a></li>
<li><a href="http://blog.csdn.net/lovelion/article/details/17517213">史上最全设计模式导学目录</a></li>
</ul>
<h3>Web</h3>
<ul>
<li><a href="http://www.20thingsilearned.com/zh-CN/home">关于浏览器和网络的 20 项须知</a></li>
<li><a href="http://knowledge.ecomfe.com/">前端知识体系</a></li>
<li><a href="http://jinlong.github.io/2013/08/29/devtoolsecrets/">浏览器开发工具的秘密</a></li>
<li><a href="https://github.com/CN-Chrome-DevTools/CN-Chrome-DevTools">Chrome 开发者工具中文手册</a></li>
<li><a href="http://open.chrome.360.cn/extension_dev/overview.html">Chrome扩展开发文档</a></li>
<li><a href="http://www.gruntjs.org/">Grunt中文文档</a></li>
<li><a href="http://yeomanjs.org/">Yeoman中文文档</a></li>
<li><a href="https://github.com/AlloyTeam/Mars">移动Web前端知识库</a></li>
<li><a href="http://deerchao.net/tutorials/regex/regex.htm">正则表达式30分钟入门教程</a></li>
<li><a href="https://github.com/fouber/blog/issues/2">前端开发体系建设日记</a></li>
<li><a href="https://github.com/hoosin/mobile-web-favorites">移动前端开发收藏夹</a></li>
<li><a href="https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md">JSON风格指南</a></li>
<li><a href="https://github.com/bolasblack/http-api-guide">HTTP 接口设计指北</a></li>
<li><a href="https://github.com/hacke2/hacke2.github.io/issues/1">前端资源分享(一)</a></li>
<li><a href="https://github.com/hacke2/hacke2.github.io/issues/3">前端资源分享(二)</a></li>
<li><a href="http://coderlmn.github.io/code-standards/">前端代码规范 及 最佳实践</a></li>
<li><a href="http://www.flygon.net/archives/427">w3school教程整理</a></li>
<li><a href="http://man.lupaworld.com/content/network/wireshark/index.html">Wireshark用户手册</a></li>
<li><a href="http://happypeter.github.io/tealeaf-http/">HTTP 下午茶</a></li>
<li><a href="http://yuedu.baidu.com/ebook/478d1a62376baf1ffc4fad99?pn=1">HTTP/2.0 中文翻译</a></li>
</ul>
<h3>大数据</h3>
<ul>
<li><a href="https://github.com/Flowerowl/Big-Data-Resources">大数据/数据挖掘/推荐系统/机器学习相关资源</a></li>
<li><a href="https://github.com/jizhang/guidetodatamining">面向程序员的数据挖掘指南</a></li>
<li><a href="https://code.csdn.net/CODE_Translation/spark_matei_phd">大型集群上的快速和通用数据处理架构</a></li>
<li><a href="https://github.com/linyiqun/DataMiningAlgorithm">数据挖掘中经典的算法实现和详细的注释</a></li>
</ul>
<h2>编程艺术</h2>
<ul>
<li><a href="https://github.com/julycoding/The-Art-Of-Programming-by-July">程序员编程艺术</a></li>
<li><a href="http://www.oschina.net/translate/what-every-programmer-should-know-about-memory-part1?print">每个程序员都应该了解的内存知识(译)</a>【第一部分】</li>
<li><a href="http://read.douban.com/ebook/4972883/">取悦的工序:如何理解游戏</a> (豆瓣阅读,免费书籍)</li>
<li><a href="http://xiaobeicn.gitbooks.io/programming-skills-summary/">编程技巧总汇</a></li>
</ul>
<h2>其它</h2>
<ul>
<li><a href="http://softwaredownload.gitbooks.io/openwrt-fanqiang/">OpenWrt智能、自动、透明翻墙路由器教程</a></li>
<li><a href="https://community.emc.com/docs/DOC-16067">SAN 管理入门系列</a></li>
<li><a href="http://sketchcn.com/sketch-chinese-user-manual.html#introduce">Sketch 中文手册</a></li>
</ul>
<h2>语言相关类</h2>
<h3>AWK</h3>
<ul>
<li><a href="http://awk.readthedocs.org/en/latest/">awk程序设计语言</a></li>
</ul>
<h3>C/C++</h3>
<ul>
<li><a href="https://github.com/forhappy/A-Detailed-Cplusplus-Concurrency-Tutorial">C++ 并发编程指南</a> (<a href="http://weibo.com/1702076100">@傅海平ICT</a>)</li>
<li><a href="http://akaedu.github.io/book/">Linux C编程一站式学习</a> (宋劲杉, 北京亚嵌教育研究中心)</li>
<li><a href="https://github.com/leeyiw/cgdb-manual-in-chinese">CGDB中文手册</a></li>
<li><a href="https://github.com/hellogcc/100-gdb-tips/blob/master/src/index.md">100个gdb小技巧</a></li>
<li><a href="https://github.com/hellogcc/100-gcc-tips/blob/master/src/index.md">100个gcc小技巧</a></li>
<li><a href="https://github.com/anjuke/zguide-cn">ZMQ 指南</a></li>
<li><a href="http://www.ituring.com.cn/book/1203">How to Think Like a Computer Scientist</a> (中英文版)</li>
<li><a href="http://scc.qibebt.cas.cn/docs/linux/base/%B8%FA%CE%D2%D2%BB%C6%F0%D0%B4Makefile-%B3%C2%F0%A9.pdf">跟我一起写Makefile(PDF)</a></li>
<li><a href="http://www.yayu.org/book/gnu_make/">GNU make中文手册</a></li>
<li><a href="http://docs.huihoo.com/gnu/linux/gmake.html">GNU make 指南</a></li>
<li><a href="http://zh-google-styleguide.readthedocs.org/en/latest/google-cpp-styleguide/contents/">Google C++ 风格指南</a></li>
<li><a href="https://github.com/andycai/cprimer">C/C++ Primer</a> (by @andycai)</li>
<li><a href="http://www.nowamagic.net/librarys/books/contents/c">简单易懂的C魔法</a></li>
<li><a href="http://sewm.pku.edu.cn/src/paradise/reference/CMake%20Practice.pdf">Cmake 实践</a> (PDF版)</li>
<li><a href="http://www.sunistudio.com/cppfaq/">C++ FAQ LITE(中文版)</a></li>
<li><a href="https://github.com/Mooophy/Cpp-Primer">C++ Primer 5th Answers</a></li>
</ul>
<h3>CSS</h3>
<ul>
<li><a href="http://zh.learnlayout.com/">学习CSS布局</a></li>
<li><a href="https://github.com/chadluo/CSS-Guidelines/blob/master/README.md">通用 CSS 笔记、建议与指导</a></li>
<li><a href="http://css.doyoe.com/">CSS参考手册</a></li>
<li><a href="http://yanxyz.github.io/emmet-docs/">Emmet 文档</a></li>
<li><a href="http://alloyteam.github.io/code-guide/">前端代码规范</a> (腾讯alloyteam团队)</li>
<li><a href="http://codeguide.bootcss.com/">HTML和CSS编码规范</a></li>
<li><a href="http://sass-guidelin.es/zh/">Sass Guidelines 中文</a></li>
</ul>
<h3>Dart</h3>
<ul>
<li><a href="http://dart.lidian.info/wiki/Language_Tour">Dart 语言导览</a></li>
</ul>
<h3>Erlang</h3>
<ul>
<li><a href="http://xn--21erlang-p00o82pmp3o.github.io/">21天学通Erlang</a></li>
</ul>
<h3>Fortran</h3>
<ul>
<li><a href="http://micro.ustc.edu.cn/Fortran/ZJDing/">Fortran77和90/95编程入门</a></li>
</ul>
<h3>Java</h3>
<ul>
<li><a href="https://github.com/waylau/apache-shiro-1.2.x-reference">Apache Shiro 用户指南</a></li>
<li><a href="https://github.com/waylau/Jersey-2.x-User-Guide">Jersey 2.x 用户指南</a></li>
<li><a href="https://github.com/waylau/spring-framework-4-reference">Spring Framework 4.x参考文档</a></li>
<li><a href="https://github.com/qibaoguang/Spring-Boot-Reference-Guide">Spring Boot参考指南</a> (翻译中)</li>
<li><a href="http://mybatis.github.io/mybatis-3/zh/index.html">MyBatis中文文档</a></li>
<li><a href="https://github.com/waylau/RestDemo">用jersey构建REST服务</a></li>
<li><a href="https://github.com/waylau/activiti-5.x-user-guide">Activiti 5.x 用户指南</a></li>
<li><a href="http://www.hawstein.com/posts/google-java-style.html">Google Java编程风格指南</a></li>
<li><a href="https://github.com/waylau/netty-4-user-guide">Netty 4.x 用户指南</a></li>
<li><a href="https://github.com/waylau/essential-netty-in-action">Netty 实战(精髓)</a></li>
<li><a href="https://github.com/waylau/rest-in-action">REST 实战</a></li>
<li><a href="https://github.com/waylau/java-code-conventions">Java 编码规范</a></li>
<li><a href="https://github.com/waylau/apache-mina-2.x-user-guide">Apache MINA 2 用户指南</a></li>
</ul>
<h3>JavaScript</h3>
<ul>
<li><a href="http://bq69.com/blog/articles/script/868/google-javascript-style-guide.html">Google JavaScript 代码风格指南</a></li>
<li><a href="https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md">Google JSON 风格指南</a></li>
<li><a href="https://github.com/adamlu/javascript-style-guide">Airbnb JavaScript 规范</a></li>
<li><a href="http://javascript.ruanyifeng.com/">JavaScript 标准参考教程(alpha)</a></li>
<li><a href="http://pij.robinqu.me/">Javascript编程指南</a> (<a href="https://github.com/RobinQu/Programing-In-Javascript">源码</a>)</li>
<li><a href="https://github.com/justjavac/12-javascript-quirks">javascript 的 12 个怪癖</a></li>
<li><a href="http://bonsaiden.github.io/JavaScript-Garden/zh/">JavaScript 秘密花园</a></li>
<li><a href="http://icodeit.org/jsccp/">JavaScript核心概念及实践</a> (PDF) (此书已由人民邮电出版社出版发行,但作者依然免费提供PDF版本,希望开发者们去购买,支持作者)</li>
<li><a href="https://github.com/jayli/javascript-patterns">《JavaScript 模式》</a> “JavaScript patterns”中译本</li>
<li><a href="http://justjavac.com/named-function-expressions-demystified.html">命名函数表达式探秘</a> (注:原文由<a href="http://www.cn-cuckoo.com">为之漫笔</a>翻译,原始地址无法打开,所以此处地址为我博客上的备份)</li>
<li><a href="http://www.oschina.net/translate/learning-javascript-design-patterns">学用 JavaScript 设计模式</a> (开源中国)</li>
<li><a href="http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html">深入理解JavaScript系列</a></li>
<li><a href="http://es6.ruanyifeng.com/">ECMAScript 6 入门</a> (作者:阮一峰)</li>
<li>jQuery
<ul>
<li><a href="http://www.cn-cuckoo.com/deconstructed/jquery.html">jQuery 解构</a></li>
<li><a href="http://www.nowamagic.net/librarys/books/contents/jquery">简单易懂的JQuery魔法</a></li>
<li><a href="http://i5ting.github.io/How-to-write-jQuery-plugin/build/jquery.plugin.html">How to write jQuery plugin</a></li>
</ul>
</li>
<li>Node.js
<ul>
<li><a href="http://www.nodebeginner.org/index-zh-cn.html">Node入门</a></li>
<li><a href="http://nqdeng.github.io/7-days-nodejs/">七天学会NodeJS</a></li>
<li><a href="https://github.com/nodejs-tw/nodejs-wiki-book">Nodejs Wiki Book</a> (繁体中文)</li>
<li><a href="http://expressjs.jser.us/">express.js 中文文档</a></li>
<li><a href="https://github.com/turingou/koa-guide">koa 中文文档</a></li>
<li><a href="https://github.com/nswbmw/N-blog">使用 Express + MongoDB 搭建多人博客</a></li>
<li><a href="http://javascript.ruanyifeng.com/nodejs/express.html">Express框架</a></li>
<li><a href="https://www.gitbook.io/book/0532/nodejs">nodejs文档</a></li>
<li><a href="https://github.com/alsotang/node-lessons">Node.js 包教不包会</a></li>
<li><a href="https://www.npmjs.org/package/learnyounode-zh-cn">Learn You The Node.js For Much Win! (中文版)</a></li>
<li><a href="http://i5ting.github.io/node-debug-tutorial/">Node debug 三法三例</a></li>
</ul>
</li>
<li>underscore.js
<ul>
<li><a href="http://learningcn.com/underscore/">Underscore.js中文文档</a></li>
</ul>
</li>
<li>backbone.js
<ul>
<li><a href="http://www.the5fire.com/backbone-js-tutorials-pdf-download.html">backbone.js入门教程</a> (PDF)</li>
<li><a href="https://github.com/the5fire/backbonejs-learning-note">Backbone.js入门教程第二版</a></li>
<li><a href="http://feliving.github.io/developing-backbone-applications">Developing Backbone.js Applications(中文版)</a></li>
</ul>
</li>
<li>AngularJS
<ul>
<li><a href="https://github.com/mgechev/angularjs-style-guide/blob/master/README-zh-cn.md">AngularJS最佳实践和风格指南</a></li>
<li><a href="https://github.com/peiransun/angularjs-cn">AngularJS中译本</a></li>
<li><a href="https://github.com/zensh/AngularjsTutorial_cn">AngularJS入门教程</a></li>
<li><a href="https://github.com/xufei/Make-Your-Own-AngularJS/blob/master/01.md">构建自己的AngularJS</a></li>
<li><a href="http://www.waylau.com/build-angularjs-app-with-yeoman-in-windows/">在Windows环境下用Yeoman构建AngularJS项目</a></li>
</ul>
</li>
<li>Zepto.js
<ul>
<li><a href="http://mweb.baidu.com/zeptoapi/">Zepto.js 中文文档</a></li>
</ul>
</li>
<li>Sea.js
<ul>
<li><a href="http://island205.github.io/HelloSea.js/">Hello Sea.js</a></li>
</ul>
</li>
<li>React.js
<ul>
<li><a href="http://reactjs.cn/">React.js 中文文档</a></li>
</ul>
</li>
<li>CoffeeScript
<ul>
<li><a href="http://island205.github.io/coffeescript-cookbook.github.com/">CoffeeScript Cookbook</a></li>
<li><a href="http://island205.github.io/tlboc/">The Little Book on CoffeeScript中文版</a></li>
<li><a href="https://github.com/geekplux/coffeescript-style-guide">CoffeeScript 编码风格指南</a></li>
</ul>
</li>
<li>ExtJS
<ul>
<li><a href="http://extjs-doc-cn.github.io/ext4api/">Ext4.1.0 中文文档</a></li>
</ul>
</li>
<li>Meteor
<ul>
<li><a href="http://zh.discovermeteor.com/">Discover Meteor</a></li>
</ul>
</li>
<li><a href="http://www.ituring.com.cn/minibook/950">Chrome扩展及应用开发</a></li>
</ul>
<h3>PHP</h3>
<ul>
<li><a href="http://www.laruence.com/2010/06/21/1608.html">PHP调试技术手册</a>(PDF)</li>
<li><a href="http://www.blogkun.com/project.html">XDebug 2中文手册(译)</a> (CHM)</li>
<li><a href="http://wulijun.github.io/php-the-right-way/">PHP之道</a></li>
<li><a href="https://github.com/justjavac/PHP-Best-Practices-zh_CN">PHP 最佳实践</a></li>
<li><a href="http://ryancao.gitbooks.io/php-developer-prepares/content/">PHP 开发者实践</a></li>
<li><a href="https://github.com/reeze/tipi">深入理解PHP内核</a></li>
<li><a href="http://www.walu.cc/phpbook/">PHP扩展开发及内核应用</a></li>
<li><a href="http://codeigniter.org.cn/user_guide/index.html">CodeIgniter 用户指南</a></li>
<li><a href="http://www.golaravel.com/docs/">Laravel4 中文文档</a></li>
<li><a href="https://github.com/huanghua581/laravel-getting-started">Laravel 入门</a></li>
<li><a href="http://symfony-docs-chs.readthedocs.org/en/latest/">Symfony2中文文档</a> (未译完)</li>
<li><a href="http://phalcon.5iunix.net/">Phalcon中文文档</a>(翻译进行中)</li>
<li><a href="http://yiibook.com//doc">YiiBook几本Yii框架的在线教程</a></li>
<li><a href="http://www.digpage.com/">深入理解 Yii 2.0</a></li>
<li><a href="http://www.yiichina.com/">Yii 框架中文文檔</a></li>
<li><a href="http://www.nowamagic.net/librarys/books/contents/php">简单易懂的PHP魔法</a></li>
<li><a href="https://github.com/LinkedDestiny/swoole-doc">swoole文档及入门教程</a></li>
<li><a href="http://www.phpcomposer.com">Composer 中文网</a></li>
<li><a href="http://minimee.org/php/slim">Slim 中文文档</a></li>
</ul>
<h3>iOS</h3>
<ul>
<li><a href="https://github.com/qinjx/30min_guides/blob/master/ios.md">iOS开发60分钟入门</a></li>
<li><a href="http://isux.tencent.com/ios-human-interface-guidelines-ui-design-basics-ios7.html">iOS7人机界面指南</a></li>
<li><a href="http://zh-google-styleguide.readthedocs.org/en/latest/google-objc-styleguide/">Google Objective-C Style Guide 中文版</a></li>
<li><a href="http://wileam.com/iphone-6-screen-cn/">iPhone 6 屏幕揭秘</a></li>
<li><a href="http://nilsun.github.io/apple-watch/">Apple Watch开发初探</a></li>
<li><a href="https://developer.apple.com/library/ios/referencelibrary/GettingStarted/RoadMapiOSCh/index.html">马上着手开发 iOS 应用程序</a></li>
<li><a href="https://github.com/jkyin/Subtitle">网易斯坦福大学公开课:iOS 7应用开发字幕文件</a></li>
</ul>
<h3>Android</h3>
<ul>
<li><a href="http://www.apkbus.com/design/index.html">Android Design(中文版)</a></li>
<li>Google Material Design 正體中文版 (<a href="http://wcc723.gitbooks.io/google_design_translate/content/style-icons.html">译本一</a> <a href="https://github.com/1sters/material_design_zh">译本二</a>)</li>
<li><a href="http://hukai.me/android-training-course-in-chinese/index.html">Google Android官方培训课程中文版</a></li>
<li><a href="http://stormzhang.github.io/android/2014/07/07/learn-android-from-rookie/">Android学习之路</a></li>
</ul>
<h3>Python</h3>
<ul>
<li><a href="http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000">小白的Python教程</a></li>
<li><a href="http://woodpecker.org.cn/abyteofpython_cn/chinese/">简明Python教程</a></li>
<li><a href="http://looly.gitbooks.io/python-basic">零基础学Python</a></li>
<li><a href="http://lovelypython.readthedocs.org/en/latest/">可爱的 Python </a></li>
<li><a href="http://www.pythondoc.com/pythontutorial27/index.html">Python 2.7 官方教程中文版</a></li>
<li><a href="http://www.pythondoc.com/pythontutorial3/index.html">Python 3.3 官方教程中文版</a></li>
<li><a href="http://sebug.net/paper/books/dive-into-python3/">深入 Python 3</a></li>
<li><a href="https://code.google.com/p/zhong-wiki/wiki/PEP8">PEP8 Python代码风格规范</a></li>
<li><a href="http://zh-google-styleguide.readthedocs.org/en/latest/google-python-styleguide/">Google Python 风格指南 中文版</a></li>
<li><a href="http://liam0205.me/2013/11/02/Python-tutorial-zh_cn/">Python入门教程</a> (<a href="http://liam0205.me/attachment/Python/The_Python_Tutorial_zh-cn.pdf">PDF</a>)</li>
<li><a href="http://article.yeeyan.org/view/311527/287706">Python的神奇方法指南</a></li>
<li><a href="http://sebug.net/paper/books/LearnPythonTheHardWay/">笨办法学 Python</a> (<a href="http://liam0205.me/attachment/Python/PyHardWay/Learn_Python_The_Hard_Way_zh-cn.pdf">PDF</a>版下载)</li>
<li><a href="http://django-chinese-docs.readthedocs.org/en/latest/">Django 1.5 文档中文版</a> 正在翻译中</li>
<li><a href="http://django-1-7-doc.coding.io/">Diango 1.7 文档中文版</a> 正在翻译中,目前只翻译了目录</li>
<li><a href="https://github.com/brantyoung/zh-django-best-practices">Django 最佳实践</a></li>
<li><a href="http://andrew-liu.gitbooks.io/django-blog/">Django搭建简易博客教程</a></li>
<li><a href="http://djangobook.py3k.cn/2.0/">The Django Book 中文版</a></li>
<li><a href="http://webpy.org/tutorial3.zh-cn">web.py 0.3 新手指南</a></li>
<li><a href="http://webpy.org/cookbook/index.zh-cn">Web.py Cookbook 简体中文版</a></li>
<li><a href="http://woodpecker.org.cn/diveintopython/">Dive Into Python 中文版</a></li>
<li><a href="https://associates.amazon.cn/gp/associates/network/main.html">Bottle 文档中文版</a> (需翻墙)</li>
<li><a href="http://docs.jinkan.org/docs/flask/">Flask 文档中文版</a></li>
<li><a href="http://docs.jinkan.org/docs/jinja2/">Jinja2 文档中文版</a></li>
<li><a href="http://werkzeug-docs-cn.readthedocs.org/zh_CN/latest/">Werkzeug 文档中文版</a></li>
<li><a href="http://spacewander.github.io/explore-flask-zh">Flask之旅</a></li>
<li><a href="http://demo.pythoner.com/itt2zh/index.html">Introduction to Tornado 中文翻译</a></li>
<li><a href="http://pan.baidu.com/s/1qW4pvnY">Python自然语言处理中文版</a> (感谢陈涛同学的翻译,也谢谢 <a href="https://github.com/shwley">@shwley</a> 联系了作者)</li>
<li><a href="http://liam0205.me/2014/09/11/matplotlib-tutorial-zh-cn/">Python 绘图库 matplotlib 官方指南中文翻译</a></li>
<li><a href="http://scrapy-chs.readthedocs.org/zh_CN/latest/">Scrapy 0.25 文档</a></li>
<li><a href="https://github.com/carfly/thinkpython-cn">ThinkPython</a></li>
<li><a href="http://www.cnblogs.com/vamei/archive/2012/09/13/2682778.html">Python快速教程</a></li>
<li><a href="http://wiki.ubuntu.org.cn/Python%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97">Python 正则表达式操作指南</a></li>
<li><a href="http://www.crifan.com/files/doc/docbook/python_beginner_tutorial/release/html/python_beginner_tutorial.html">python初级教程:入门详解</a></li>
<li><a href="http://python3-cookbook.readthedocs.org/zh_CN/latest/">Python Cookbook 第3版 中文版</a></li>
<li><a href="http://likebeta.gitbooks.io/twisted-intro-cn/">Twisted 与异步编程入门</a></li>
<li><a href="http://textgrocery.readthedocs.org/zh/latest/index.html">TextGrocery 中文 API</a> ( 基于svm算法的一个短文本分类 Python 库 )</li>
<li><a href="http://requests-docs-cn.readthedocs.org/zh_CN/latest/">Requests: HTTP for Humans</a></li>
<li><a href="http://pillow-cn.readthedocs.org/en/latest/#">Pillow 中文文档</a></li>
</ul>
<h3>Ruby</h3>
<ul>
<li><a href="https://github.com/JuanitoFatas/ruby-style-guide/blob/master/README-zhCN.md">Ruby 风格指南</a></li>
<li><a href="https://github.com/JuanitoFatas/rails-style-guide/blob/master/README-zhCN.md">Rails 风格指南</a></li>
<li><a href="http://lrthw.github.io/">笨方法學 Ruby</a></li>
<li><a href="http://guides.ruby-china.org/">Ruby on Rails 指南</a></li>
<li><a href="http://ihower.tw/rails4/index.html">Ruby on Rails 實戰聖經</a></li>
<li><a href="http://railstutorial-china.org/">Ruby on Rails Tutorial 原书第 3 版</a> (本书网页版免费提供,电子版以 PDF、EPub 和 Mobi 格式提供购买,仅售 9.9 美元)</li>
<li><a href="http://wusuopu.gitbooks.io/write-ruby-extension-with-c/content/">编写Ruby的C拓展</a></li>
<li><a href="https://ruby-china.org/topics/22386">Ruby 源码解读</a></li>
</ul>
<h3>Shell</h3>
<ul>
<li><a href="https://github.com/qinjx/30min_guides/blob/master/shell.md">Shell脚本编程30分钟入门</a></li>
<li><a href="http://blog.sae.sina.com.cn/archives/3606">Bash脚本15分钟进阶教程</a></li>
<li><a href="https://github.com/me115/linuxtools_rst">Linux工具快速教程</a></li>
<li><a href="https://github.com/wzb56/13_questions_of_shell">shell十三问</a></li>
</ul>
<h3>Go</h3>
<ul>
<li><a href="https://github.com/Unknwon/go-fundamental-programming">Go编程基础</a></li>
<li><a href="https://github.com/Unknwon/the-way-to-go_ZH_CN">Go入门指南</a></li>
<li><a href="http://mikespook.com/learning-go/">学习Go语言</a> (<a href="http://xxiyy.qiniudn.com/%E5%AD%A6%E4%B9%A0%20Go%20%E8%AF%AD%E8%A8%80(Golang).pdf?download">PDF</a>)</li>
<li><a href="https://github.com/astaxie/build-web-application-with-golang">Go Web 编程</a> (此书已经出版,希望开发者们去购买,支持作者的创作)</li>
<li><a href="https://github.com/astaxie/Go-in-Action">Go实战开发</a> (当我收录此项目时,作者已经写完第三章,如果读完前面章节觉得有帮助,可以给作者<a href="https://me.alipay.com/astaxie">捐赠</a>,以鼓励作者的继续创作)</li>
<li><a href="https://github.com/astaxie/NPWG_zh">Network programming with Go 中文翻译版本</a></li>
<li><a href="http://www.hellogcc.org/effective_go.html">Effective Go</a></li>
</ul>
<h3>Groovy</h3>
<ul>
<li><a href="http://www.ibm.com/developerworks/cn/java/j-pg/">实战 Groovy 系列</a></li>
</ul>
<h3>LaTeX</h3>
<ul>
<li><a href="http://liam0205.me/2014/09/08/latex-introduction/">一份其实很短的 LaTeX 入门文档</a></li>
<li><a href="http://www.mohu.org/info/lshort-cn.pdf">一份不太简短的 LATEX 2ε 介绍</a> (PDF版)</li>
</ul>
<h3>LISP</h3>
<ul>
<li><a href="http://acl.readthedocs.org/en/latest/">ANSI Common Lisp 中文翻譯版</a></li>
</ul>
<h3>Lua</h3>
<ul>
<li><a href="https://github.com/andycai/luaprimer">Lua编程入门</a></li>
<li><a href="http://www.codingnow.com/2000/download/lua_manual.html">Lua 5.1 参考手册 中文翻译</a></li>
<li><a href="http://cloudwu.github.io/lua53doc/">Lua 5.3 参考手册 中文翻译</a></li>
</ul>
<h3>Haskell</h3>
<ul>
<li><a href="http://rwh.readthedocs.org/en/latest/">Real World Haskell 中文版</a></li>
<li><a href="http://fleurer-lee.com/lyah/">Haskell趣学指南</a></li>
</ul>
<h3>R</h3>
<ul>
<li><a href="https://github.com/yihui/r-ninja">R语言忍者秘笈</a></li>
</ul>
<h3>Scala</h3>
<ul>
<li><a href="http://twitter.github.io/scala_school/zh_cn/index.html">Scala课堂</a> (Twitter的Scala中文教程)</li>
<li><a href="http://twitter.github.io/effectivescala/index-cn.html">Effective Scala</a>(Twitter的Scala最佳实践的中文翻译)</li>
<li><a href="http://zh.scala-tour.com/">Scala指南</a></li>
</ul>
<h3>Swift</h3>
<ul>
<li><a href="http://numbbbbb.github.io/the-swift-programming-language-in-chinese/">The Swift Programming Language 中文版</a></li>
<li><a href="http://dev.swiftguide.cn">Swift 语言指南</a></li>
<li><a href="https://github.com/x140yu/Developing_iOS_8_Apps_With_Swift">Stanford 公开课,Developing iOS 8 Apps with Swift 字幕翻译文件</a></li>
</ul>
<h3>Perl</h3>
<ul>
<li><a href="https://github.com/horus/modern_perl_book">Modern Perl 中文版</a></li>
<li><a href="http://perl.linuxtoy.org/">Perl 程序员应该知道的事</a></li>
</ul>
<h3>Prolog</h3>
<ul>
<li><a href="http://fengdidi.github.io/blog/2011/11/15/qian-yan/">笨办法学Prolog</a></li>
</ul>
<h3>Scheme</h3>
<ul>
<li><a href="http://deathking.github.io/yast-cn/">Yet Another Scheme Tutorial Scheme入门教程</a></li>
<li><a href="http://songjinghe.github.io/TYS-zh-translation/">Scheme语言简明教程</a></li>
</ul>
<h3>读书笔记及其它</h3>
<ul>
<li><a href="https://github.com/fool2fish/dragon-book-exercise-answers">编译原理(紫龙书)中文第2版习题答案</a></li>
<li><a href="http://hawstein.com/posts/make-thiner-programming-pearls.html">把《编程珠玑》读薄</a></li>
<li><a href="https://github.com/XiaolongJason/ReadingNote/blob/master/Effective%20C%2B%2B/Effective%20C%2B%2B.md">Effective C++读书笔记</a></li>
<li><a href="https://github.com/qyuhen/book">Golang 学习笔记、Python 学习笔记、C 学习笔记</a> (PDF)</li>
<li><a href="https://github.com/code4craft/jsoup-learning">Jsoup 学习笔记</a></li>
<li><a href="https://github.com/lzjun567/note">学习笔记: Vim、Python、memcached</a></li>
<li><a href="http://www.ituring.com.cn/activity/details/2004">图灵开放书翻译计划--C++、Python、Java等</a></li>
<li><a href="http://g.yeeyan.org/books/2095">蒂姆·奥莱利随笔</a> (由译言网翻译,电子版免费)</li>
<li><a href="http://coer.zju.edu.cn/liu/octave-tutorial-cn.pdf">Octave 入门</a> (PDF版)</li>
<li><a href="http://sicp.readthedocs.org/en/latest/">SICP 解题集</a></li>
<li><a href="https://github.com/hacke2/hacke2.github.io/issues/2">精彩博客集合</a></li>
<li><a href="http://www.xiaoleilu.com/regex-guide/">正则表达式简明参考</a></li>
<li><a href="https://github.com/sparanoid/chinese-copywriting-guidelines">中文文案排版指北</a></li>
<li><a href="http://ganquan.info/standard-c/">Standard C 语言标准函数库速查 (Cheat Sheet)</a></li>
<li><a href="http://gh.amio.us/git-cheatsheet-chs/">Git Cheatsheet Chs</a></li>
<li><a href="https://github.com/qibaoguang/Study-Step-by-Step/blob/master/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/javascript_the_good_parts.md">JavaScript语言精粹</a></li>
<li><a href="http://producingoss.com/zh/">制造开源软件</a></li>
<li><a href="http://www.beiww.com/doc/oss/smart-questions.html">提问的智慧</a></li>
<li><a href="https://github.com/LearnShare/Learning-Markdown">Markdown 入门参考</a></li>
</ul>
<h3>测试相关</h3>
<ul>
<li><a href="http://appium.io/slate/cn/v1.2.0/">移动APP自动化测试优秀框架Appium API Reference V1.2.0 CN</a></li>
</ul>
virtualenvwrapper2015-04-07T00:00:00+00:00http://jiaxianhua.github.io/python/2015/04/07/virtualenvwrapper<h2>Mac OS X</h2>
<hr />
<p><code>sudo pip install virtualenvwrapper</code></p>
<p><code>cat ~/.bashrc</code></p>
<blockquote><p>export WORKON_HOME=$HOME/.virtualenvs</p>
<p>export PROJECT_HOME=$HOME/Devel</p>
<p>source /usr/local/bin/virtualenvwrapper.sh</p></blockquote>
<p><code>source ~/.bashrc</code></p>
<p><code>mkvirtualenv temp</code></p>
<p><code>(temp)$ pip install django</code></p>
<p><code>deactivate</code></p>
<p><code>echo 'cd $VIRTUAL_ENV' >> $WORKON_HOME/postactivate</code></p>
<p><code>workon</code></p>
<blockquote><p>temp</p></blockquote>
<p><code>workon temp</code></p>
<p><code>lssitepackages</code></p>
<blockquote><p>Django-1.8.dist-info/ pip/</p>
<p>_markerlib/ pip-6.1.0.dist-info/</p>
<p>django/ pkg_resources/</p>
<p>easy_install.py setuptools/</p>
<p>easy_install.pyc setuptools-15.0.dist-info/</p></blockquote>
<h2>virtualenvwrapper 4.3.2.post5</h2>
<hr />
<p><a href="http://virtualenvwrapper.readthedocs.org/en/latest/">http://virtualenvwrapper.readthedocs.org/en/latest/</a></p>
<p><a href="http://virtualenvwrapper.readthedocs.org/en/latest/install.html">http://virtualenvwrapper.readthedocs.org/en/latest/install.html</a></p>
<p>virtualenvwrapper is a set of extensions to Ian Bicking’s virtualenv tool. The extensions include wrappers for creating and deleting virtual environments and otherwise managing your development workflow, making it easier to work on more than one project at a time without introducing conflicts in their dependencies.</p>
<h3>Features</h3>
<ol>
<li>Organizes all of your virtual environments in one place.</li>
<li>Wrappers for managing your virtual environments (create, delete, copy).</li>
<li>Use a single command to switch between environments.</li>
<li>Tab completion for commands that take a virtual environment as argument.</li>
<li>User-configurable hooks for all operations (see Per-User Customization).</li>
<li>Plugin system for more creating sharable extensions (see Extending Virtualenvwrapper).</li>
</ol>
<h3>Introduction</h3>
<p>The best way to explain the features virtualenvwrapper gives you is to show it in use.</p>
<p>First, some initialization steps. Most of this only needs to be done one time. You will want to add the command to source /usr/local/bin/virtualenvwrapper.sh to your shell startup file, changing the path to virtualenvwrapper.sh depending on where it was installed by pip.</p>
<blockquote><p>$ pip install virtualenvwrapper</p>
<p>...</p>
<p>$ export WORKON_HOME=~/Envs</p>
<p>$ mkdir -p $WORKON_HOME</p>
<p>$ source /usr/local/bin/virtualenvwrapper.sh</p>
<p>$ mkvirtualenv env1</p>
<p>Installing</p>
<p>setuptools..........................................</p>
<p>....................................................</p>
<p>....................................................</p>
<p>...............................done.</p>
<p>virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env1/bin/predeactivate</p>
<p>virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env1/bin/postdeactivate</p>
<p>virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env1/bin/preactivate</p>
<p>virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env1/bin/postactivate New python executable in env1/bin/python</p>
<p>(env1)$ ls $WORKON_HOME</p>
<p>env1 hook.log</p></blockquote>
<p>Now we can install some software into the environment.</p>
<blockquote><p>(env1)$ pip install django</p>
<p>Downloading/unpacking django</p>
<p> Downloading Django-1.1.1.tar.gz (5.6Mb): 5.6Mb downloaded</p>
<p> Running setup.py egg_info for package django</p>
<p>Installing collected packages: django</p>
<p> Running setup.py install for django</p>
<pre><code>changing mode of build/scripts-2.6/django-admin.py from 644 to 755
changing mode of /Users/dhellmann/Envs/env1/bin/django-admin.py to 755
</code></pre>
<p>Successfully installed django</p></blockquote>
<p>We can see the new package with lssitepackages:</p>
<blockquote><p>(env1)$ lssitepackages</p>
<p>Django-1.1.1-py2.6.egg-info easy-install.pth</p>
<p>setuptools-0.6.10-py2.6.egg pip-0.6.3-py2.6.egg</p>
<p>django setuptools.pth</p></blockquote>
<p>Of course we are not limited to a single virtualenv:</p>
<blockquote><p>(env1)$ ls $WORKON_HOME</p>
<p>env1 hook.log</p>
<p>(env1)$ mkvirtualenv env2</p>
<p>Installing setuptools...............................</p>
<p>....................................................</p>
<p>....................................................</p>
<p>........... ...............................done.</p>
<p>virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env2/bin/predeactivate</p>
<p>virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env2/bin/postdeactivate</p>
<p>virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env2/bin/preactivate</p>
<p>virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env2/bin/postactivate New python executable in env2/bin/python</p>
<p>(env2)$ ls $WORKON_HOME</p>
<p>env1 env2 hook.log</p></blockquote>
<p>Switch between environments with workon:</p>
<blockquote><p>(env2)$ workon env1
(env1)$ echo $VIRTUAL_ENV
/Users/dhellmann/Envs/env1
(env1)$</p></blockquote>
<p>The workon command also includes tab completion for the environment names, and invokes customization scripts as an environment is activated or deactivated (see Per-User Customization).</p>
<blockquote><p>(env1)$ echo 'cd $VIRTUAL_ENV' >> $WORKON_HOME/postactivate</p>
<p>(env1)$ workon env2</p>
<p>(env2)$ pwd</p>
<p>/Users/dhellmann/Envs/env2</p></blockquote>
<p>postmkvirtualenv is run when a new environment is created, letting you automatically install commonly-used tools.</p>
<blockquote><p>(env2)$ echo 'pip install sphinx' >> $WORKON_HOME/postmkvirtualenv</p>
<p>(env3)$ mkvirtualenv env3</p>
<p>New python executable in env3/bin/python</p>
<p>Installing setuptools...............................</p>
<p>....................................................</p>
<p>....................................................</p>
<p>........... ...............................done.</p>
<p>virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env3/bin/predeactivate</p>
<p>virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env3/bin/postdeactivate</p>
<p>virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env3/bin/preactivate</p>
<p>virtualenvwrapper.user_scripts Creating /Users/dhellmann/Envs/env3/bin/postactivate</p>
<p>Downloading/unpacking sphinx</p>
<p> Downloading Sphinx-0.6.5.tar.gz (972Kb): 972Kb downloaded</p>
<p> Running setup.py egg_info for package sphinx</p>
<pre><code>no previously-included directories found matching 'doc/_build'
</code></pre>
<p>Downloading/unpacking Pygments>=0.8 (from sphinx)</p>
<p> Downloading Pygments-1.3.1.tar.gz (1.1Mb): 1.1Mb downloaded</p>
<p> Running setup.py egg_info for package Pygments</p>
<p>Downloading/unpacking Jinja2>=2.1 (from sphinx)</p>
<p> Downloading Jinja2-2.4.tar.gz (688Kb): 688Kb downloaded</p>
<p> Running setup.py egg_info for package Jinja2</p>
<pre><code>warning: no previously-included files matching '*' found under directory 'docs/_build/doctrees'
</code></pre>
<p>Downloading/unpacking docutils>=0.4 (from sphinx)</p>
<p> Downloading docutils-0.6.tar.gz (1.4Mb): 1.4Mb downloaded</p>
<p> Running setup.py egg_info for package docutils</p>
<p>Installing collected packages: docutils, Jinja2, Pygments, sphinx</p>
<p> Running setup.py install for docutils</p>
<p> Running setup.py install for Jinja2</p>
<p> Running setup.py install for Pygments</p>
<p> Running setup.py install for sphinx</p>
<pre><code>no previously-included directories found matching 'doc/_build'
Installing sphinx-build script to /Users/dhellmann/Envs/env3/bin
Installing sphinx-quickstart script to /Users/dhellmann/Envs/env3/bin
Installing sphinx-autogen script to /Users/dhellmann/Envs/env3/bin
</code></pre>
<p>Successfully installed docutils Jinja2 Pygments sphinx (env3)$</p>
<p>(venv3)$ which sphinx-build</p>
<p>/Users/dhellmann/Envs/env3/bin/sphinx-build</p></blockquote>
<p>Through a combination of the existing functions defined by the core package (see Command Reference), third-party plugins (see Extending Virtualenvwrapper), and user-defined scripts (see Per-User Customization) virtualenvwrapper gives you a wide variety of opportunities to automate repetitive operations.</p>
pip2015-04-07T00:00:00+00:00http://jiaxianhua.github.io/python/2015/04/07/pip<h2>Mac OS X</h2>
<hr />
<p><code>sudo easy_install pip</code></p>
<p><a href="http://stackoverflow.com/questions/17271319/installing-pip-on-mac-os-x">http://stackoverflow.com/questions/17271319/installing-pip-on-mac-os-x</a></p>
<h2>pip</h2>
<p><a href="https://pypi.python.org/pypi/pip/">https://pypi.python.org/pypi/pip/</a></p>
<p><a href="https://pip.pypa.io/en/latest/installing.html">https://pip.pypa.io/en/latest/installing.html</a></p>
<h2>Installation</h2>
<hr />
<h3>Python & OS Support</h3>
<p>pip works with CPython versions 2.6, 2.7, 3.2, 3.3, 3.4 and also pypy.</p>
<p>pip works on Unix/Linux, OS X, and Windows.</p>
<blockquote><p><strong>Note</strong></p>
<p>Python 2.5 was supported through v1.3.1, and Python 2.4 was supported through v1.1.</p></blockquote>
<p>pip included with Python
Python 2.7.9 and later (on the python2 series), and Python 3.4 and later include pip by default <a href="<https://docs.python.org/3/installing/>">^1</a>, so you may have pip already.</p>
<h3>Install pip</h3>
<p>To install pip, securely download get-pip.py. <a href="%22Secure%22">^2</a></p>
<p>Then run the following (which may require administrator access):</p>
<p><code>python get-pip.py</code></p>
<p>If setuptools (or distribute) is not already installed, get-pip.py will install setuptools for you. <a href="Beginning">^3</a></p>
<p>To upgrade an existing setuptools (or distribute), run pip install -U setuptools. <a href="Although">^4</a></p>
<p>Additionally, get-pip.py supports using the pip install options and the general options. Below are some examples:</p>
<p>Install from local copies of pip and setuptools:</p>
<p><code>python get-pip.py --no-index --find-links=/local/copies</code></p>
<p>Install to the user site <a href="The">^5</a>:</p>
<p><code>python get-pip.py --user</code></p>
<p>Install behind a proxy:</p>
<p><code>python get-pip.py --proxy="[user:passwd@]proxy.server:port"</code></p>
<p>Upgrade pip
On Linux or OS X:</p>
<p><code>pip install -U pip</code>
On Windows <a href="<https://github.com/pypa/pip/issues/1299>">^6</a>:</p>
<p><code>python -m pip install -U pip</code></p>
<p>Using OS Package Managers
On Linux, pip will generally be available for the system install of python using the system package manager, although often the latest version will be unavailable.</p>
<p>On Debian and Ubuntu:</p>
<p><code>sudo apt-get install python-pip</code></p>
<p>On Fedora:</p>
<p><code>sudo yum install python-pip</code></p>
<hr />
Libretro2015-04-07T00:00:00+00:00http://jiaxianhua.github.io/2015/04/07/libretro<p><img src="/assets/img/RetroArch/libretro_final_thumb.png" alt="libRETRO" /></p>
<p>RetroArch - Official Site</p>
<p><a href="http://www.libretro.com/">http://www.libretro.com/</a></p>
<h2>What is Libretro?</h2>
<hr />
<p>Libretro is a simple but powerful developement interface that allows for the easy creation of emulators, games and multimedia applications that can plug stright into any libretro-compatible frontend. This development interface is open to others so that they can run these pluggable emulator and game cores also in their own programs or devices.</p>
<h3>What is possible with libretro?</h3>
<ul>
<li>Play games (retro and more current ones)</li>
<li>Watch movies (through the ffmpeg libretro core)</li>
<li>Other stuff (augmented reality, etc) (experimental test cores that showcase camera and location API support)</li>
</ul>
<p>With libretro, you can make cross-platform applications that can use rich features such as OpenGL, cross-platform camera support, location support, and more in the future.</p>
<h2>What is RetroArch?</h2>
<hr />
<p>RetroArch is the official reference frontend for the libretro API.</p>
<h2>What is it most commonly used for?</h2>
<hr />
<p>RetroArch can be many things to many persons. However, it is currently used by most as a modular multi-system game/emulator system. It suits that usecase well since it has been designed to be fast, lightweight, and portable. It has fetures few other dedicated games / emulators have, such as:</p>
<ul>
<li>Real-time rewinding</li>
<li>Multi-pass shaders (up to 8-pass)</li>
<li>Game aware shading</li>
<li>Remote networked sound support</li>
<li>A build-in GUI/OSD system</li>
</ul>
<p>And more...</p>
<h2>What platforms does it support?</h2>
<hr />
<p>RetroArch sets the standard on cross-platform portability right now.</p>
<ul>
<li>Linux (x86 and x86_64)</li>
<li>Windows (32bit/64bit)</li>
<li>Mac OSX (Intel)</li>
<li>Playstation 3 (PS3 SDK)</li>
<li>Playstation Portable (PSP)[WIP]</li>
<li>Xbox 1 (XDK)</li>
<li>Xbox 360 (XeXDK/Libxenon)</li>
<li>Gamecube</li>
<li>Wii</li>
<li>Raspberry Pi</li>
<li>OpenPandora</li>
<li>Android</li>
<li>Blackberry (10/Playbook)</li>
<li>iOS</li>
</ul>
<h2>What web platforms does it support?</h2>
<hr />
<p>Far be it for us to just limit ourselves to physical platforms - we also go beyond that:</p>
<ul>
<li>Javascript (ASM.js - Enscripten)</li>
</ul>
<p>Check out a handful of demos here - <a href="http://toadking.com/retroarch/index.html">RetroArch Emscripten</a>. Note - best results are obtained by using Firefox Nightly. It can mean the difference between a core running at fullspeed or not depending on your hardware/OS.</p>
<h2>Where can I get it?</h2>
<hr />
<p>Latest stable releases are available at the <a href="http://buildbot.libretro.com/stable/">Download</a> page.</p>
<p><a href="https://play.google.com/store/apps/details?id=com.retroarch">Android app on Google Play</a></p>
Jekyll Code snippet highlighting2015-04-07T00:00:00+00:00http://jiaxianhua.github.io/jekyll/2015/04/07/jekyll-code-snippet-highlighting<p><a href="http://jekyllrb.com/docs/templates/">http://jekyllrb.com/docs/templates/</a></p>
<h2>Mac OS X</h2>
<hr />
<p><code>pip install pygments</code></p>
<blockquote><p>/Users/jiaxianhua/.virtualenvs/jekyll/lib/python2.7/site-packages/pip/<em>vendor/requests/packages/urllib3/util/ssl</em>.py:79: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.</p>
<p> InsecurePlatformWarning</p>
<p>Collecting pygments</p>
<p>/Users/jiaxianhua/.virtualenvs/jekyll/lib/python2.7/site-packages/pip/<em>vendor/requests/packages/urllib3/util/ssl</em>.py:79: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.</p>
<p>InsecurePlatformWarning</p>
<p>Downloading Pygments-2.0.2-py2-none-any.whl (672kB)</p>
<p> 100% |████████████████████████████████| 675kB 226kB/s</p>
<p>Installing collected packages: pygments</p>
<p>Successfully installed pygments-2.0.2</p></blockquote>
<p><code>$ pygmentize -S default -f html > assets/themes/bootstrap-3/css/
pygments.css</code></p>
<p><code>vim _includes/themes/bootstrap-3/default.html</code></p>
<blockquote><pre><code><!-- Custom styles -->
<link href="/assets/themes//css/style.css?body=1" rel="stylesheet" type="text/css" media="all">
<!-- highlighting -->
<link href="/assets/themes//css/pygments.css" rel="stylesheet">
</code></pre></blockquote>
<h2>Code snippet highlightingPermalink</h2>
<hr />
<p>Jekyll has built in support for syntax highlighting of over 100 languages thanks to Pygments. To use Pygments, you must have Python installed on your system and set highlighter to pygments in your site’s configuration file.</p>
<p>Alternatively, you can use Rouge to highlight your code snippets. It doesn’t support as many languages as Pygments, however it should suit most use cases. Also, since Rouge is written in pure Ruby, you don’t need Python on your system!</p>
<p>To render a code block with syntax highlighting, surround your code as follows:</p>
<p><figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">foo</span>
<span class="nb">puts</span> <span class="s1">'foo'</span>
<span class="k">end</span></code></pre></figure></p>
<p>The argument to the highlight tag (ruby in the example above) is the language identifier. To find the appropriate identifier to use for the language you want to highlight, look for the “short name” on the Pygments’ Lexers page or the Rouge wiki.
Line numbersPermalink</p>
<p>There is a second argument to highlight called linenos that is optional. Including the linenos argument will force the highlighted code to include line numbers. For instance, the following code block would include line numbers next to each line:</p>
<blockquote><p><figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table style="border-spacing: 0"><tbody><tr><td class="gutter gl" style="text-align: right"><pre class="lineno">1
2
3</pre></td><td class="code"><pre><span class="k">def</span> <span class="nf">foo</span>
<span class="nb">puts</span> <span class="s1">'foo'</span>
<span class="k">end</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></figure></p></blockquote>
<p>Stylesheets for syntax highlightingPermalink</p>
<p>In order for the highlighting to show up, you’ll need to include a highlighting stylesheet. For an example stylesheet you can look at syntax.css. These are the same styles as used by GitHub and you are free to use them for your own site. If you use linenos, you might want to include an additional CSS class definition for the .lineno class in syntax.css to distinguish the line numbers from the highlighted code.</p>
软件工程师的鄙视链2015-04-06T00:00:00+00:00http://jiaxianhua.github.io/2015/04/06/software-engineer-who-despise-chain<p>最近这几年在世界各地突然吹起了一股全民写程序的风潮,连美国总统欧巴马都在写 <em>JavaScript</em> 了,但是身为一介靠写程序(以及在上班时间胡乱上网)来谋生的 <em>developer</em>(所谓的 <em>developer</em> 就是「软件工程师」的比较潮的说法),想要提醒那些想学习写程序的人一件重要的事:慎选你的第一个程序语言。</p>
<p>在软件工程师(中国叫做「程序员」或「码农」)的圈子里,文人相轻的现象可是非常严重的,在程序设计的各个领域里都有着错综复杂的「鄙视链」。从程序语言、编辑器、平台到 { 是写在 <em>if</em> 的同一行还是下一行,不同阵营的人都习惯鄙视来鄙视去。而其中「你用什么程序语言?」更是大家最热衷的一条鄙视链,所以对于刚踏入程序设计领域的初学者来说,万一程序语言选得不好,可是会一开始就落入鄙视链的底层啊。</p>
<p>软件工程师的鄙视链到底有多惨烈、多残酷呢?</p>
<h2>程序语言篇</h2>
<p>懂 <em>Functional</em> <em>Programming</em> 的工程师鄙视老是把设计模式挂在嘴边的工程师,老是把设计模式挂在嘴边的工程师鄙视会说「你这样写就不 <em>OO</em> 了啊」的工程师,会说「你这样写就不 <em>OO</em> 了啊」的工程师鄙视会说「蛤?什么面向对象?不是把重复的 <em>code</em> 写成一个 <em>function</em> 就好了吗?」的工程师,会说「蛤?什么面向对象?不是把重复的 <em>code</em> 写成一个 <em>function</em> 就好了吗?」的工程师鄙视把同一段 <em>code</em> 到处复制贴上的工程师,把同一段 <em>code</em> 到处复制贴上的工程师鄙视 <em>PM</em>。</p>
<p>写静态语言的工程师鄙视写动态语言的工程师。</p>
<p>写汇编语言的工程师鄙视写 <em>C</em> 语言的工程师,<em>C</em> 语言工程师鄙视 <em>C</em>++ 工程师,<em>C</em>++ 工程师鄙视 <em>Java</em> 和 <em>C</em># 工程师,<em>Java</em> 工程师和 <em>C</em># 工程师则互相鄙视,而 <em>C</em># 工程师又鄙视 <em>Visual</em> <em>Basic</em> 工程师和会把 <em>C</em># 念成「<em>C</em> 井」的工程师,会把 <em>C</em># 念成「<em>C</em> 井」的工程师则鄙视认为 <em>HTML</em> 是一种程序语言的设计师。</p>
<p>用 <em>Python</em> 3 的工程师鄙视还在用 <em>Python</em> 2 的工程师,用 <em>Python</em> 2 的工程师鄙视遇到 <em>UnicodeEncodeError</em> 的工程师。</p>
<p>写 <em>iOS</em> 的工程师鄙视写 <em>Android</em> 的工程师,写 <em>Android</em> 的工程师鄙视写 <em>Windows</em> <em>Phone</em> 的工程师。</p>
<p>有 <em>Swift</em> 一年经验的工程师鄙视有 <em>Objective</em>-<em>C</em> 五年经验的工程师,写 <em>Objective</em>-<em>C</em> 的工程师鄙视用 <em>PhoneGap</em> 包装成 <em>native</em> <em>app</em> 的工程师。</p>
<p>用 <em>React</em>.<em>js</em> 的工程师鄙视用 <em>AngularJS</em> 的工程师,用 <em>AngularJS</em> 的工程师鄙视用 <em>jQuery</em> 的工程师,用 <em>jQuery</em> 的工程师鄙视用 <em>Vanilla</em> <em>JavaScript</em> 的工程师,用 <em>Vanilla</em> <em>JavaScript</em> 的工程师鄙视 <em>IE</em> 的用户。</p>
<p>会用 <em>debugger</em> 的工程师鄙视用 <em>assert</em> 的工程师,用 <em>assert</em> 的工程师鄙视只会 <em>print</em>() 的工程师;用 <em>console</em>.<em>log</em>() 来 <em>debug</em> 的工程师鄙视用 <em>alert</em>() 来 <em>debug</em> 的工程师。</p>
<p>写 <em>Ruby</em> <em>on</em> <em>Rails</em> 的工程师鄙视所有使用其他语言的工程师。</p>
<p>什么?你说 <em>Ruby</em>?<em>Ruby</em> 只是 <em>Ruby</em> <em>on</em> <em>Rails</em> 的一套框架,才不是什么程序语言呢!</p>
<p>所有的工程师都鄙视 <em>PHP</em> 工程师。</p>
<h2>工具篇</h2>
<hr />
<p>用 <em>text</em> <em>editor</em> 的工程师鄙视用 <em>IDE</em> 的工程师。</p>
<p>用 <em>Vim</em> 的工程师鄙视用 <em>Emacs</em> 的工程师,用 <em>Emacs</em> 的工程师鄙视用 <em>Vim</em> 的工程师,无论是用 <em>Vim</em> 或 <em>Emacs</em> 的工程师都鄙视所有用其他编辑器的工程师;用 <em>Atom</em>、<em>Notepad</em>++、<em>Sublime</em> <em>Text</em> 的工程师鄙视用 <em>Windows</em> 记事本的工程师。</p>
<p>用 <em>Android</em> <em>Studio</em> 或 <em>IntelliJ</em> <em>IDEA</em> 的工程师鄙视用 <em>Eclipse</em> 的工程师,用 <em>Eclipse</em> 的工程师鄙视用 <em>NetBeans</em> 的工程师。</p>
<p>程序代码用 <em>space</em> 缩排的工程师鄙视用 <em>tab</em> 缩排的工程师,用 <em>tab</em> 缩排的工程师鄙视混用 <em>space</em> 和 <em>tab</em> 来缩排的工程师。</p>
<p>用 <em>Git</em> 或 <em>Mercurial</em> 的工程师鄙视用 <em>Subversion</em> 的工程师,用 <em>Subversion</em> 的工程师鄙视用 <em>Dropbox</em> 来做版本控制的工程师,用 <em>Dropbox</em> 来做版本控制的工程师鄙视根本不知道什么叫做版本控制的工程师。</p>
<p>知道 <em>GitHub</em> 的工程师鄙视不知道 <em>GitHub</em> 的工程师;在 <em>GitHub</em> 有 <em>private</em> <em>repo</em> 的工程师鄙视为了免费的 <em>private</em> <em>repo</em> 而去用 <em>BitBucket</em> 的工程师。</p>
<p>用 <em>Zsh</em> 的工程师鄙视用 <em>Bash</em> 的工程师,用 <em>Bash</em> 的工程师鄙视用 <em>Cygwin</em> 的工程师,用 <em>Cygwin</em> 的工程师鄙视用「命令提示字符」的工程师,用命令提示字符的工程师鄙视用 <em>GUI</em> 接口的工程师。</p>
<p>用 <em>IRC</em> 的工程师鄙视用 <em>HipChat</em> 的工程师,用 <em>HipChat</em> 的工程师鄙视用 <em>Slack</em> 的设计师和 <em>PM</em>。</p>
<p>用 <em>reStructuredText</em> 写文件的工程师鄙视用 <em>Markdown</em> 写文件的工程师,用 <em>Markdown</em> 写文件的工程师鄙视用 <em>HTML</em> 写文件的工程师,用 <em>HTML</em> 写文件的工程师鄙视不写文件的工程师,然后用 <em>LaTeX</em> 写文件的工程师鄙视所有工程师。</p>
<p>用 <em>Nginx</em> 的工程师鄙视用 <em>Apache</em> 的工程师,用 <em>Apache</em> 的工程师鄙视用 <em>IIS</em> 的工程师。</p>
<p>用 <em>Spark</em> 的工程师鄙视用 <em>Hadoop</em> 的工程师,用 <em>Hadoop</em> 的工程师鄙视用 <em>Hadoop</em> 处理只有几 <em>GB</em> 数据的工程师,用 <em>Hadoop</em> 处理只有 1<em>GB</em> 数据的工程师鄙视用 <em>NoSQL</em> 的工程师,用 <em>NoSQL</em> 的工程师鄙视用关系数据库的工程师,用关系数据库的工程师鄙视用 <em>Excel</em> 的 <em>PM</em>。</p>
<p>用 <em>Docker</em> 来部署 <em>server</em> 的工程师鄙视用 <em>Ansible</em> 或 <em>Puppet</em> 来部署 <em>server</em> 的工程师,用 <em>Ansible</em> 或 <em>Puppet</em> 来部署 <em>server</em> 的工程师鄙视用 <em>Fabric</em> 来部署 <em>server</em> 的工程师,用 <em>Fabric</em> 来部署 <em>server</em> 的工程师鄙视手动 <em>SSH</em> 的工程师。</p>
<h2><em>OS</em> 篇</h2>
<hr />
<p>用 <em>Mac</em> <em>OS</em> <em>X</em> 的工程师鄙视用 <em>Linux</em> 的工程师,用 <em>Linux</em> 的工程师鄙视用 <em>Windows</em> 的工程师。</p>
<p>用 <em>Debian</em> 的工程师瞧不起用 <em>Ubuntu</em> 的工程师,用 <em>Ubuntu</em> 的工程师瞧不起用非 <em>LTS</em> 版本的 <em>Ubuntu</em> 的工程师。</p>
<h2>硬件篇</h2>
<hr />
<p>用 <em>MacBook</em> <em>Pro</em> <em>Retina</em> 的工程师鄙视用 <em>MacBook</em> <em>Air</em> 的工程师,用 <em>MacBook</em> <em>Air</em> 的工程师鄙视用 <em>ThinkPad</em> 的工程师,然后用 <em>Raspberry</em> <em>Pi</em> 的工程师鄙视用 <em>MacBook</em> <em>Pro</em> <em>Retina</em> 的工程师。</p>
<p>用 <em>Dvorak</em> 键盘的工程师鄙视用 <em>Mac</em> 键盘的工程师,用 <em>Mac</em> 键盘的工程师鄙视用 <em>QWERTY</em> 键盘的工程师,用 <em>QWERTY</em> 键盘的工程师鄙视鄙视不知道 <em>QWERTY</em> 键盘是什么的工程师,不知道 <em>QWERTY</em> 键盘是什么的工程师鄙视用手写板的设计师。</p>
<p>坐 <em>Aeron</em> 椅子的工程师鄙视坐普通办公椅的工程师,坐普通办公椅的工程师鄙视跟他一样做普通办公椅的 <em>PM</em>,然后站着写程序的工程师鄙视坐 <em>Aeron</em> 椅子的工程师。</p>
<h2>职场篇</h2>
<hr />
<p>搞硬件的工程师鄙视搞软件的工程师。</p>
<p>写 <em>OS</em> 的工程师鄙视写 <em>Web</em> 的工程师,写 <em>Web</em> 的工程师鄙视写 <em>desktop</em> <em>application</em> 的工程师。</p>
<p>后端工程师鄙视前端工程师。</p>
<p>工程师跟设计师互相鄙视。</p>
<p>信奉 <em>Test</em>-<em>Driven</em> <em>Development</em> 的工程师鄙视先写 <em>code</em> 再补 <em>tests</em> 的工程师,先写 <em>code</em> 再补 <em>tests</em> 的工程师鄙视不写 <em>tests</em> 的工程师,不写 <em>tests</em> 的工程师鄙视又他妈乱改需求的 <em>PM</em>。</p>
<p>没有证照的工程师鄙视考了一堆证照的工程师。</p>
<p>上班穿休闲服的工程师鄙视上班穿西装的工程师,上班穿西装的工程师鄙视上班穿系服的工程师。</p>
<p>原文链接:<a href="http://vinta.ws/blog/695">http://vinta.ws/blog/695</a></p>
git合并多个commit到一个2015-04-05T00:00:00+00:00http://jiaxianhua.github.io/2015/04/05/muiltply-commits-to-one-commit<p>用<code>rebase -i</code></p>
<p>比如下图的commit 历史,想要把 "Second change" 和 "Third change" 这两个commit合并到一起</p>
<p><img src="/assets/img/git/1.jpg" alt="1" /></p>
<p>那么可以
<code>git rebase -i 7a734e9d47895e096313003d6a2e4f697a16e2e3</code></p>
<blockquote><p><strong>注意</strong> 7a734e9d47895e096313003d6a2e4f697a16e2e3 是 "Second change" 的<strong>前</strong>一个commit ID。</p></blockquote>
<p>然后会出现编辑器 (具体什么编辑器看你的配置,在linux下,默认是 vi)列出从 7a734e 后面的所有commit,如下图</p>
<p><img src="/assets/img/git/2.jpg" alt="2" /></p>
<p>因为我们要把 "Second change" 和 "Third change" 合并到一起,所以只需要把 "Third change"前面的那个 pick 改成 squash即可,意思是将 "Third change" 和 它前一个commit (即 "Second change") 合并</p>
<p>修改后应该是这样</p>
<p><img src="/assets/img/git/3.jpg" alt="3" /></p>
<p>然后保存退出编辑器,git 就会执行rebase操作,当他遇到 "Second" 和 "Third" 的时候,会再次启动编辑器告诉你即将合并,让你提供commit message,如下图</p>
<p><img src="/assets/img/git/4.jpg" alt="4" /></p>
<p>默认的包括了两个commit的原始消息,你可以在这里任意修改commit message,比如改成 “Second and Third changes in single commit",然后保存退出,git就会把这两个commit变成一个新的commit。做完后我们再用git log看一下,就会变成下图</p>
<p><img src="/assets/img/git/5.jpg" alt="5" /></p>
<p>对比原始git log信息,你就可以发现两个commit被合成一个了。</p>
<p>同理,你可以将任意多个commit合并成一个 (第一个commit保持 pick, 后续commit改成 squash即可)</p>
<p>更新到远程仓库<br/>
<code>git push origin +master</code></p>
visual git guide2015-04-04T00:00:00+00:00http://jiaxianhua.github.io/2015/04/04/visual-git-guide<p><a href="http://marklodato.github.io/visual-git-guide/index-en.html">http://marklodato.github.io/visual-git-guide/index-en.html</a></p>
<p>This page gives brief, visual reference for the most common commands in git. Once you know a bit about how git works, this site may solidify your understanding.</p>
<p>Also recommended: <a href="http://onlywei.github.io/explain-git-with-d3/">Visualizing Gi#t Concepts with D3</a></p>
<h2>Contents</h2>
<hr />
<ol>
<li>Basic Usage</li>
<li>Conventions</li>
<li>Commands in Detail
<ol>
<li>Diff</li>
<li>Commit</li>
<li>Checkout</li>
<li>Committing with a Detached HEAD</li>
<li>Reset</li>
<li>Merge</li>
<li>Cherry Pick</li>
<li>Rebase</li>
</ol>
</li>
<li>Technical Notes</li>
</ol>
<h2>Basic Usage</h2>
<hr />
<p><img src="/assets/img/git/basic-usage.png" alt="basic-usage" /></p>
<p>The four commands above copy files between the working directory, the stage (also called the index), and the history (in the form of commits).</p>
<p><code>git add files</code> copies files (at their current state) to the stage.<br/>
<code>git commit</code> saves a snapshot of the stage as a commit.<br/>
<code>git reset -- files</code> unstages files; that is, it copies files from the latest commit to the stage. Use this command to "undo" a git add files. You can also <code>git reset to unstage everything.</code> git checkout -- files copies files from the stage to the working directory. Use this to throw away local changes.</p>
<p>You can use git <code>reset -p</code>, <code>git checkout -p</code>, or <code>git add -p</code> instead of (or in addition to) specifying particular files to interactively choose which hunks copy.</p>
<p>It is also possible to jump over the stage and check out files directly from the history or commit files without staging first.</p>
<p><img src="/assets/img/git/basic-usage-2.png" alt="basic-usage-2" /></p>
<p><code>git commit -a</code> is equivalent to running git add on all filenames that existed in the latest commit, and then running git commit.<br/>
<code>git commit files</code> creates a new commit containing the contents of the latest commit, plus a snapshot of files taken from the working directory. Additionally, files are copied to the stage.<br/>
<code>git checkout HEAD -- files</code> copies files from the latest commit to both the stage and the working directory.</p>
<h2>Conventions</h2>
<hr />
<p>In the rest of this document, we will use graphs of the following form.</p>
<p><img src="/assets/img/git/conventions.png" alt="conventions" /></p>
<p>Commits are shown in green as 5-character IDs, and they point to their parents. Branches are shown in orange, and they point to particular commits. The current branch is identified by the special reference HEAD, which is "attached" to that branch. In this image, the five latest commits are shown, with ed489 being the most
recent. master (the current branch) points to this commit, while maint (another branch) points to an ancestor of master's commit.</p>
<h2>Commands in Detail</h2>
<hr />
<h3>Diff</h3>
<p>There are various ways to look at differences between commits. Below are some common examples. Any of these commands can optionally take extra filename arguments that limit the differences to the named files.</p>
<p><img src="/assets/img/git/diff.png" alt="diff" /></p>
<h3>Commit</h3>
<p>When you commit, git creates a new commit object using the files from the stage and sets the parent to the current commit. It then points the current branch to this new commit. In the image below, the current branch is master. Before the command was run, master pointed to ed489. Afterward, a new commit, <em>f0cec</em>, was created, with parent
<em>ed489</em>, and then <em>master</em> was moved to the new commit.</p>
<p><img src="/assets/img/git/commit-master.png" alt="commit-master" /></p>
<p>This same process happens even when the current branch is an ancestor of another. Below, a commit occurs on branch <em>maint</em>, which was an ancestor of master, resulting in <em>1800b</em>. Afterward, <em>maint</em> is no longer an ancestor of <em>master</em>. To join the two histories, a merge (or rebase) will be necessary.</p>
<p><img src="/assets/img/git/commit-maint.png" alt="commit-maint" /></p>
<p>Sometimes a mistake is made in a commit, but this is easy to correct with git commit --amend. When you use this command, git creates a new commit with the same parent as the current commit. (The old commit will be discarded if nothing else references it.)</p>
<p><img src="/assets/img/git/commit-amend.png" alt="commit-amend" /></p>
<p>A fourth case is committing with a detached HEAD, as explained later.</p>
<h3>Checkout</h3>
<p>The checkout command is used to copy files from the history (or stage) to the working directory, and to optionally switch branches.</p>
<p>When a filename (and/or -p) is given, git copies those files from the given commit to the stage and the working directory. For example, git checkout HEAD~ foo.c copies the file foo.c from the commit called <em>HEAD~</em> (the parent of the current commit) to the working directory, and also stages it. (If no commit name is given, files are copied from the stage.) Note that the current branch is not changed.</p>
<p><img src="/assets/img/git/checkout-files.png" alt="checkout-files" /></p>
<p>When a filename is not given but the reference is a (local) branch, HEAD is moved to that branch (that is, we "switch to" that branch), and then the stage and working directory are set to match the contents of that commit. Any file that exists in the new commit (<em>a47c3</em> below) is copied; any file that exists in the old commit (<em>ed489</em>) but not in the new one is deleted; and any file that exists in neither is ignored.</p>
<p><img src="/assets/img/git/checkout-branch.png" alt="checkout-branch" /></p>
<p>When a filename is not given and the reference is not a (local) branch — say, it is a tag, a remote branch, a SHA-1 ID, or something like master~3 — we get an anonymous branch, called a detached HEAD. This is useful for jumping around the history. Say you want to compile version 1.6.6.1 of git. You can git checkout v1.6.6.1 (which is a tag, not a branch), compile, install, and then switch back to another branch, say git checkout master. However, committing works slightly differently with a detached HEAD; this is covered below.</p>
<p><img src="/assets/img/git/checkout-detached.png" alt="checkout-detached" /></p>
<h3>Committing with a Detached HEAD</h3>
<p>When <em>HEAD</em> is detached, commits work like normal, except no named branch gets updated. (You can think of this as an anonymous branch.)</p>
<p><img src="/assets/img/git/commit-detached.png" alt="commit-detached" /></p>
<p>Once you check out something else, say master, the commit is (presumably) no longer referenced by anything else, and gets lost. Note that after the command, there is nothing referencing <em>2eecb</em>.</p>
<p><img src="/assets/img/git/checkout-after-detached.png" alt="checkout-after-detached" /></p>
<p>If, on the other hand, you want to save this state, you can create a new named branch using git checkout -b <em>name</em>.</p>
<p><img src="/assets/img/git/checkout-b-detached.png" alt="checkout-b-detached" /></p>
<h3>Reset</h3>
<p>The reset command moves the current branch to another position, and optionally updates the stage and the working directory. It also is used to copy files from the history to the stage without touching the working directory.</p>
<p>If a commit is given with no filenames, the current branch is moved to that commit, and then the stage is updated to match this commit. If <code>--hard</code> is given, the working directory is also updated. If <code>--soft</code> is given, neither is updated.</p>
<p><img src="/assets/img/git/reset-commit.png" alt="reset-commit" /></p>
<p>If a commit is not given, it defaults to <em>HEAD</em>. In this case, the branch is not moved, but the stage (and optionally the working directory, if --hard is given) are reset to the contents of the last commit.</p>
<p><img src="/assets/img/git/reset.png" alt="reset" /></p>
<p>If a filename (and/or -p) is given, then the command works similarly to checkout with a filename, except only the stage (and not the working directory) is updated. (You may also specify the commit from which to take files, rather than <em>HEAD</em>.)</p>
<p><img src="/assets/img/git/reset-files.png" alt="reset-files" /></p>
<h3>Merge</h3>
<p>A merge creates a new commit that incorporates changes from other commits. Before merging, the stage must match the current commit. The trivial case is if the other commit is an ancestor of the current commit, in which case nothing is done. The next most simple is if the current commit is an ancestor of the other commit. This results in a <em>fast-forward</em> merge. The reference is simply moved, and then the new commit is checked out.</p>
<p><img src="/assets/img/git/merge-ff.png" alt="merge-ff" /></p>
<p>Otherwise, a "real" merge must occur. You can choose other strategies, but the default is to perform a "recursive" merge, which basically takes the current commit (ed489 below), the other commit (33104), and their common ancestor (b325c), and performs a three-way merge. The result is saved to the working directory and the stage, and then a commit occurs, with an extra parent (33104) for the new commit.</p>
<p><img src="/assets/img/git/merge.png" alt="merge" /></p>
<h3>Cherry Pick</h3>
<p>The cherry-pick command "copies" a commit, creating a new commit on the current branch with the same message and patch as another commit.</p>
<p><img src="/assets/img/git/cherry-pick.png" alt="cherry-pick" /></p>
<h2>Rebase</h2>
<p>A rebase is an alternative to a merge for combining multiple branches. Whereas a merge creates a single commit with two parents, leaving a non-linear history, a rebase replays the commits from the current branch onto another, leaving a linear history. In essence, this is an automated way of performing several cherry-picks in a row.</p>
<p><img src="/assets/img/git/rebase.png" alt="rebase" /></p>
<p>The above command takes all the commits that exist in topic but not in master (namely 169a6 and 2c33a), replays them onto master, and then moves the branch head to the new tip. Note that the old commits will be garbage collected if they are no longer referenced.</p>
<p>To limit how far back to go, use the --onto option. The following command replays onto master the most recent commits on the current branch since 169a6 (exclusive), namely 2c33a.</p>
<p><img src="/assets/img/git/rebase-onto.png" alt="rebase-onto" /></p>
<p>There is also git rebase --interactive, which allows one to do more complicated things than simply replaying commits, namely dropping, reordering, modifying, and squashing commits. There is no obvious picture to draw for this; see git-rebase(1) for more details.</p>
<h2>Technical Notes</h2>
<hr />
<p>The contents of files are not actually stored in the index (.git/index) or in commit objects. Rather, each file is stored in the object database (.git/objects) as a blob, identified by its SHA-1 hash. The index file lists the filenames along with the identifier of the associated blob, as well as some other data. For commits, there is an additional data type, a tree, also identified by its hash. Trees correspond to directories in the working directory, and contain a list of trees and blobs corresponding to each filename within that directory. Each commit stores the identifier of its top-level tree, which in turn contains all of the blobs and other trees associated with that commit.</p>
<p>If you make a commit using a detached HEAD, the last commit really is referenced by something: the reflog for HEAD. However, this will expire after a while, so the commit will eventually be garbage collected, similar to commits discarded with git commit --amend or git rebase.</p>
Git Manual2015-04-04T00:00:00+00:00http://jiaxianhua.github.io/2015/04/04/git-manual<h2>Create</h2>
<hr />
<p>Clone an existing repository</p>
<blockquote><p> <code>git clone ssh://user@domain.tld/repo.git</code></p></blockquote>
<p>Clone an existing repository and all its sub-modules recursively</p>
<blockquote><p> <code>git clone --recursive ssh://user@domain.tld/repo.git</code></p></blockquote>
<p>Create a new local repository</p>
<blockquote><p> <code>git init</code></p></blockquote>
<h2>Local Changes</h2>
<hr />
<p>List changed files in your working directory</p>
<blockquote><p> <code>git status</code></p></blockquote>
<p>List changes to tracked files</p>
<blockquote><p> <code>git diff</code></p></blockquote>
<p>Add all current changes to the next commit</p>
<blockquote><p> <code>git add .</code></p></blockquote>
<p>Add some changes to the next commit</p>
<blockquote><p> <code>git add -p <file></code></p></blockquote>
<p>Commit all local changes in tracked files</p>
<blockquote><p> <code>git commit -a</code></p></blockquote>
<p>Commit previously staged changes</p>
<blockquote><p> <code>git commit</code></p></blockquote>
<p>Change the last commit</p>
<blockquote><p> <code>git commit --amend</code></p></blockquote>
<p>Note: You shouldn't amend published commits!</p>
<h2>Commit History</h2>
<hr />
<p>Show all commits</p>
<blockquote><p> <code>git log</code></p></blockquote>
<p>Show changes over time for a specific file</p>
<blockquote><p> <code>git log -p <file></code></p></blockquote>
<p>Show changes over time for a specific committer</p>
<blockquote><p> <code>git log --author=<committer name></code></p></blockquote>
<p>Note: <code><committer name></code> is a pattern, so Ed will match Edward Smith. Quotes are optional if the pattern doesn't contain spaces.<br/>
Who changed what and when in file</p>
<blockquote><p> <code>git blame <file></code></p></blockquote>
<p>Store changes temporarily</p>
<blockquote><p> <code>git stash</code></p></blockquote>
<p>Remove and apply stashed changes</p>
<blockquote><p> <code>git stash pop</code></p></blockquote>
<h2>Branches & Tags</h2>
<hr />
<p>List all existing branches</p>
<blockquote><p> <code>git branch</code></p></blockquote>
<p>Switch HEAD branch</p>
<blockquote><p> <code>git checkout <branch></code></p></blockquote>
<p>Create a new branch based on your current HEAD</p>
<blockquote><p> <code>git branch <new-branch></code></p></blockquote>
<p>Create a new tracking branch based on a remote branch</p>
<blockquote><p> <code>git branch --track <new-branch> <remote-branch></code></p></blockquote>
<p>Delete a local branch</p>
<blockquote><p> <code>git branch -d <branch></code></p></blockquote>
<p>Delete a remote branch</p>
<blockquote><p> <code>git push origin --delete <branch></code></p></blockquote>
<p>Tag the current commit</p>
<blockquote><p> <code>git tag <tag-name></code></p></blockquote>
<h2>Update & Publish</h2>
<hr />
<p>List all currently configured remotes</p>
<blockquote><p> <code>git remote -v</code></p></blockquote>
<p>Show information about a remote</p>
<blockquote><p> <code>git remote show <remote></code></p></blockquote>
<p>Add new remote repository</p>
<blockquote><p> <code>git remote add <remote> <url></code></p></blockquote>
<p>Download all changes from remote, but don't merge into HEAD</p>
<blockquote><p> <code>git fetch <remote></code></p></blockquote>
<p>Download all changes from remote, but don't merge into HEAD and clean up deleted branches from origin</p>
<blockquote><p> <code>git fetch -p <remote></code></p></blockquote>
<p>Download changes and directly merge into HEAD</p>
<blockquote><p> <code>git pull <remote> <branch></code></p></blockquote>
<p>Publish local changes on a remote</p>
<blockquote><p> <code>git push <remote> <branch></code></p></blockquote>
<p>Track a remote repository</p>
<blockquote><p> <code>git remote add --track <remote-branch> <remote> <url></code></p></blockquote>
<p>Publish your tags</p>
<blockquote><p> <code>git push --tags</code></p></blockquote>
<h2>Merge & Rebase</h2>
<hr />
<p>Merge branch into your current HEAD</p>
<blockquote><p> <code>git merge <branch></code></p></blockquote>
<p>Rebase your current HEAD onto branch</p>
<blockquote><p> <code>git rebase <branch></code></p></blockquote>
<p>Note: You shouldn't rebase published commits!<br/>
Abort a rebase</p>
<blockquote><p> <code>git rebase --abort</code></p></blockquote>
<p>Continue a rebase after resolving conflicts</p>
<blockquote><p> <code>git rebase --continue</code></p></blockquote>
<p>Resolve conflicts using your configured merge tool</p>
<blockquote><p> <code>git mergetool</code></p></blockquote>
<p>Manually resolve conflicts using your editor and mark file as resolved</p>
<blockquote><p> <code>git add <resolved-file></code></p>
<p> <code>git rm <resolved-file></code></p></blockquote>
<h2>Undo</h2>
<hr />
<p>Discard all local changes in your working directory</p>
<blockquote><p> <code>git reset --hard HEAD</code></p></blockquote>
<p>Discard local changes in a specific file</p>
<blockquote><p> <code>git checkout HEAD <file></code></p></blockquote>
<p>Revert a commit by providing a new commit with contrary changes</p>
<blockquote><p> <code>git revert <commit></code></p></blockquote>
<p>Restore a specific file from a previous commit</p>
<blockquote><p> <code>git checkout <commit> <file></code></p></blockquote>
<p>Reset your HEAD pointer to a previous commit<br/>
Discarding local changes:</p>
<blockquote><p> <code>git reset --hard <commit></code></p></blockquote>
<p>Preserving all changes as unstaged changes:</p>
<blockquote><p> <code>git reset <commit></code></p></blockquote>
<p>Preserving uncommitted local changes:</p>
<blockquote><p> <code>git reset --keep <commit></code></p></blockquote>
<h1>Notes</h1>
<hr />
<p>Based on the cheat sheet from <a href="www.git-tower.com">Tower.app</a>. The original can be found <a href="www.git-tower.com/blog/git-cheat-sheet/">here</a>.</p>
git push multiple hosts2015-04-03T00:00:00+00:00http://jiaxianhua.github.io/2015/04/03/git-push-multiple-hosts<hr />
<p>bash</p>
<blockquote><p> <code>$ cat .git/config</code></p>
<pre><code>[remote "origin"]
url = ssh://551a7df75973ca0c1d00012a@markdown-jiaxianhua.rhcloud.com/~/git/ markdown.git/
fetch = +refs/heads/*:refs/remotes/origin/*
</code></pre>
<p> <code>$ vim .git/config</code></p>
<pre><code>[remote "origin"]
url = git@github.com:jiaxianhua/jiaxianhua.github.io.git
url = ssh://551a7df75973ca0c1d00012a@markdown-jiaxianhua.rhcloud.com/~/git/ markdown.git/
fetch = +refs/heads/*:refs/remotes/origin/*
</code></pre>
<p><code>$ git pull</code></p>
<pre><code>warning: no common commits
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:jiaxianhua/jiaxianhua.github.io
+ f27c775...1290d2a master -> origin/master (forced update)
Auto-merging README.md
CONFLICT (add/add): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
GnuHuadeMacBook-Air:markdown gnuhua$ git status
On branch master
Your branch and 'origin/master' have diverged,
and have 4 and 1 different commit each, respectively.
(use "git pull" to merge the remote branch into yours)
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
**both added: README.md**
no changes added to commit (use "git add" and/or "git commit -a")
</code></pre>
<p> <code>$ git diff</code></p>
<p> <code>$ vim README.md</code></p>
<p> <code>$ git status</code></p>
<pre><code>On branch master
Your branch and 'origin/master' have diverged,
and have 4 and 1 different commit each, respectively.
(use "git pull" to merge the remote branch into yours)
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add >file>..." to mark resolution)
both added: README.md
no changes added to commit (use "git add" and/or "git commit -a")
</code></pre>
<p> <code>$ git commit -am "Merge README"</code></p>
<pre><code>[master 89190bc] Merge README
</code></pre>
<p> <code>$ git status</code></p>
<pre><code>On branch master
Your branch is ahead of 'origin/master' by 5 commits.
(use "git push" to publish your local commits)
nothing to commit, working directory clean
</code></pre>
<p> <code>$ git push</code></p>
<pre><code>Counting objects: 124, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (109/109), done.
Writing objects: 100% (122/122), 207.62 KiB | 0 bytes/s, done.
Total 122 (delta 13), reused 19 (delta 0)
To git@github.com:jiaxianhua/jiaxianhua.github.io.git
1290d2a..89190bc master -> master
Counting objects: 8, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 528 bytes | 0 bytes/s, done.
Total 6 (delta 2), reused 0 (delta 0)
remote: Stopping jekyll cart
remote: Sending SIGTERM to jekyll:450149 ...
remote: Building git ref 'master', commit 89190bc
remote: Preparing build for deployment
remote: Deployment id is 63930df6
remote: Activating deployment
remote: Starting jekyll cart
remote: Executing bundle install
remote: Fetching gem metadata from https://rubygems.org/.........
remote: Resolving dependencies...
remote: Using blankslate (2.1.2.4)
remote: Using hitimes (1.2.2)
remote: Using timers (4.0.1)
remote: Using celluloid (0.16.0)
remote: Using fast-stemmer (1.0.2)
remote: Using classifier-reborn (2.0.3)
remote: Using coffee-script-source (1.9.1)
remote: Using execjs (2.4.0)
remote: Using coffee-script (2.3.0)
remote: Using colorator (0.1)
remote: Using ffi (1.9.8)
remote: Using jekyll-coffeescript (1.0.1)
remote: Using jekyll-gist (1.2.1)
remote: Using jekyll-paginate (1.1.0)
remote: Using sass (3.4.13)
remote: Using jekyll-sass-converter (1.3.0)
remote: Using rb-fsevent (0.9.4)
remote: Using rb-inotify (0.9.5)
remote: Using listen (2.10.0)
remote: Using jekyll-watch (1.2.1)
remote: Using kramdown (1.6.0)
remote: Using liquid (2.6.2)
remote: Using mercenary (0.3.5)
remote: Using posix-spawn (0.3.10)
remote: Using yajl-ruby (1.2.1)
remote: Using pygments.rb (0.6.3)
remote: Using redcarpet (3.2.2)
remote: Using safe_yaml (1.0.4)
remote: Using parslet (1.5.0)
remote: Using toml (0.1.2)
remote: Using jekyll (2.5.3)
remote: Using json (1.8.2)
remote: Using bundler (1.3.5)
remote: Your bundle is complete!
remote: Use `bundle show [gemname]` to see where a bundled gem is installed.
remote: Starting Jekyll server
remote: Found 127.11.163.129:8080 listening port
remote: -------------------------
remote: Git Post-Receive Result: success
remote: Activation status: success
remote: Deployment completed with status: success
To ssh://551a7df75973ca0c1d00012a@markdown-jiaxianhua.rhcloud.com/~/git/markdown.git/
f27c775..89190bc master -> master
</code></pre></blockquote>
git - the simple guide2015-04-03T00:00:00+00:00http://jiaxianhua.github.io/2015/04/03/git---the-simple-guide<p>just a simple guide for getting started with git. no deep shit ;)</p>
<p><a href="http://rogerdudler.github.io/git-guide/index.html">git - the simeple guide</a></p>
<p><a href="http://rogerdudler.github.io/git-guide/index.html">http://rogerdudler.github.io/git-guide/index.html</a></p>
<h2>setup</h2>
<hr />
<p><a href="http://code.google.com/p/git-osx-installer/downloads/list?can=3">Download git for OS X</a></p>
<p><a href="http://msysgit.github.io/">Download git for Windows</a></p>
<p><a href="http://git-scm.com/book/en/Getting-Started-Installing-Git">Download git for LInux</a></p>
<h2>create a new repository</h2>
<hr />
<p>create a new directory, open it and perform a</p>
<p><code>git init</code></p>
<p>to create a new git repository.</p>
<h2>checkout a repository</h2>
<hr />
<p>create a working copy of a local repository by running the command</p>
<p><code>git clone /path/to/repository</code></p>
<p>when using a remote server, your command will be</p>
<p><code>git clone username@host:/path/to/repository</code></p>
<h2>workflow</h2>
<hr />
<p>your local repository consists of three <code>trees</code> maintained by git. the first one is your <strong>Working Directory</strong> which holds the actual files. the second one is the <strong>Index</strong> which acts as a staging area and finally the <strong>HEAD</strong> which points to the last commit you're made.</p>
<p><img src="/assets/img/git/trees.png" alt="git trees" /></p>
<h2>add & commit</h2>
<hr />
<p>You can propose changes (add it to the <strong>Index</strong>) using</p>
<p><code>git add <filename></code><br/>
<code>git add *</code></p>
<p>This is the first step in the basic git workflow. To actually commit these change use</p>
<p><code>git commit -m "Commit message"</code></p>
<p>Now the file is commited to the <strong>HEAD</strong>, but not in your remote repository yet.</p>
<h2>pushing changes</h2>
<hr />
<p>Your changes are now in the <strong>HEAD</strong> of your local working copy. To send those changes to your remote repository, execute</p>
<p><code>git push origin master</code></p>
<p>Change <em>master</em> to whatever branch you want to push your changes to.</p>
<p>If you have not cloned an existing repository and want to connect your repository to a remote server, you need to add it with</p>
<p><code>git remote add origin <server></code></p>
<p>Now you are able to push your changes to the selected remote server.</p>
<h2>branching</h2>
<hr />
<p>Branches are used to develop features isolated from each other. The <em>master</em> branch is the <code>default</code> branch when you create a repository. Use other branches for development and merge them back to the master branch upon completion.</p>
<p><img src="/assets/img/git/branches.png" alt="branches" /></p>
<p>create a new branch named <code>feature_x</code> and switch to it using</p>
<p><code>git checkout -b feature_x</code></p>
<p>switch back to master</p>
<p><code>git checkout master</code></p>
<p>and delete the branch again</p>
<p><code>git branch -d feature_x</code></p>
<p>a branch is <em>not available to other</em> unless you push the branch to you remote repository</p>
<p><code>git push origin <branch></code></p>
<h2>update & merge</h2>
<hr />
<p>to update your local repository to the newest commit, execute</p>
<p><code>git pull</code></p>
<p>in your working directory to <em>fetch</em> and <em>merge</em> remote changes.</p>
<p>to merge another branch into your active branch(e.g. master), use</p>
<p><code>git merge <branch></code></p>
<p>in both cases git tries to auto-merge changes. Unfortunately, this is not always possible and results in <em>conflicts</em>. Your are responsible to merge those <em>conflicts</em> manually by editing the files shown by git. After changing, you need to mark them as merged with</p>
<p><code>git add <filename></code></p>
<p>before merging changes, you can also preview them by using</p>
<p><code>git diff <source_brance> <target_branch></code></p>
<h2>tagging</h2>
<hr />
<p>it's recommended to create tags for software releases. this is a known concept, which also exists in SVN. You can create a new tag named 1.0.0 by executing</p>
<p><code>git tag 1.0.0 1b2e1d63ff</code></p>
<p>the <em>1b2e1d63ff</em> stands for the first 10 characters of the commit id you want to reference with your tag. You can get the commit id by looking at the...</p>
<h2>log</h2>
<hr />
<p>in its simplest form, you can study repository history using.. <code>git log</code>
You can add a lot of parameters to make the log look like what you want. To see only the commits of a certain author:</p>
<p><code>git log --author=bob</code></p>
<p>To see a very compressed log where each commit is one line:</p>
<p><code>git log --pretty=oneline</code></p>
<p>Or maybe you want to see an ASCII art tree of all the branches, decorated with the names of tags and branches:</p>
<p><code>git log --graph --oneline --decorate --all</code></p>
<p>See only which files have changed:</p>
<p><code>git log --name-status</code></p>
<p>These are just a few of the possible parameters you can use. For more, see <code>git log --help</code></p>
<h2>replace local changes</h2>
<hr />
<p>In case you did something wrong, which for sure never happens ;), you can replace local changes using the command</p>
<p><code>git checkout -- <filename></code></p>
<p>this replaces the changes in your working tree with the last content in HEAD. Changes already added to the index, as well as new files, will be kept.</p>
<p>If you instead want to drop all your local changes and commits, fetch the latest history from the server and point your local master branch at it like this</p>
<p><code>git fetch origin</code><br/>
<code>git reset --hard origin/master</code></p>
<h2>useful hints</h2>
<hr />
<p>built-in git GUI</p>
<p><code>gitk</code></p>
<p>use colorful git output</p>
<p><code>git config color.ui true</code></p>
<p>show log on just one line per commit</p>
<p><code>git config format.pretty oneline</code></p>
<p>use interactive adding</p>
<p><code>git add -i</code></p>
<h2>links & resources</h2>
<hr />
<p>graphical clients</p>
<p><a href="http://gitx.laullon.com/" title="GitX (L) (OSX, open source)">GitX (L) (OSX, open source)</a><br/>
<a href="http://www.git-tower.com/" title="Tower (OSX)">Tower (OSX)</a><br/>
<a href="http://www.sourcetreeapp.com/" title="Source Tree (OSX & Windows, free)">Source Tree (OSX & Windows, free)</a><br/>
<a href="http://mac.github.com/" title="GitHub for Mac (OSX, free)">GitHub for Mac (OSX, free)</a><br/>
<a href="https://itunes.apple.com/gb/app/gitbox/id403388357?mt=12" title="GitBox (OSX, App Store)">GitBox (OSX, App Store)</a></p>
<p>guides</p>
<p><a href="http://book.git-scm.com/" title="Git Community Book">Git Community Book</a><br/>
<a href="http://progit.org/book/" title="Pro Git">Pro Git</a><br/>
<a href="http://think-like-a-git.net/" title="Think like a git">Think like a git</a><br/>
<a href="http://help.github.com/" title="GitHub Help">GitHub Help</a><br/>
<a href="http://marklodato.github.com/visual-git-guide/index-en.html" title="A Visual Git Guide">A Visual Git Guide</a></p>
<p>get help</p>
<p><a href="http://groups.google.com/group/git-users/" title="Git User Mailing List">Git User Mailing List</a><br/>
[#git on irc.freenode.net][]</p>
markdown2015-04-02T00:00:00+00:00http://jiaxianhua.github.io/2015/04/02/markdown<h1>markdown: Syntax</h1>
<hr />
<ul>
<li><p>Overview</p>
<ul>
<li>Philosophy</li>
<li>Inline HTML</li>
<li>Automatic Escaping for Special Characters</li>
</ul>
</li>
<li><p>Block Elements</p>
<ul>
<li>Paragraphs and Line Breaks</li>
<li>Headers</li>
<li>Blockquotes</li>
<li>Lists</li>
<li>Code Blocks</li>
<li>Horizontal Rules</li>
</ul>
</li>
<li>Span Elements
<ul>
<li>Links</li>
<li>Emphasis</li>
<li>Code</li>
<li>Images</li>
</ul>
</li>
<li>Miscellaneous
<ul>
<li>Backslash Escapes</li>
<li>Automatic Links</li>
</ul>
</li>
</ul>
<h2>Block Elements</h2>
<hr />
<h3>Paragraphs and Line Breaks</h3>
<p>A blank line is any line that looks like a blank line -- a line containing nothing but spaces or tabs is considered blank.</p>
<pre><code>---
</code></pre>
<hr />
<h3>Header</h3>
<pre><code># This is an H1
## This is an H2
### This is an H3
#### This is an H4
##### This is an H5
###### This is an H6
</code></pre>
<h1>This is an H1</h1>
<h2>This is an H2</h2>
<h3>This is an H3</h3>
<h4>This is an H4</h4>
<h5>This is an H5</h5>
<h6>This is an H6</h6>
<h2>Blockquotes</h2>
<pre><code>> This is the first level of quoting.
>
> > This is nested blockquote.
>
> Back to the first level.
</code></pre>
<blockquote><p>This is the first level of quoting.</p>
<blockquote><p>This is nested blockquote.</p></blockquote>
<p>Back to the first level.</p></blockquote>
<pre><code>> ## This is a header.
>
> 1. This is the first list item.
> 2. This is the second list item.
>
> Here's some example code:
>
> return shell_exec("echo $input | $markdown_script");
</code></pre>
<blockquote><h2>This is a header.</h2>
<ol>
<li>This is the first list item.</li>
<li>This is the second list item.</li>
</ol>
<p>Here's some example code:</p>
<pre><code>return shell_exec("echo $input | $markdown_script");
</code></pre></blockquote>
<h2>Lists</h2>
<p>Markdown supports ordered (numbered) and unordered (bulleted) lists.</p>
<p>Unordered use * + -</p>
<pre><code>* red
* green
* blue
</code></pre>
<ul>
<li>red</li>
<li>green</li>
<li>blue</li>
</ul>
<p>Ordered lists use numbers followed by periods:</p>
<pre><code>1. Bird
1. McHale
1. Parish
</code></pre>
<ol>
<li>Bird</li>
<li>McHale</li>
<li>Parish</li>
</ol>
<h3>Code Blocks</h3>
<pre><code>This is a normal paragraph:
This is a code block.
</code></pre>
<p>This is a normal paragraph:</p>
<pre><code>This is a code block.
</code></pre>
<h3>Horizontal Rules</h3>
<pre><code>* * *
***
*****
- - -
---------------------------------------
</code></pre>
<hr />
<hr />
<hr />
<hr />
<hr />
<h2>Span Elements</h2>
<hr />
<h3>Links</h3>
<p>inline</p>
<pre><code>This is [an example](http://example.com/ "Title") inline link.
[This link](http://example.net/) has no title attribute.
</code></pre>
<p>This is <a href="http://example.com/" title="Title">an example</a> inline link.</p>
<p><a href="http://example.net/">This link</a> has no title attribute.</p>
<p>reference</p>
<pre><code>I get 10 times more traffic from [Google] [1] than from
[Yahoo] [2] or [MSN] [3].
[1]: http://google.com/ "Google"
[2]: http://search.yahoo.com/ "Yahoo Search"
[3]: http://search.msn.com/ "MSN Search
</code></pre>
<p>I get 10 times more traffic from <a href="http://google.com/" title="Google">Google</a> than from
<a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a> or <a href="http://search.msn.com/">MSN</a>.</p>
<h3>Emphasis</h3>
<pre><code>*single asterisks*
_single underscores_
**double asterisks**
__double underscores__
</code></pre>
<p><em>single asterisks</em></p>
<p><em>single underscores</em></p>
<p><strong>double asterisks</strong></p>
<p><strong>double underscores</strong></p>
<h3>Code</h3>
<p>To indicate a span of code, wrap it with backtick quotes (`).</p>
<pre><code>Use the `printf()` function.
</code></pre>
<p>Use the <code>printf()</code> function.</p>
<h3>Images</h3>
<pre><code>![Alt text](/path/to/img.jpg)
![Alt text](/assets/img/jiaxianhua.png "Optional title")
</code></pre>
<p><img src="/path/to/img.jpg" alt="Alt text" /></p>
<p><img src="/assets/img/jiaxianhua.png" title="Optional title" alt="Alt text" /></p>
<h2>Miscellaneous</h2>
<hr />
<h3>Automatic Links</h3>
<pre><code><http://jiaxianhua.com/>
</code></pre>
<p><a href="http://jiaxianhua.com/">http://jiaxianhua.com/</a></p>
<pre><code><address@example.com>
</code></pre>
<p><a href="mailto:address@example.com">address@example.com</a></p>
<h3>Backslash Escapes</h3>
<pre><code>\*literal asterisks\*
</code></pre>
<p>*literal asterisks*</p>
<pre><code>\ backslash
` backtick
* asterisk
_ underscore
{} curly braces
[] square brackets
() parentheses
# hash mark
+ plus sign
- minus sign (hyphen)
. dot
! exclamation mark
</code></pre>
Jekyll Introduction2011-12-29T00:00:00+00:00http://jiaxianhua.github.io/lessons/2011/12/29/jekyll-introduction<p>This Jekyll introduction will outline specifically what Jekyll is and why you would want to use it.
Directly following the intro we'll learn exactly <em>how</em> Jekyll does what it does.</p>
<h2>Overview</h2>
<h3>What is Jekyll?</h3>
<p>Jekyll is a parsing engine bundled as a ruby gem used to build static websites from
dynamic components such as templates, partials, liquid code, markdown, etc. Jekyll is known as "a simple, blog aware, static site generator".</p>
<h3>Examples</h3>
<p>This website is created with Jekyll. <a href="https://github.com/mojombo/jekyll/wiki/Sites">Other Jekyll websites</a>.</p>
<h3>What does Jekyll Do?</h3>
<p>Jekyll is a ruby gem you install on your local system.
Once there you can call <code>jekyll --server</code> on a directory and provided that directory
is setup in a way jekyll expects, it will do magic stuff like parse markdown/textile files,
compute categories, tags, permalinks, and construct your pages from layout templates and partials.</p>
<p>Once parsed, Jekyll stores the result in a self-contained static <code>_site</code> folder.
The intention here is that you can serve all contents in this folder statically from a plain static web-server.</p>
<p>You can think of Jekyll as a normalish dynamic blog but rather than parsing content, templates, and tags
on each request, Jekyll does this once <em>beforehand</em> and caches the <em>entire website</em> in a folder for serving statically.</p>
<h3>Jekyll is Not Blogging Software</h3>
<p><strong>Jekyll is a parsing engine.</strong></p>
<p>Jekyll does not come with any content nor does it have any templates or design elements.
This is a common source of confusion when getting started.
Jekyll does not come with anything you actually use or see on your website - you have to make it.</p>
<h3>Why Should I Care?</h3>
<p>Jekyll is very minimalistic and very efficient.
The most important thing to realize about Jekyll is that it creates a static representation of your website requiring only a static web-server.
Traditional dynamic blogs like Wordpress require a database and server-side code.
Heavily trafficked dynamic blogs must employ a caching layer that ultimately performs the same job Jekyll sets out to do; serve static content.</p>
<p>Therefore if you like to keep things simple and you prefer the command-line over an admin panel UI then give Jekyll a try.</p>
<p><strong>Developers like Jekyll because we can write content like we write code:</strong></p>
<ul>
<li>Ability to write content in markdown or textile in your favorite text-editor.</li>
<li>Ability to write and preview your content via localhost.</li>
<li>No internet connection required.</li>
<li>Ability to publish via git.</li>
<li>Ability to host your blog on a static web-server.</li>
<li>Ability to host freely on GitHub Pages.</li>
<li>No database required.</li>
</ul>
<h1>How Jekyll Works</h1>
<p>The following is a complete but concise outline of exactly how Jekyll works.</p>
<p>Be aware that core concepts are introduced in rapid succession without code examples.
This information is not intended to specifically teach you how to do anything, rather it
is intended to give you the <em>full picture</em> relative to what is going on in Jekyll-world.</p>
<p>Learning these core concepts should help you avoid common frustrations and ultimately
help you better understand the code examples contained throughout Jekyll-Bootstrap.</p>
<h2>Initial Setup</h2>
<p>After <a href="/index.html#start-now">installing jekyll</a> you'll need to format your website directory in a way jekyll expects.
Jekyll-bootstrap conveniently provides the base directory format.</p>
<h3>The Jekyll Application Base Format</h3>
<p>Jekyll expects your website directory to be laid out like so:</p>
<pre><code>.
|-- _config.yml
|-- _includes
|-- _layouts
| |-- default.html
| |-- post.html
|-- _posts
| |-- 2011-10-25-open-source-is-good.markdown
| |-- 2011-04-26-hello-world.markdown
|-- _site
|-- index.html
|-- assets
|-- css
|-- style.css
|-- javascripts
</code></pre>
<ul>
<li><p><strong>_config.yml</strong>
Stores configuration data.</p></li>
<li><p><strong>_includes</strong>
This folder is for partial views.</p></li>
<li><p><strong>_layouts</strong>
This folder is for the main templates your content will be inserted into.
You can have different layouts for different pages or page sections.</p></li>
<li><p><strong>_posts</strong>
This folder contains your dynamic content/posts.
the naming format is required to be <code>@YEAR-MONTH-DATE-title.MARKUP@</code>.</p></li>
<li><p><strong>_site</strong>
This is where the generated site will be placed once Jekyll is done transforming it.</p></li>
<li><p><strong>assets</strong>
This folder is not part of the standard jekyll structure.
The assets folder represents <em>any generic</em> folder you happen to create in your root directory.
Directories and files not properly formatted for jekyll will be left untouched for you to serve normally.</p></li>
</ul>
<p>(read more: <a href="https://github.com/mojombo/jekyll/wiki/Usage">https://github.com/mojombo/jekyll/wiki/Usage</a>)</p>
<h3>Jekyll Configuration</h3>
<p>Jekyll supports various configuration options that are fully outlined here:
(<a href="https://github.com/mojombo/jekyll/wiki/Configuration">https://github.com/mojombo/jekyll/wiki/Configuration</a>)</p>
<h2>Content in Jekyll</h2>
<p>Content in Jekyll is either a post or a page.
These content "objects" get inserted into one or more templates to build the final output for its respective static-page.</p>
<h3>Posts and Pages</h3>
<p>Both posts and pages should be written in markdown, textile, or HTML and may also contain Liquid templating syntax.
Both posts and pages can have meta-data assigned on a per-page basis such as title, url path, as well as arbitrary custom meta-data.</p>
<h3>Working With Posts</h3>
<p><strong>Creating a Post</strong>
Posts are created by properly formatting a file and placing it the <code>_posts</code> folder.</p>
<p><strong>Formatting</strong>
A post must have a valid filename in the form <code>YEAR-MONTH-DATE-title.MARKUP</code> and be placed in the <code>_posts</code> directory.
If the data format is invalid Jekyll will not recognize the file as a post. The date and title are automatically parsed from the filename of the post file.
Additionally, each file must have <a href="https://github.com/mojombo/jekyll/wiki/YAML-Front-Matter">YAML Front-Matter</a> prepended to its content.
YAML Front-Matter is a valid YAML syntax specifying meta-data for the given file.</p>
<p><strong>Order</strong>
Ordering is an important part of Jekyll but it is hard to specify a custom ordering strategy.
Only reverse chronological and chronological ordering is supported in Jekyll.</p>
<p>Since the date is hard-coded into the filename format, to change the order, you must change the dates in the filenames.</p>
<p><strong>Tags</strong>
Posts can have tags associated with them as part of their meta-data.
Tags may be placed on posts by providing them in the post's YAML front matter.
You have access to the post-specific tags in the templates. These tags also get added to the sitewide collection.</p>
<p><strong>Categories</strong>
Posts may be categorized by providing one or more categories in the YAML front matter.
Categories offer more significance over tags in that they can be reflected in the URL path to the given post.
Note categories in Jekyll work in a specific way.
If you define more than one category you are defining a category hierarchy "set".
Example:</p>
<pre><code>---
title : Hello World
categories : [lessons, beginner]
---
</code></pre>
<p>This defines the category hierarchy "lessons/beginner". Note this is <em>one category</em> node in Jekyll.
You won't find "lessons" and "beginner" as two separate categories unless you define them elsewhere as singular categories.</p>
<h3>Working With Pages</h3>
<p><strong>Creating a Page</strong>
Pages are created by properly formatting a file and placing it anywhere in the root directory or subdirectories that do <em>not</em> start with an underscore.</p>
<p><strong>Formatting</strong>
In order to register as a Jekyll page the file must contain <a href="https://github.com/mojombo/jekyll/wiki/YAML-Front-Matter">YAML Front-Matter</a>.
Registering a page means 1) that Jekyll will process the page and 2) that the page object will be available in the <code>site.pages</code> array for inclusion into your templates.</p>
<p><strong>Categories and Tags</strong>
Pages do not compute categories nor tags so defining them will have no effect.</p>
<p><strong>Sub-Directories</strong>
If pages are defined in sub-directories, the path to the page will be reflected in the url.
Example:</p>
<pre><code>.
|-- people
|-- bob
|-- essay.html
</code></pre>
<p>This page will be available at <code>http://yourdomain.com/people/bob/essay.html</code></p>
<p><strong>Recommended Pages</strong></p>
<ul>
<li><strong>index.html</strong>
You will always want to define the root index.html page as this will display on your root URL.</li>
<li><strong>404.html</strong>
Create a root 404.html page and GitHub Pages will serve it as your 404 response.</li>
<li><strong>sitemap.html</strong>
Generating a sitemap is good practice for SEO.</li>
<li><strong>about.html</strong>
A nice about page is easy to do and gives the human perspective to your website.</li>
</ul>
<h2>Templates in Jekyll</h2>
<p>Templates are used to contain a page's or post's content.
All templates have access to a global site object variable: <code>site</code> as well as a page object variable: <code>page</code>.
The site variable holds all accessible content and metadata relative to the site.
The page variable holds accessible data for the given page or post being rendered at that point.</p>
<p><strong>Create a Template</strong>
Templates are created by properly formatting a file and placing it in the <code>_layouts</code> directory.</p>
<p><strong>Formatting</strong>
Templates should be coded in HTML and contain YAML Front Matter.
All templates can contain Liquid code to work with your site's data.</p>
<p><strong>Rending Page/Post Content in a Template</strong>
There is a special variable in all templates named : <code>content</code>.
The <code>content</code> variable holds the page/post content including any sub-template content previously defined.
Render the content variable wherever you want your main content to be injected into your template:</p>
<p> <pre><code>...
<body>
<div id="sidebar"> ... </div>
<div id="main">
{{content}}
</div>
</body>
...</code></pre></p>
<h3>Sub-Templates</h3>
<p>Sub-templates are exactly templates with the only difference being they
define another "root" layout/template within their YAML Front Matter.
This essentially means a template will render inside of another template.</p>
<h3>Includes</h3>
<p>In Jekyll you can define include files by placing them in the <code>_includes</code> folder.
Includes are NOT templates, rather they are just code snippets that get included into templates.
In this way, you can treat the code inside includes as if it was native to the parent template.</p>
<p>Any valid template code may be used in includes.</p>
<h2>Using Liquid for Templating</h2>
<p>Templating is perhaps the most confusing and frustrating part of Jekyll.
This is mainly due to the fact that Jekyll templates must use the Liquid Templating Language.</p>
<h3>What is Liquid?</h3>
<p><a href="https://github.com/Shopify/liquid">Liquid</a> is a secure templating language developed by <a href="http://shopify.com">Shopify</a>.
Liquid is designed for end-users to be able to execute logic within template files
without imposing any security risk on the hosting server.</p>
<p>Jekyll uses Liquid to generate the post content within the final page layout structure and as the primary interface for working with
your site and post/page data.</p>
<h3>Why Do We Have to Use Liquid?</h3>
<p>GitHub uses Jekyll to power <a href="http://pages.github.com/">GitHub Pages</a>.
GitHub cannot afford to run arbitrary code on their servers so they lock developers down via Liquid.</p>
<h3>Liquid is Not Programmer-Friendly.</h3>
<p>The short story is liquid is not real code and its not intended to execute real code.
The point being you can't do jackshit in liquid that hasn't been allowed explicitly by the implementation.
What's more you can only access data-structures that have been explicitly passed to the template.</p>
<p>In Jekyll's case it is not possible to alter what is passed to Liquid without hacking the gem or running custom plugins.
Both of which cannot be supported by GitHub Pages.</p>
<p>As a programmer - this is very frustrating.</p>
<p>But rather than look a gift horse in the mouth we are going to
suck it up and view it as an opportunity to work around limitations and adopt client-side solutions when possible.</p>
<p><strong>Aside</strong>
My personal stance is to not invest time trying to hack liquid. It's really unnecessary
<em>from a programmer's</em> perspective. That is to say if you have the ability to run custom plugins (i.e. run arbitrary ruby code)
you are better off sticking with ruby. Toward that end I've built <a href="http://github.com/plusjade/mustache-with-jekyll">Mustache-with-Jekyll</a></p>
<h2>Static Assets</h2>
<p>Static assets are any file in the root or non-underscored subfolders that are not pages.
That is they have no valid YAML Front Matter and are thus not treated as Jekyll Pages.</p>
<p>Static assets should be used for images, css, and javascript files.</p>
<h2>How Jekyll Parses Files</h2>
<p>Remember Jekyll is a processing engine. There are two main types of parsing in Jekyll.</p>
<ul>
<li><strong>Content parsing.</strong>
This is done with textile or markdown.</li>
<li><strong>Template parsing.</strong>
This is done with the liquid templating language.</li>
</ul>
<p>And thus there are two main types of file formats needed for this parsing.</p>
<ul>
<li><strong>Post and Page files.</strong>
All content in Jekyll is either a post or a page so valid posts and pages are parsed with markdown or textile.</li>
<li><strong>Template files.</strong>
These files go in <code>_layouts</code> folder and contain your blogs <strong>templates</strong>. They should be made in HTML with the help of Liquid syntax.
Since include files are simply injected into templates they are essentially parsed as if they were native to the template.</li>
</ul>
<p><strong>Arbitrary files and folders.</strong>
Files that <em>are not</em> valid pages are treated as static content and pass through
Jekyll untouched and reside on your blog in the exact structure and format they originally existed in.</p>
<h3>Formatting Files for Parsing.</h3>
<p>We've outlined the need for valid formatting using <strong>YAML Front Matter</strong>.
Templates, posts, and pages all need to provide valid YAML Front Matter even if the Matter is empty.
This is the only way Jekyll knows you want the file processed.</p>
<p>YAML Front Matter must be prepended to the top of template/post/page files:</p>
<pre><code>---
layout: post
category : pages
tags : [how-to, jekyll]
---
... contents ...
</code></pre>
<p>Three hyphens on a new line start the Front-Matter block and three hyphens on a new line end the block.
The data inside the block must be valid YAML.</p>
<p>Configuration parameters for YAML Front-Matter is outlined here:
<a href="https://github.com/mojombo/jekyll/wiki/YAML-Front-Matter">A comprehensive explanation of YAML Front Matter</a></p>
<h4>Defining Layouts for Posts and Templates Parsing.</h4>
<p>The <code>layout</code> parameter in the YAML Front Matter defines the template file for which the given post or template should be injected into.
If a template file specifies its own layout, it is effectively being used as a <code>sub-template.</code>
That is to say loading a post file into a template file that refers to another template file with work in the way you'd expect; as a nested sub-template.</p>
<h2>How Jekyll Generates the Final Static Files.</h2>
<p>Ultimately, Jekyll's job is to generate a static representation of your website.
The following is an outline of how that's done:</p>
<ol>
<li><p><strong>Jekyll collects data.</strong>
Jekyll scans the posts directory and collects all posts files as post objects. It then scans the layout assets and collects those and finally scans other directories in search of pages.</p></li>
<li><p><strong>Jekyll computes data.</strong>
Jekyll takes these objects, computes metadata (permalinks, tags, categories, titles, dates) from them and constructs one
big <code>site</code> object that holds all the posts, pages, layouts, and respective metadata.
At this stage your site is one big computed ruby object.</p></li>
<li><p><strong>Jekyll liquifies posts and templates.</strong>
Next jekyll loops through each post file and converts (through markdown or textile) and <strong>liquifies</strong> the post inside of its respective layout(s).
Once the post is parsed and liquified inside the the proper layout structure, the layout itself is "liquified".
<strong>Liquification</strong> is defined as follows: Jekyll initiates a Liquid template, and passes a simpler hash representation of the ruby site object as well as a simpler
hash representation of the ruby post object. These simplified data structures are what you have access to in the templates.</p></li>
<li><p><strong>Jekyll generates output.</strong>
Finally the liquid templates are "rendered", thereby processing any liquid syntax provided in the templates
and saving the final, static representation of the file.</p></li>
</ol>
<p><strong>Notes.</strong>
Because Jekyll computes the entire site in one fell swoop, each template is given access to
a global <code>site</code> hash that contains useful data. It is this data that you'll iterate through and format
using the Liquid tags and filters in order to render it onto a given page.</p>
<p>Remember, in Jekyll you are an end-user. Your API has only two components:</p>
<ol>
<li>The manner in which you setup your directory.</li>
<li>The liquid syntax and variables passed into the liquid templates.</li>
</ol>
<p>All the data objects available to you in the templates via Liquid are outlined in the <strong>API Section</strong> of Jekyll-Bootstrap.
You can also read the original documentation here: <a href="https://github.com/mojombo/jekyll/wiki/Template-Data">https://github.com/mojombo/jekyll/wiki/Template-Data</a></p>
<h2>Conclusion</h2>
<p>I hope this paints a clearer picture of what Jekyll is doing and why it works the way it does.
As noted, our main programming constraint is the fact that our API is limited to what is accessible via Liquid and Liquid only.</p>
<p>Jekyll-bootstrap is intended to provide helper methods and strategies aimed at making it more intuitive and easier to work with Jekyll =)</p>
<p><strong>Thank you</strong> for reading this far.</p>
<h2>Next Steps</h2>
<p>Please take a look at <a href=""></a>
or jump right into <a href="">Usage</a> if you'd like.</p>