PageSourceSearch

https://xbay.github.io/

html xbay.github.io collected 2026-10-03 10:11:16 UTC 94,521 bytes, 1,439 lines download raw bytes

1<!doctype html>
2<html class="theme-next use-motion ">
3<head>
4  
5
6<meta charset="UTF-8"/>
7<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
8<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
9
10
11<meta http-equiv="Cache-Control" content="no-transform" />
12<meta http-equiv="Cache-Control" content="no-siteapp" />
13
14
15
16
17
18
19
20
21  <link rel="stylesheet" type="text/css" href="/vendors/fancybox/source/jquery.fancybox.css?v=2.1.5"/>
22
23
24
25  
26    <link href='//fonts.lug.ustc.edu.cn/css?family=Lato:300,400,700,400italic&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
27  
28
29
30<link rel="stylesheet" type="text/css" href="/vendors/font-awesome/css/font-awesome.min.css?v=4.4.0" />
31
32<link rel="stylesheet" type="text/css" href="/css/main.css?v=0.4.5.2"/>
33
34
35
36
37  <meta name="keywords" content="Hexo,next" />
38
39
40
41
42
43  <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=0.4.5.2" />
44
45
46<meta name="description">
47<meta property="og:type" content="website">
48<meta property="og:title" content="xbay">
49<meta property="og:url" content="http://xbay.github.io/index.html">
50<meta property="og:site_name" content="xbay">
51<meta property="og:description">
52<meta name="twitter:card" content="summary">
53<meta name="twitter:title" content="xbay">
54<meta name="twitter:description">
55
56
57<script type="text/javascript" id="hexo.configuration">
58  var CONFIG = {
59    scheme: '',
60    sidebar: 'post'
61  };
62</script>
62
63
64
65
66  <title> xbay </title>
67</head>
68
69<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
70
71  <!--[if lte IE 8]>
72  <div style=' clear: both; height: 59px; padding:0 0 0 15px; position: relative;margin:0 auto;'>
73    <a href="http://windows.microsoft.com/en-US/internet-explorer/products/ie/home?ocid=ie6_countdown_bannercode">
74      <img src="http://7u2nvr.com1.z0.glb.clouddn.com/picouterie.jpg" border="0" height="42" width="820"
75           alt="You are using an outdated browser. For a faster, safer browsing experience, upgrade for free today or use other browser ,like chrome firefox safari."
76           style='margin-left:auto;margin-right:auto;display: block;'/>
77    </a>
78  </div>
79<![endif]-->
80  
81
82
83
84
85
86
87  <div class="container one-column 
88   page-home 
89">
90    <div class="headband"></div>
91
92    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
93      <div class="header-inner"><div class="site-meta ">
94  
95
96  <div class="custom-logo-site-title">
97    <a href="/"  class="brand" rel="start">
98      <span class="logo-line-before"><i></i></span>
99      <span class="site-title"></span>
100      <span class="logo-line-after"><i></i></span>
101    </a>
102  </div>
103  <p class="site-subtitle"></p>
104</div>
105
106<div class="site-nav-toggle">
107  <button>
108    <span class="btn-bar"></span>
109    <span class="btn-bar"></span>
110    <span class="btn-bar"></span>
111  </button>
112</div>
113
114<nav class="site-nav">
115  
116
117  
118    <ul id="menu" class="menu ">
119      
120        
121        <li class="menu-item menu-item-home">
122          <a href="/" rel="section">
123            
124              <i class="menu-item-icon fa fa-home"></i> <br />
125            
126            首页
127          </a>
128        </li>
129      
130        
131        <li class="menu-item menu-item-archives">
132          <a href="/archives" rel="section">
133            
134              <i class="menu-item-icon fa fa-archive"></i> <br />
135            
136            归档
137          </a>
138        </li>
139      
140        
141        <li class="menu-item menu-item-tags">
142          <a href="/tags" rel="section">
143            
144              <i class="menu-item-icon fa fa-tags"></i> <br />
145            
146            标签
147          </a>
148        </li>
149      
150
151      
152      
153    </ul>
154  
155
156  
157</nav>
158 </div>
159    </header>
160
161    <main id="main" class="main">
162      <div class="main-inner">
163        <div id="content" class="content"> 
164  <section id="posts" class="posts-expand">
165    
166      
167
168  
169  
170
171  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
172
173    
174      <header class="post-header">
175
176        
177        
178          <h1 class="post-title" itemprop="name headline">
179            
180            
181              
182                <a class="post-title-link" href="/2016/04/19/shadowsocks_proxychainsNG_iterms2/" itemprop="url">
183                  ShadowSocks+ProxyChains-NG 实现终端(iTerm2)下代理
184                </a>
185              
186            
187          </h1>
188        
189
190        <div class="post-meta">
191          <span class="post-time">
192            发表于
193            <time itemprop="dateCreated" datetime="2016-04-19T08:31:15+08:00" content="2016-04-19">
194              2016-04-19
195            </time>
196          </span>
197
198          
199
200          
201          <span class="post-time">
202          &nbsp; | &nbsp;
203          
204            <span itemprop="name">mac.meng</span>
205          
206
207
208          </span>
209
210          
211
212
213          
214            
215              <span class="post-comments-count">
216              &nbsp; | &nbsp;
217              <a href="/2016/04/19/shadowsocks_proxychainsNG_iterms2/#comments" itemprop="discussionUrl">
218                <span class="post-comments-count disqus-comment-count" data-disqus-identifier="2016/04/19/shadowsocks_proxychainsNG_iterms2/" itemprop="commentsCount"></span>
219              </a>
220            </span>
221            
222          
223
224          
225
226        </div>
227      </header>
228    
229
230
231    <div class="post-body">
232
233      
234      
235
236      
237        
238          <span itemprop="articleBody">
239            
240              <p><a href="https://segmentfault.com/a/1190000004607285" target="_blank" rel="external">参考原文</a></p>
241<h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>
241使用 Homebrew 安装 proxychains-ng<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~ $ brew install proxychains-ng</span><br></pre></td></tr></table></figure></p>
242<h3 id="编辑配置文件"><a href="#编辑配置文件" class="headerlink" title="编辑配置文件"></a>编辑配置文件</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /usr/local/etc/proxychains.conf</span><br></pre></td></tr></table></figure>
243<p>在 [ProxyList] 下面(也就是末尾)加入代理类型,代理地址和端口<br>注释掉原来的代理并添加, 1080 就是默认的shadowSocks 本地代理端口号,</p>
244<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">socks5 127.0.0.1 1080</span><br></pre></td></tr></table></figure>
245<p>如果有的软件支持socks5代理, 比如 <strong>dropbox</strong> ,可以直接设置, </p>
246<blockquote>
247<p>我们在这里搞这么多飞机就是因为, iTerm2 不支持配置代理,而且就算你在系统设置了也不管用.</p>
248</blockquote>
249<p>如果所在的网络很复杂,可能需要在配置文件中启用<br>dynamic_chain - 按照列表中出现的代理服务器的先后顺序组成一条链,如果有代理服务器失效,则自动将其排除,但至少要有一个是有效的<br>然后在 [ProxyList] 下添加多个代理<br>默认:strict_chain - 按照后面列表中出现的代理服务器的先后顺序组成一条链,要求所有的代理服务器都是有效的</p>
250<h3 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h3><p>  proxychains4 curl google.com<br>注意:proxychains支持的是socks,http, https协议.它们以tcp或者udp协议为基础, ping命令用的是 ICMP 协议, proxychains 不支持;</p>
251<h3 id="优化"><a href="#优化" class="headerlink" title="优化"></a>优化</h3><p>alias 命令别名<br>直接在命令行中输入</p>
252<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~ $ alias pc=&quot;proxychains4”</span><br></pre></td></tr></table></figure>
253<p>iTerm中前缀补全<br>你输了很长一段命令,然后你突然想使用代理功能,怎么办?<br>我想你应该会复制一下,然后重写;<br>现在你可以这样做:</p>
254<p>在 iTerm -&gt; Preferences -&gt; Profiles -&gt; Keys 中,新建一个快捷键,例如 ⌥(alt) + p ,Action 选择 Send Hex Code,键值为 0x1 0x70 0x63 0x20 0xd,保存生效。这个 HexCode 用 <a href="http://www.rapidtables.com/convert/number/hex-to-ascii.htm" target="_blank" rel="external">hexCodeToAsciiText</a> 就是 “pc”两个字母</p>
255<p>以后命令要代理就直接敲命令,然后 ⌥ + p 即可,这样命令补全也能保留了。<br>比如 git clone, npm install 加速等等.</p>
256<h3 id="推荐-福利"><a href="#推荐-福利" class="headerlink" title="推荐(福利)"></a>推荐(福利)</h3><p>最后推荐一个下载视频的神器 <a href="https://github.com/soimort/you-get" target="_blank" rel="external">you-get</a>可以下载国内外主流的视频网站的视频,made by python3. 也可以配置 proxychains4使用</p>
257<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">~ $ pc you-get https://www.youtube.com/watch?v=fwoDwajwqdo</span><br></pre></td></tr></table></figure>
258<h3 id="谜团"><a href="#谜团" class="headerlink" title="谜团"></a>谜团</h3><p>关于使用shadowSocks 时,dns是不是一起走了代理了,这里面还有很多网络知识不是特别明白</p>
259
260            
261          </span>
262        
263      
264    </div>
265
266    <footer class="post-footer">
267      
268
269      
270
271      
272      
273        <div class="post-eof"></div>
274      
275    </footer>
276  </article>
277
278
279    
280      
281
282  
283  
284
285  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
286
287    
288      <header class="post-header">
289
290        
291        
292          <h1 class="post-title" itemprop="name headline">
293            
294            
295              
296                <a class="post-title-link" href="/2016/03/31/React-Native-NativeComponent/" itemprop="url">
297                  ReactNative-NativeComponent -- 1. 官方指引
298                </a>
299              
300            
301          </h1>
302        
303
304        <div class="post-meta">
305          <span class="post-time">
306            发表于
307            <time itemprop="dateCreated" datetime="2016-03-31T12:03:15+08:00" content="2016-03-31">
308              2016-03-31
309            </time>
310          </span>
311
312          
313
314          
315          <span class="post-time">
316          &nbsp; | &nbsp;
317          
318            <span itemprop="name">mac.meng</span>
319          
320
321
322          </span>
323
324          
325
326
327          
328            
329              <span class="post-comments-count">
330              &nbsp; | &nbsp;
331              <a href="/2016/03/31/React-Native-NativeComponent/#comments" itemprop="discussionUrl">
332                <span class="post-comments-count disqus-comment-count" data-disqus-identifier="2016/03/31/React-Native-NativeComponent/" itemprop="commentsCount"></span>
333              </a>
334            </span>
335            
336          
337
338          
339
340        </div>
341      </header>
342    
343
344
345    <div class="post-body">
346
347      
348      
349
350      
351        
352          <span itemprop="articleBody">
353            
354              <h2 id="Native-Module-基本分为两种"><a href="#Native-Module-基本分为两种" class="headerlink" title="Native-Module 基本分为两种"></a>Native-Module 基本分为两种</h2><ul>
355<li>可见的Module. (显示,原生动画)<br>View 本身继承自RCTView,如果想要在JS中使用,需要再实现一个 Manager</li>
356<li>不可见的Module. (数据,网络,modeling)<br>需要实现 <rctbridgemodule>delegate</rctbridgemodule></li>
357</ul>
358<p>今天我们来讲讲第二种不可见的Module</p>
359<h3 id="Module-和-method的关系"><a href="#Module-和-method的关系" class="headerlink" title="Module 和 method的关系"></a>Module 和 method的关系</h3><h4 id="One-Module"><a href="#One-Module" class="headerlink" title="One Module"></a>One Module</h4><blockquote>
360<p>-&gt; MethodA<br>-&gt; MethodB<br>…<br>-&gt; MethodN</p>
361</blockquote>
362<h3 id="官方指引"><a href="#官方指引" class="headerlink" title="官方指引"></a><a href="http://facebook.github.io/react-native/docs/native-modules-ios.html#content" target="_blank" rel="external">官方指引</a></h3><ol>
363<li>正常的参数, 所有json格式的参数<br>而且有个强大的Util RCTConvert.h 从字体,颜色,string,number都可以转换</li>
364<li>支持回调 callback, 可以传function进去<br>原生类型是 RCTResponseSenderBlock<br>和传参都是 用RCT_EXPORT_METHOD 声明</li>
365<li><p>支持Promise<br>但是需要用到 RCT_REMAP_METHOD 声明,再辅助两个block<br>RCTPromiseResolveBlock/ RCTPromiseRejectBlock<br>JS 层面 用  ES2016 <strong>async/await</strong> 去写, 这个需要看一下</p>
366</li>
367<li><p>接下来讲多线程<br>methodQueue 是在一个Module下的所有method共享的, 要么这个module是在主线程上执行,要么就是在某个线程上执行. 而且大家可以共享这个线程<br>如果我有一个方法需要单独在新的线程上执行怎么办,比如相对耗时的存储等等.我可以在方法里面 用dispatch_async包一下执行代码,执行完之后可以用 2.里面提到的callback通知外面<br>最后提到了 在不同的Module中共享线程,比较高深</p>
368</li>
369<li><p>导出常量<br>返回Dictionary的native method 在初始化的时候完成赋值,运行时的时候再改变他,js也拿不到最新的值.<br>如果要枚举常量,他建议新建一个RCTConvert的extension</p>
370</li>
371<li>向JS发送Event<br>如何使用 bridge.eventDispatcher, 如何在native send 以及如何在JS subscription. 这样Native 也可作用在JS上了,这是除了 callback的另一种实现.<br>采用的是观察者模式. 如果想用JS作用在Native上直接调取Native 用RCT_EXPORT_METHOD 暴露出来的函数就好了,各种回调,promise,sender伺候你.</li>
372</ol>
373<p>这里多数一句,我们在OC里常用的传递消息的方式有很多 KVO,Delegate,Block. 还有高逼格的NSOperation,GCD 等等.基本上RN都用了.可见facebook的原生功力还是比较深厚的</p>
374<ol>
375<li>最后提到了如何在swift 用RN,然并卵.</li>
376</ol>
377<h3 id="RN-ReactNative"><a href="#RN-ReactNative" class="headerlink" title="RN = ReactNative"></a>RN = ReactNative</h3><p>————</p>
378<h4 id="有兴趣的同学可以读一下具体-Module-和-Method-的原生实现"><a href="#有兴趣的同学可以读一下具体-Module-和-Method-的原生实现" class="headerlink" title="有兴趣的同学可以读一下具体 Module 和 Method 的原生实现"></a>有兴趣的同学可以读一下具体 Module 和 Method 的原生实现</h4><blockquote>
379<p>关于Bridge的一篇facebook的<a href="http://tadeuzagallo.com/blog/react-native-bridge/" target="_blank" rel="external">内部剖析</a><br>如何优化RNApp性能的<a href="https://code.facebook.com/posts/895897210527114/dive-into-react-native-performance/" target="_blank" rel="external">官方介绍</a></p>
380</blockquote>
381<h4 id="next"><a href="#next" class="headerlink" title="next"></a>next</h4><p>下一部分准备按照官方的指引写一个Demo 把这些点传起来</p>
382
383            
384          </span>
385        
386      
387    </div>
388
389    <footer class="post-footer">
390      
391
392      
393
394      
395      
396        <div class="post-eof"></div>
397      
398    </footer>
399  </article>
400
401
402    
403      
404
405  
406  
407
408  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
409
410    
411      <header class="post-header">
412
413        
414        
415          <h1 class="post-title" itemprop="name headline">
416            
417            
418              
419                <a class="post-title-link" href="/2016/02/26/scalaz_optionT/" itemprop="url">
420                  用scalaz的optionT让业务代码变得更简洁
421                </a>
422              
423            
424          </h1>
425        
426
427        <div class="post-meta">
428          <span class="post-time">
429            发表于
430            <time itemprop="dateCreated" datetime="2016-02-26T23:19:50+08:00" content="2016-02-26">
431              2016-02-26
432            </time>
433          </span>
434
435          
436
437          
438          <span class="post-time">
439          &nbsp; | &nbsp;
440          
441            <span itemprop="name">uni.x.bell</span>
442          
443
444
445          </span>
446
447          
448
449
450          
451            
452              <span class="post-comments-count">
453              &nbsp; | &nbsp;
454              <a href="/2016/02/26/scalaz_optionT/#comments" itemprop="discussionUrl">
455                <span class="post-comments-count disqus-comment-count" data-disqus-identifier="2016/02/26/scalaz_optionT/" itemprop="commentsCount"></span>
456              </a>
457            </span>
458            
459          
460
461          
462
463        </div>
464      </header>
465    
466
467
468    <div class="post-body">
469
470      
471      
472
473      
474        
475          <span itemprop="articleBody">
476            
477              <p>scala里使用Option类型来封装一个可能为null的引用,和Java引用相比,它避免了直接操作null引发的异常。一般来说可以用switch/case来处理Option,比如要把一个Int对象转成一个String对象:<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f</span></span>(p: <span class="type">Option</span>[<span class="type">Int</span>]): <span class="type">Option</span>[<span class="type">String</span>] = &#123;</span><br><span class="line">  p <span class="keyword">match</span> &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="type">Some</span>(i) =&gt; i.toString</span><br><span class="line">    <span class="keyword">case</span> <span class="type">None</span> =&gt; <span class="type">None</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
478<p>和Java里判断引用是否为空的代码其实是类似的,不过Option的封装保证了程序员不会忘记处理空的情况--除非程序员使用get取值,非常不推荐这样做:<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">p.get.toString</span><br></pre></td></tr></table></figure></p>
479<p>跟Java一样,这样写代码很容易收获一个空引用异常。<br>不过通过Option的map函数能把代码写得跟用get求值一样简洁,还不会引发空引用异常。<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f</span></span>(p: <span class="type">Option</span>[<span class="type">Int</span>]): <span class="type">Option</span>[<span class="type">String</span>] = p.map(_.toString)</span><br></pre></td></tr></table></figure></p>
480<p>map函数只在Option为Some()的时候调用传入的函数,也就是说,None被直接“短路”到输出了,这对于大部分情况都是适用的。</p>
481<hr>
482<p>scala里用Future来封装IO操作,代表”未来可能会获得某个值“。比如发起一个HTTP请求或者访问数据库,函数会返回一个Future,和Option有点类似,Future也有俩状态,一个是成功一个是失败。所以常用的方法也是map。<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f</span></span>(p: <span class="type">Future</span>[<span class="type">Int</span>]): <span class="type">Future</span>[<span class="type">String</span>] = p.map(_.toString)</span><br></pre></td></tr></table></figure></p>
483<p>当IO成功的时候,获得的值会被用做参数,调用map里传入的函数,失败则直接“短路”到输出。<br>我们还可以用flatMap简单的把IO“串联”起来:<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">readById</span></span>(id: <span class="type">Int</span>): <span class="type">Future</span>[<span class="type">String</span>
483] = ...</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">readByName</span></span>(name: <span class="type">String</span>): <span class="type">Future</span>[<span class="type">Result</span>] = ...</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">getResult</span></span>(p: <span class="type">Future</span>[<span class="type">Int</span>]): <span class="type">Future</span>[<span class="type">Result</span>] = p.flatMap(id =&gt; &#123;</span><br><span class="line">  readById(id).flatMap(name =&gt; &#123;</span><br><span class="line">    readByName(name)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure></p>
484<p>这么写在形式上有个弱点,类似回调函数,每次串联都会带来一层缩进,影响可读性。scala提供了for来处理这个问题,下面这段代码和上面的多层flatMap是等价的:<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">readById</span></span>(id: <span class="type">Int</span>): <span class="type">Future</span>[<span class="type">String</span>] = ...</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">readByName</span></span>(name: <span class="type">String</span>): <span class="type">Future</span>[<span class="type">Result</span>] = ...</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">getResult</span></span>(p: <span class="type">Future</span>[<span class="type">Int</span>]): <span class="type">Future</span>[<span class="type">Result</span>] = <span class="keyword">for</span> &#123;</span><br><span class="line">  id &lt;- p</span><br><span class="line">  name &lt;- readById(id)</span><br><span class="line">  result &lt;- readByName(name)</span><br><span class="line">&#125; <span class="keyword">yield</span>(result)</span><br></pre></td></tr></table></figure></p>
485<hr>
486<p>不过真实世界里的代码会稍微复杂一些,我们经常会混用Future和Option,最常见的是IO返回的对象里,某些属性可能为空,我们用Option来表示这些属性的话,意味着代码逻辑里会出现Future[Option[T]]这样的类型。我们当然可以在Future的map/flatMap里用match/case来写逻辑,其实也比较清晰,但是缩进的层数会比较多,影响可读性。来看一段实际的例子,这是从真实世界的业务代码里拷贝/粘贴出来的:<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cityCodeFriendly</span></span>(term: <span class="type">Option</span>[<span class="type">String</span>]): <span class="type">Future</span>[<span class="type">Option</span>[<span class="type">JsValue</span>]] = &#123;</span><br><span class="line">  term <span class="keyword">match</span> &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="type">Some</span>(cityCode) =&gt; &#123;</span><br><span class="line">
486      <span class="type">AreaService</span>.findOneByCode(cityCode) flatMap &#123; cityOption =&gt;</span><br><span class="line">        cityOption <span class="keyword">match</span> &#123;</span><br><span class="line">          <span class="keyword">case</span> <span class="type">Some</span>(city) =&gt; &#123;</span><br><span class="line">            <span class="type">AreaService</span>.findOneByCode(city.parentCode.getOrElse(<span class="string">""</span>)) map &#123; provinceOption =&gt;</span><br><span class="line">              provinceOption <span class="keyword">match</span> &#123;</span><br><span class="line">                <span class="keyword">case</span> <span class="type">Some</span>(province) =&gt; &#123;</span><br><span class="line">                  <span class="type">Some</span>(<span class="type">JsObject</span>(<span class="type">Seq</span>(<span class="string">"provinceName"</span> -&gt; <span class="type">JsString</span>(province.name.getOrElse(<span class="string">""</span>)),</span><br><span class="line">                    <span class="string">"cityName"</span> -&gt; <span class="type">JsString</span>(city.name.getOrElse(<span class="string">""</span>)),</span><br><span class="line">                    <span class="string">"cityCode"</span> -&gt; <span class="type">JsString</span>(cityCode))))</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">case</span> <span class="type">None</span> =&gt; <span class="type">None</span></span><br><span class="line">              &#125;</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;</span><br><span class="line">          <span class="keyword">case</span> <span class="type">None</span> =&gt; <span class="type">Future</span>(<span class="type">None</span>)</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">case</span> <span class="type">None</span> =&gt; <span class="type">Future</span>(<span class="type">None</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
487<p>代码逻辑还是比较清晰的:传入一个城市id(可能为空),然后从数据库里依次读取城市的名称和所属省份的名称,最后把结果组装成一个json对象输出,只要中间有一步读到空值,就输出一个空的json对象。代码风格看起来有两个问题,一是缩进太多,第二是case None =&gt; None看起来很多余。<br>试试用Option的flatMap来改造一下,得到如下的代码:<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cityCodeFriendly</span></span>(term: <span class="type">Option</span>[<span class="type">String</span>]): <span class="type">Future</span>[<span class="type">Option</span>[<span class="type">JsValue</span>]] = &#123;</span><br><span class="line">  term.map(cityCode =&gt; &#123;</span><br><span class="line">    <span class="type">AreaService</span></span><br><span class="line">    .findOneByCode(cityCode)</span><br><span class="line">    .flatMap(cityOption =&gt; &#123;</span><br><span class="line">      cityOption.map(city =&gt; &#123;</span><br><span class="line">        city.parentCode.map(parentCode =&gt; &#123;</span><br><span class="line">
487          <span class="type">AreaService</span>.findOneByCode(parentCode).map(provinceOption =&gt; &#123;</span><br><span class="line">            provinceOption.map(province =&gt;</span><br><span class="line">              <span class="type">JsObject</span>(<span class="type">Seq</span>(<span class="string">"provinceName"</span> -&gt; <span class="type">JsString</span>(province.name.getOrElse(<span class="string">""</span>)),</span><br><span class="line">                <span class="string">"cityName"</span> -&gt; <span class="type">JsString</span>(city.name.getOrElse(<span class="string">""</span>)),</span><br><span class="line">                <span class="string">"cityCode"</span> -&gt; <span class="type">JsString</span>(cityCode))))</span><br><span class="line">          &#125;)</span><br><span class="line">        &#125;).getOrElse(<span class="type">Future</span>(<span class="type">None</span>))</span><br><span class="line">      &#125;).getOrElse(<span class="type">Future</span>(<span class="type">None</span>))</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;).getOrElse(<span class="type">Future</span>(<span class="type">None</span>))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
488<p>看起来更糟糕了,Future的flatMap只能和Future级联,Option的flatMap只能和Option的级联,所以两者都用flatMap其实并无好处,反而需要用.getOrElse(Future(None))来处理分支的情形,比switch/case更不清晰,缩进只是略少,总体来说得不偿失。<br>因为Future和Option的flatMap互相不能级联,所以我们没发用for把它们串联起来,看看flatMap写法里的getOrElse就知道了。<br>但是从逻辑上来说,应该存在一种方法,能把Future和Option串起来,中间只要有Future.failure就短路到Future.failure,只要有Future(None)就短路到Future(None),这是一个很机械的逻辑,编译器应该能处理。<br>scalaz提供的optionT函数,就能做到这一点。改造后实现看起来清爽多了:<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">cityCodeFriendly</span></span>(term: <span class="type">Option</span>[<span class="type">String</span>]): <span class="type">Future</span>[<span class="type">Option</span>[<span class="type">JsValue</span>]] = &#123;</span><br><span class="line">  <span class="function"><span class="keyword">def</span> <span class="title">wrapCityCode</span></span>: <span class="type">Future</span>[<span class="type">Option</span>[<span class="type">String</span>]] = <span class="type">Future</span>(term)</span><br><span class="line">  <span class="function"><span class="keyword">def</span> <span class="title">wrapJsObject</span></span>(province: <span class="type">Area</span>, city:<span class="type">Area</span>, cityCode: <span class="type">String</span>): <span class="type">Future</span>[<span class="type">Option</span>[<span class="type">JsValue</span>]] = </span><br><span class="line">    <span class="type">Future</span>(<span class="type">Some</span>(<span class="type">JsObject</span>(<span class="type">Seq</span>(</span><br><span class="line">      <span class="string">"provinceName"</span> -&gt; <span class="type">JsString</span>(province.name.getOrElse(<span class="string">""</span>)),</span><br><span class="line">      <span class="string">"cityName"</span> -&gt; <span class="type">JsString</span>(city.name.getOrElse(<span class="string">""</span>)),</span><br><span class="line">
488      <span class="string">"cityCode"</span> -&gt; <span class="type">JsString</span>(cityCode)))))</span><br><span class="line">      </span><br><span class="line">  (<span class="keyword">for</span> &#123;</span><br><span class="line">    cityCode &lt;- optionT(wrapCityCode)</span><br><span class="line">    city &lt;- optionT(<span class="type">AreaService</span>.findOneByCode(cityCode))</span><br><span class="line">    parentCode &lt;- optionT(<span class="type">Future</span>(city.parentCode))</span><br><span class="line">    province &lt;- optionT(<span class="type">AreaService</span>.findOneByCode(parentCode))</span><br><span class="line">    res &lt;- optionT(wrapJsObject(province, city, cityCode))</span><br><span class="line">  &#125; <span class="keyword">yield</span> res).run</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
489<p>optionT函数接收一个Future[Option[T]]类型,返回一个封装好的OptionT类型,OptionT类型相当于“混合”了Future和Option的特性,它提供了map/flatMap函数,只针对成功并有值的情况调用传入的函数,否则直接“短路”到输出。这样的写法不但让ä
489»£ç å˜å¾—简洁(平板的结构,很少的缩进),而且在编写过程中不需要再去关注错误处理(不需要再加入case None =&gt; None 或者 getOrElse之类的玩意了)。</p>
490
491            
492          </span>
493        
494      
495    </div>
496
497    <footer class="post-footer">
498      
499
500      
501
502      
503      
504        <div class="post-eof"></div>
505      
506    </footer>
507  </article>
508
509
510    
511      
512
513  
514  
515
516  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
517
518    
519      <header class="post-header">
520
521        
522        
523          <h1 class="post-title" itemprop="name headline">
524            
525            
526              
527                <a class="post-title-link" href="/2016/02/26/sortInScala/" itemprop="url">
528                  基本排序算法的Scala实现
529                </a>
530              
531            
532          </h1>
533        
534
535        <div class="post-meta">
536          <span class="post-time">
537            发表于
538            <time itemprop="dateCreated" datetime="2016-02-26T13:26:00+08:00" content="2016-02-26">
539              2016-02-26
540            </time>
541          </span>
542
543          
544
545          
546          <span class="post-time">
547          &nbsp; | &nbsp;
548          
549            <span itemprop="name">hiddenlotus</span>
550          
551
552
553          </span>
554
555          
556
557
558          
559            
560              <span class="post-comments-count">
561              &nbsp; | &nbsp;
562              <a href="/2016/02/26/sortInScala/#comments" itemprop="discussionUrl">
563                <span class="post-comments-count disqus-comment-count" data-disqus-identifier="2016/02/26/sortInScala/" itemprop="commentsCount"></span>
564              </a>
565            </span>
566            
567          
568
569          
570
571        </div>
572      </header>
573    
574
575
576    <div class="post-body">
577
578      
579      
580
581      
582        
583          <span itemprop="articleBody">
584            
585              <p>排序算法是算法的基础部分。<br>本文讲用函数式的方法,用Scala实现4种常用的排序算法。<br>由于是用函数式的方式,所以使用List代替Array。下文中统一由“列表”代替。</p>
586<h2 id="选择排序"><a href="#选择排序" class="headerlink" title="选择排序"></a>选择排序</h2><p>从一个列表中选出最小或最大的元素,插入到列表头或列表尾。<br>循环一次,对列表中的每个元素都做同样的处理。<br>平均时间复杂度为N的平方,N为列表的长度。<br><img src="/images/selection.png" style="width: 250px"><br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">selectionSort</span></span>[<span class="type">T</span> &lt;% <span class="type">Ordered</span>[<span class="type">T</span>]](list: <span class="type">List</span>[<span class="type">T</span>]): <span class="type">List</span>[<span class="type">T</span>] = &#123;</span><br><span class="line">  <span class="function"><span class="keyword">def</span> <span class="title">remove</span></span>(e: <span class="type">T</span>, list: <span class="type">List</span>[<span class="type">T</span>]): <span class="type">List</span>[<span class="type">T</span>] =</span><br><span class="line">
586    list <span class="keyword">match</span> &#123;</span><br><span class="line">      <span class="keyword">case</span> <span class="type">Nil</span> =&gt; <span class="type">Nil</span></span><br><span class="line">      <span class="keyword">case</span> x :: xs =&gt; <span class="keyword">if</span> (x == e) xs <span class="keyword">else</span> x :: remove(e, xs)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">  list <span class="keyword">match</span> &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="type">Nil</span> =&gt; <span class="type">Nil</span></span><br><span class="line">    <span class="keyword">case</span> _ =&gt;</span><br><span class="line">      <span class="keyword">val</span> min = list.min</span><br><span class="line">      min :: selectionSort(remove(min, list))</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
587<h2 id="插入排序"><a href="#插入排序" class="headerlink" title="插入排序"></a>插入排序</h2><p>像平时我们打扑克时抓牌,并依次按照顺序插入到列表中相应的位置。<br>平均时间复杂度为N的平方。<br><img src="/images/insertion.png" style="width: 250px"><br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">
587insertionSort</span></span>[<span class="type">T</span> &lt;% <span class="type">Ordered</span>[<span class="type">T</span>]](list: <span class="type">List</span>[<span class="type">T</span>]): <span class="type">List</span>[<span class="type">T</span>] = &#123;</span><br><span class="line">  <span class="function"><span class="keyword">def</span> <span class="title">insert</span></span>(e: <span class="type">T</span>, list: <span class="type">List</span>[<span class="type">T</span>]): <span class="type">List</span>[<span class="type">T</span>] =</span><br><span class="line">    list <span class="keyword">match</span> &#123;</span><br><span class="line">      <span class="keyword">case</span> <span class="type">Nil</span> =&gt; <span class="type">List</span>(e)</span><br><span class="line">      <span class="keyword">case</span> x :: xs =&gt; <span class="keyword">if</span> (x &lt; e) x :: insert(e, xs) <span class="keyword">else</span> e :: list</span><br><span class="line">    &#125;</span><br><span class="line">  list.foldLeft(<span class="type">List</span>.empty[<span class="type">T</span>])((res, init) =&gt; insert(init, res))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
588<h2 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h2><p>递归的将列表分成长度更小的列表。当每个子列表的长度小于等于1时,将列表们归并在一起。<br>平均时间复杂度为NlogN。<br><img src="/images/merge.png" style="width: 250px"><br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">mergeSort</span></span>[<span class="type">T</span> &lt;% <span class="type">Ordered</span>[<span class="type">T</span>]](list: <span class="type">List</span>[<span class="type">T</span>]): <span class="type">List</span>[<span class="type">T</span>] = &#123;</span><br><span class="line">  <span class="function"><span class="keyword">def</span> <span class="title">merge</span></span>(l1: <span class="type">List</span>[<span class="type">T</span>], l2: <span class="type">List</span>[<span class="type">T</span>]): <span class="type">List</span>[<span class="type">T</span>] =</span><br><span class="line">    (l1, l2) <span class="keyword">match</span> &#123;</span><br><span class="line">      <span class="keyword">case</span> (<span class="type">Nil</span>, l) =&gt; l</span><br><span class="line">      <span class="keyword">case</span> (l, <span class="type">Nil</span>) =&gt; l</span><br><span class="line">      <span class="keyword">case</span> (x :: xs, y :: ys) =&gt;</span><br><span class="line">        <span class="keyword">if</span> (x &lt; y) x :: merge(xs, l2)</span><br><span class="line">        <span class="keyword">else</span> y :: merge(l1, ys)</span><br><span class="line">    &#125;</span><br><span class="line">  <span class="keyword">val</span> n = list.length / <span class="number">2</span></span><br><span class="line">  <span class="keyword">if</span> (n == <span class="number">0</span>) list</span><br><span class="line">  <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">val</span> (ys, zs) = list.splitAt(n)</span><br><span class="line">    merge(mergeSort(ys), mergeSort(zs))</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
589<h2 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h2><p>从一个列表中随机选出一个元素作为基准,分别在过滤出小于它的元素,大于等于它的元素,<br>递归的对这些列表调用该算法,最后将列表合并。<br>这里并没有随机从列表中取元素,只取第一个元素作为基准元素。<br>平均时间复杂度为NlogN。<br><img src="/images/quick.png" style="width: 250px"><br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">
589quickSort</span></span>[<span class="type">T</span> &lt;% <span class="type">Ordered</span>[<span class="type">T</span>]](list: <span class="type">List</span>[<span class="type">T</span>]): <span class="type">List</span>[<span class="type">T</span>] =</span><br><span class="line">  list <span class="keyword">match</span> &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="type">Nil</span> =&gt; <span class="type">Nil</span></span><br><span class="line">    <span class="keyword">case</span> x :: _ =&gt;</span><br><span class="line">      <span class="keyword">val</span> (smaller, eqs, bigger) = list.foldLeft((<span class="type">List</span>.empty[<span class="type">T</span>], <span class="type">List</span>.empty[<span class="type">T</span>], <span class="type">List</span>.empty[<span class="type">T</span>])) &#123;</span><br><span class="line">        (res, init) =&gt;</span><br><span class="line">        <span class="keyword">if</span> (init &lt; x) (init :: res._1, res._2, res._3)</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (init == x) (res._1, init :: res._2, res._3)</span><br><span class="line">        <span class="keyword">else</span> (res._1, res._2, init :: res._3)</span><br><span class="line">      &#125;</span><br><span class="line">      quickSort(smaller) ::: eqs ::: quickSort(bigger)</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></p>
590<p>注1:图片来源于网络<br>注2:<code>T &lt;% Ordered[T]</code>为View Bound,表示“I can use any T, so long as T can be treated as an Ordered[T].”</p>
591
592            
593          </span>
594        
595      
596    </div>
597
598    <footer class="post-footer">
599      
600
601      
602
603      
604      
605        <div class="post-eof"></div>
606      
607    </footer>
608  </article>
609
610
611    
612      
613
614  
615  
616
617  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
618
619    
620      <header class="post-header">
621
622        
623        
624          <h1 class="post-title" itemprop="name headline">
625            
626            
627              
628                <a class="post-title-link" href="/2016/01/27/ASDK/" itemprop="url">
629                  ASDK基本原理
630                </a>
631              
632            
633          </h1>
634        
635
636        <div class="post-meta">
637          <span class="post-time">
638            发表于
639            <time itemprop="dateCreated" datetime="2016-01-27T18:15:50+08:00" content="2016-01-27">
640              2016-01-27
641            </time>
642          </span>
643
644          
645
646          
647          <span class="post-time">
648          &nbsp; | &nbsp;
649          
650            <span itemprop="name">raven.zhang</span>
651          
652
653
654          </span>
655
656          
657
658
659          
660            
661              <span class="post-comments-count">
662              &nbsp; | &nbsp;
663              <a href="/2016/01/27/ASDK/#comments" itemprop="discussionUrl">
664                <span class="post-comments-count disqus-comment-count" data-disqus-identifier="2016/01/27/ASDK/" itemprop="commentsCount"></span>
665              </a>
666            </span>
667            
668          
669
670          
671
672        </div>
673      </header>
674    
675
676
677    <div class="post-body">
678
679      
680      
681
682      
683        
684          <span itemprop="articleBody">
685            
686              <p>AsyncDisplayKit是Facebook开源的一套用于提高iOS界面流畅的异步绘制UI的框架。其核心思想是对CPU以及GPU的优化,CPU方面则是在将一些高消耗运算放在后台线程中进行,尽量减轻主线程负担;GPU方面则是将每一帧的内容渲染成一张texture,这个工作可以在后台线程中完成。</p>
687<p>AsyncDisplayKit的基本单元是node,Node是UIView以及对应Layer的抽象层。与UIView的最大区别在于,Node是线程安全的,并可以设置对应的Node内部层次后在后台线程中运行,而UIView只能将大量的耗时操作全都放在主线程上进行,下面是AsyncDisplayKit和UIKit的一些类对应关系。</p>
688<p>ASDisplayNode. Counterpart to UIView — subclass to make custom nodes.<br>
688ASControlNode. Analogous to UIControl — subclass to make buttons.<br>ASImageNode. Like UIImageView — decodes images asynchronously.<br>ASTextNode. Like UITextView — built on TextKit with full-featured rich text support.<br>ASTableView and ASCollectionView. UITableView and UICollectionView subclasses that support nodes.</p>
689<p>ASDisplayNode是线程安全的,它可以在后台线程创建和修改。Node刚创建时,并不会在内部创建UIView和CALayer,直到第一次在主线程访问view或者layer属性时,它才会在内部生成对应的对象。当它的属性(比如frame/transform)改变后,它并不会立刻同步到其持有的view或layer去,而是把被改变的属性保存到内部的一个中间变量,稍后在需要时,再通过某个机制一次性设置到内部的view或layer。实际的绘制代码在:ASDisplayNode和ASDisplayNode+AsyncDisplay.mm。</p>
690<p>从主线程中剥离出来的耗时运算比较重要的就是图像、布局还有绘制。<br>图像(解压与处理):一般UIImage对其内部图像格式的解压发生在即将将图片交给GPU渲染的时候。从API上来看,一般我们调用[UIImage drawInRect]函数或者将UIView可见(放置于window上)的时候,UIImage会将其内部图像格式如PNG,JPEG进行解压。AsyncDisplayKit就是采用后面那个方式将UIView预先放置入一个frame为空得workingview中以达到将view内部的image进行预解压的目的。再说图像的处理。一般图像需要一些blend运算或者图像需要strech或crop,这些工作其实可以留在GPU中进行快速计算,但因为UIKit并没有提供此类的API,所以我们一般都是通过CoreGraphic来做,但CoreGraphic是CPU drawing比较费时。AsyncDisplayKit将这些图像处理放在工作线程中来处理,虽然也是CPU drawing,但不会影响到UI得顺滑响应。</p>
691<p>布局:以AsyncDisplayKit的ASTableView为例,TableView的UI布局需要计算每行的高度,然后计算行内元素的布局,将行插入到TableView中,同时TableView又是scrollview,需要上下滑动。一旦行的生成和渲染比较慢,必然影响到滑动时的流畅体验。在这个过程中只有将行插入到TableView中需要在UI线程中执行。AsyncDisplayKit在子线程中分批次计算行(row)以及其子元素的布局,计算好后通过dispatch_group_notify通知UI线程将row插入到view中。AsyncDisplayKit有一个比较细腻的方式是考虑到设备的CPU核数,根据核数来分批次计算row的大小。</p>
692<p>绘制:AsyncDisplayKit另一个强大之处在于将UI CALayer的backing image的绘制放入到工作线程。我们知道UIView的真正展现内容在于其CALayer的contents属性,而contents属性对应一个Backing image,我们可以将其理解成一个内存位图。默认情况下UIView一旦需要展现,其会自动创建一个Backing image,但我们也可以通过CALayer的delegate方式来定制这个Backing image。AsyncDisplayKit就是通过CALayer的delegate控制backing image的生成,并且通过Core Graphic的方式在工作线程上将View以及其子节点绘制到backing image上,这些绘制工作会根据UIView的层次构建一个绘制数组统一执行,绘制好之后在UI线程上将这个backing image传给CALayer的contents,最后将CALayer的渲染树交给GPU进行渲染。虽然这个过程中主要依赖于CoreGraphic来进行绘制,但因为都在后台,而且绘制以组的方式执行减少了graphic context的切换,对于UI性能和顺滑性没有什么影响。而且他通过transaction的方式管理dispatch_group之间的关系,并且只有在UI线程处于idle状态或将退出时才将transaction commit并将backing image赋给CALayer的contents。除了通过CAlayer的backing image绘制,AsyncDisplayKit还提供UIView的drawRect绘制以及UIView的rasterize。两者都会使用offscreen drawing,但后者会将UIView以及所有子节点都绘制在一个backing image上。</p>
693<p>核心技术:Runloop 任务分发<br>iOS 的显示系统是由 VSync 信号驱动的,VSync 信号由硬件时钟生成,每秒钟发出 60 次(这个值取决设备硬件,比如 iPhone 真机上通常是 59.97)。iOS 图形服务接收到 VSync 信号后ï¼
693Œä¼šé€šè¿‡ IPC 通知到 App 内。App 的 Runloop 在启动后会注册对应的 CFRunLoopSource 通过 mach_port 接收传过来的时钟信号通知,随后 Source 的回调会驱动整个 App 的动画与显示。</p>
694<p>Core Animation 在 RunLoop 中注册了一个 Observer,监听了 BeforeWaiting 和 Exit 事件。这个 Observer 的优先级是 2000000,低于常见的其他 Observer。当一个触摸事件到来时,RunLoop 被唤醒,App 中的代码会执行一些操作,比如创建和调整视图层级、设置 UIView 的 frame、修改 CALayer 的透明度、为视图添加一个动画;这些操作最终都会被 CALayer 捕获,并通过 CATransaction 提交到一个中间状态去(CATransaction 的文档略有提到这些内容,但并不完整)。当上面所有操作结束后,RunLoop 即将进入休眠(或者退出)时,关注该事件的 Observer 都会得到通知。这时 CA 注册的那个 Observer 就会在回调中,把所有的中间状态合并提交到 GPU 去显示;如果此处有动画,CA 会通过 DisplayLink 等机制多次触发相关流程。</p>
695<p>ASDK 在此处模拟了 Core Animation 的这个机制:所有针对 ASNode 的修改和提交,总有些任务是必需放入主线程执行的。当出现这种任务时,ASNode 会把任务用 ASAsyncTransaction(Group) 封装并提交到一个全局的容器去。ASDK 也在 RunLoop 中注册了一个 Observer,监视的事件和 CA 一样,但优先级比 CA 要低。当 RunLoop 进入休眠前、CA 处理完事件后,ASDK 就会执行该 loop 内提交的所有任务。具体代码见这个文件:ASAsyncTransactionGroup。</p>
696<p>以上是前两周看的一些原理性的东西,代码太多太复杂还没研究懂~,总之ASDK就是能实现把异步、并发的操作同步到主线程去,并且获得不错的性能。只是这个库是有点重,使用需谨慎,要用就得用一套node的东西。如果没有特别多图片处理或者富文本编辑的话也体现不出它的优势。</p>
697
698            
699          </span>
700        
701      
702    </div>
703
704    <footer class="post-footer">
705      
706
707      
708
709      
710      
711        <div class="post-eof"></div>
712      
713    </footer>
714  </article>
715
716
717    
718      
719
720  
721  
722
723  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
724
725    
726      <header class="post-header">
727
728        
729        
730          <h1 class="post-title" itemprop="name headline">
731            
732            
733              
734                <a class="post-title-link" href="/2016/01/13/symbol_es6/" itemprop="url">
735                  关于ES6 Symbol类型的小帖士
736                </a>
737              
738            
739          </h1>
740        
741
742        <div class="post-meta">
743          <span class="post-time">
744            发表于
745            <time itemprop="dateCreated" datetime="2016-01-13T16:54:50+08:00" content="2016-01-13">
746              2016-01-13
747            </time>
748          </span>
749
750          
751
752          
753          <span class="post-time">
754          &nbsp; | &nbsp;
755          
756            <span itemprop="name">uni.bell</span>
757          
758
759
760          </span>
761
762          
763
764
765          
766            
767              <span class="post-comments-count">
768              &nbsp; | &nbsp;
769              <a href="/2016/01/13/symbol_es6/#comments" itemprop="discussionUrl">
770                <span class="post-comments-count disqus-comment-count" data-disqus-identifier="2016/01/13/symbol_es6/" itemprop="commentsCount"></span>
771              </a>
772            </span>
773            
774          
775
776          
777
778        </div>
779      </header>
780    
781
782
783    <div class="post-body">
784
785      
786      
787
788      
789        
790          <span itemprop="articleBody">
791            
792              <p>先来看看Symbol函数的定义:<br><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">Symbol</span><span class="params">(description?)</span></span> → symbol</span><br></pre></td></tr></table></figure></p>
793<p>接收一个描述(描述可以为undefined),返回一个symbol类型的变量。<br>然后有意思的来了,首先传进去的参数只是个描述,给读代码的人看的,和返回值无关,Symbol函数每次都返回一个不一样的symbol类型变量,像这样:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&gt; <span class="built_in">Symbol</span>() === <span class="built_in">Symbol</span>()</span><br><span class="line"><span class="literal">
793false</span></span><br><span class="line"></span><br><span class="line">&gt; <span class="built_in">Symbol</span>(<span class="string">"a"</span>) === <span class="built_in">Symbol</span>(<span class="string">"a"</span>)</span><br><span class="line"><span class="literal">false</span></span><br><span class="line"></span><br><span class="line">&gt; <span class="keyword">let</span> a = <span class="built_in">Symbol</span>(<span class="string">"this is a"</span>)</span><br><span class="line">&gt; a === a </span><br><span class="line"><span class="literal">true</span></span><br></pre></td></tr></table></figure></p>
794<p>不过要注意的是,symbol类型虽然是一个基本类型,但是一般来说不被json格式支持,不要试图在json格式里使用它,像这样([k]的含义是用k的内容作为对象键-值对的键而不是用k这个字符串作为键):<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&gt; <span class="keyword">let</span> k = <span class="built_in">Symbol</span>(<span class="string">"key"</span>)</span><br><span class="line">&gt; <span class="keyword">let</span> obj = &#123;[k]: <span class="number">5</span>&#125;</span><br><span class="line">&gt; <span class="built_in">JSON</span>.stringify(obj)</span><br><span class="line">&#123;&#125;</span><br></pre></td></tr></table></figure></p>
795<p>这导致了nodejs的REPL打印对象的时候会漏掉以symbole作为键的内容,像这样:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&gt; <span class="keyword">let</span> k = <span class="built_in">Symbol</span>(<span class="string">"key"</span>)</span><br><span class="line">&gt; <span class="keyword">let</span> obj = &#123;[k]: <span class="number">5</span>, d: <span class="number">6</span>&#125;</span><br><span class="line">&gt; obj</span><br><span class="line">&#123; d: <span class="number">6</span> &#125;</span><br></pre></td></tr></table></figure></p>
796<p>不过直接访问是没有问题的:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&gt; <span class="keyword">let</span> k = <span class="built_in">Symbol</span>(<span class="string">"key"</span>)</span><br><span class="line">&gt; <span class="keyword">let</span> obj = &#123;[k]: <span class="number">5</span>, d: <span class="number">6</span>&#125;</span><br><span class="line">&gt; obj[k]</span><br><span class="line"><span class="number">5</span></span><br></pre></td></tr></table></figure></p>
797<p>所以Symbol最大的用处是用来做常量定义,相比:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> ERROR = <span class="string">"error"</span></span><br></pre></td></tr></table></figure></p>
798<p>使用Symbol<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> ERROR = <span class="built_in">Symbol</span>(<span class="string">"error"</span>)</span><br></pre></td></tr></table></figure></p>
799<p>会更好一些,比如你有两个包log和print,它们的ERROR常量其实不是一回事,用Symbol就会很清晰:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&gt; log.ERROR === print.ERROR</span><br><span class="line"><span class="literal">
799false</span></span><br></pre></td></tr></table></figure></p>
800
801            
802          </span>
803        
804      
805    </div>
806
807    <footer class="post-footer">
808      
809
810      
811
812      
813      
814        <div class="post-eof"></div>
815      
816    </footer>
817  </article>
818
819
820    
821      
822
823  
824  
825
826  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
827
828    
829      <header class="post-header">
830
831        
832        
833          <h1 class="post-title" itemprop="name headline">
834            
835            
836              
837                <a class="post-title-link" href="/2015/12/31/锁的开销/" itemprop="url">
838                  锁的开销
839                </a>
840              
841            
842          </h1>
843        
844
845        <div class="post-meta">
846          <span class="post-time">
847            发表于
848            <time itemprop="dateCreated" datetime="2015-12-31T17:43:50+08:00" content="2015-12-31">
849              2015-12-31
850            </time>
851          </span>
852
853          
854
855          
856          <span class="post-time">
857          &nbsp; | &nbsp;
858          
859            <span itemprop="name">Uni.Bell</span>
860          
861
862
863          </span>
864
865          
866
867
868          
869            
870              <span class="post-comments-count">
871              &nbsp; | &nbsp;
872              <a href="/2015/12/31/锁的开销/#comments" itemprop="discussionUrl">
873                <span class="post-comments-count disqus-comment-count" data-disqus-identifier="2015/12/31/锁的开销/" itemprop="commentsCount"></span>
874              </a>
875            </span>
876            
877          
878
879          
880
881        </div>
882      </header>
883    
884
885
886    <div class="post-body">
887
888      
889      
890
891      
892        
893          <span itemprop="articleBody">
894            
895              <p>我在学习多线程编程的时候,得到的第一条关于性能忠告是锁的开销很大。由此引发了三个问题:有多大,为什么以及如何尽量避免。</p>
896<hr>
897<p>在计算机里,“很大”实在是一个太过于模糊的概念。比如同样是函数调用,在一个移动APP的大部分地方,我们会鼓励尽量把大的函数拆分以便于阅读和理解代码,而如果这个APP包含一个视频codec,里边为了效率会尽量避免在循环里做函数调用,甚至会把循环展开减少跳转的次数来优化处理器流水线。所以笼统的说某个操作“开销很大”没太大的意义,只有比较精确的测量出实际的开销有多大,我们才能决定使用的方式和优化机制。</p>
898<h2 id="锁开销的测量"><a href="#锁开销的测量" class="headerlink" title="锁开销的测量"></a>锁开销的测量</h2><p>我们针对的是多线程环境下的锁机制,基于linux做测试。每种编程语言提供的锁机制都不太一样,不过无论如何,最终都会落实到两种机制上,一是处理器提供的原子操作指令(现在一般是CAS—compare and swap),处理器会用轮询的方式试图获得锁,在处理器(包括多核)架构里这是必不可少的机制;二是内核提供的锁系统调用,在被锁住的时候会把当前线程置于睡眠(阻塞)状态。</p>
899<p>实际上我们在编程的时候并不会直接调用这两种机制,而是使用编程语言所带函数库里的锁方法,锁方法内部混合使用这两种机制。以pthread库(NPTL)的pthread_mutex来举例,一把锁本质上只是一个int类型的变量,占用4个字节内存并且内存边界按4字节对齐。加锁的时候先用trylock方法(内部使用的是CAS指令)来尝试获得锁,如果无法获得锁,则调用系统调用sys_futex来试图获得锁,这时候如果还不能获得锁,当前线程就会被阻塞。</p>
900<p>java之类的语言会使用看起来和pthread_mutex很不一样的锁机制(比如synchronise),但是实际上底层还是通过pthread_mutex的方法来加锁,或者是混合使用CAS和sys_futex—和pthread_mutex差不多。</p>
901<p>所以很容易得到一个结论,如果锁不存在冲突,每次获得锁和释放锁的处理器开销仅仅是CAS指令的开销,在x86-64处理器上,这个开销只比一次内存访问(无cac
901he)高一点(大概是1.3倍)。以我现在在使用的MacBoolPro为例,内存的规格是DDR3-1600,实际的时钟频率为800MHz,每个时钟周期1.25ns;突发的内存访问存在8个时钟周期的延迟(这个延迟的缩写也是CAS,参考<a href="https://en.wikipedia.org/wiki/CAS_latency" target="_blank" rel="external">https://en.wikipedia.org/wiki/CAS_latency</a>),也就是10ns的内存访问延迟,这样算下来,CAS指令的开销大改是十多个ns。</p>
902<p>确定一件事情最好的方法是实际测试和观测它,让我们写一段代码来测试无冲突时锁的开销,核心代码大概是这样:</p>
903<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(c &lt; MAX) &#123;</span><br><span class="line">	pthread_mutex_lock(&amp;mutex);</span><br><span class="line">	c = c + <span class="number">1</span>;</span><br><span class="line">	pthread_mutex_unlock(&amp;mutex);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
904<p>其实就是循环的lock和unlock。运行五亿次以后,计算耗时为14.5s左右,平摊到每次加锁/解锁操作大改是14ns每次加锁/解锁(扣除很少的一点循环开销)。这个数值和CAS指令的理论开销吻合得很好。</p>
905<p>在锁冲突的情况下,开销就没有这么小了。首先pthread_mutex_lock会真正的调用sys_futex来进入内核来试图加锁,被锁住以后线程会进入睡眠,这带来了上下文切换和线程调度的开销。可以写两个互相解锁的线程来测试这个过程的开销,<br>代码: <a href="https://github.com/tsuna/contextswitch/blob/master/timetctxsw.c" target="_blank" rel="external">https://github.com/tsuna/contextswitch/blob/master/timetctxsw.c</a> 。<br>在单核和双核机器上分别测试(都是在一台2.5GHz的Core i5上用vmware建的虚拟机,运行ubuntu linux)。在单核机器上消耗大约1080ns而双核机器上消耗大约3400ns,双核机器上锁冲突的开销大概是单核的3倍。<br>另外一个c程序可以用来测试“纯上下文切换”的开销,线程只是使用sched_yield来放弃处理器,并不进入睡眠。<br>代码:<a href="https://github.com/tsuna/contextswitch/blob/master/timetctxsw2.c" target="_blank" rel="external">https://github.com/tsuna/contextswitch/blob/master/timetctxsw2.c</a> 。<br>结论很惊人,在单核处理器上,“纯上下文切换”只消耗了大概150ns。</p>
906<h2 id="锁开销的来源"><a href="#锁开销的来源" class="headerlink" title="锁开销的来源"></a>锁开销的来源</h2><p>这样我们大致可以把锁冲突的开销分成三部分,“纯上下文切换”开销,大概是150ns,调度器开销(把线程从睡眠变成就绪或者反过来)大概是900ns,在多核系统上,还存在跨处理器调度的开销,那部分开销很大,超过2000ns。在真实的应用场景里,还要考虑上下文切换带来的cache不命中和TLB不命中的开销,开销只会进一步加大。</p>
907<h2 id="锁的优化"><a href="#锁的优化" class="headerlink" title="锁的优化"></a>锁的优化</h2><p>锁的实现方式对我们的优化方向给予了很好的提示,让我们回想一下--先用一个快的操作(每秒7000万次)来测试锁是否冲突,再用一个慢的操作来实际加锁(每秒30万次)。所以真正消耗时间的不是上锁的次数,而是锁冲突的次数。减少锁冲突的次数才是提升性能的关键。<br>使用更细粒度的锁,可以减少锁冲突。这里说的粒度包括时间和空间,比如哈希表包含一系列å“
907ˆå¸Œæ¡¶ï¼Œä¸ºæ¯ä¸ªæ¡¶è®¾ç½®ä¸€æŠŠé”ï¼Œç©ºé—´ç²’度就会小很多--哈希值相互不冲突的访问不会导致锁冲突,这比为整个哈希表维护一把锁的冲突机率低很多。减少时间粒度也很容易理解,加锁的范围只包含必要的代码段,尽量缩短获得锁到释放锁之间的时间,最重要的是,绝对不要在锁中进行任何可能会阻塞的操作。使用读写锁也是一个很好的减少冲突的方式,读操作之间不互斥,大大减少了冲突。<br>锁本身的行为也存在进一步优化的可能性,sys_futex系统调用的作用在于让被锁住的当前线程睡眠,让出处理器供其它线程使用,既然这个过程的消耗高达几千ns,也就是说如果被锁定的时间不超过这个数值的话,根本没有必要进内核加锁--释放的处理器时间还不够消耗的。sys_futex的时间消耗够跑二百多次compare and swap的,也就是说,对于一个锁冲突比较频繁而且平均锁定时间比较短的系统,一个值得考虑的优化方式是先循环调用compare and swap来尝试获得锁(这个操作也被称作自旋锁),在若干次失败(一般是一百次左右)后再进入内核真正加锁。当然这个优化只能在多处理器的系统里起作用(得有另一个处理器来解锁,否则自旋锁无意义)。在glibc的pthread实现里,通过对pthread_mutex设置PTHREAD_MUTEX_ADAPTIVE_NP属性就可以使用这个机制;在Java世界的HotSpot虚拟机里,可以用-XX:PreBlockSpin来设置自选锁尝试的次数。</p>
908
909            
910          </span>
911        
912      
913    </div>
914
915    <footer class="post-footer">
916      
917
918      
919
920      
921      
922        <div class="post-eof"></div>
923      
924    </footer>
925  </article>
926
927
928    
929      
930
931  
932  
933
934  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
935
936    
937      <header class="post-header">
938
939        
940        
941          <h1 class="post-title" itemprop="name headline">
942            
943            
944              
945                <a class="post-title-link" href="/2015/12/31/length/" itemprop="url">
946                  length的几种写法
947                </a>
948              
949            
950          </h1>
951        
952
953        <div class="post-meta">
954          <span class="post-time">
955            发表于
956            <time itemprop="dateCreated" datetime="2015-12-31T16:43:50+08:00" content="2015-12-31">
957              2015-12-31
958            </time>
959          </span>
960
961          
962
963          
964          <span class="post-time">
965          &nbsp; | &nbsp;
966          
967            <span itemprop="name">hiddenlotus</span>
968          
969
970
971          </span>
972
973          
974
975
976          
977            
978              <span class="post-comments-count">
979              &nbsp; | &nbsp;
980              <a href="/2015/12/31/length/#comments" itemprop="discussionUrl">
981                <span class="post-comments-count disqus-comment-count" data-disqus-identifier="2015/12/31/length/" itemprop="commentsCount"></span>
982              </a>
983            </span>
984            
985          
986
987          
988
989        </div>
990      </header>
991    
992
993
994    <div class="post-body">
995
996      
997      
998
999      
1000        
1001          <span itemprop="articleBody">
1002            
1003              <p>一个传统程序员是这么写的:<br><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">function <span class="built_in">length</span>(<span class="built_in">array</span>) &#123;</span><br><span class="line">  <span class="built_in">var</span> <span class="built_in">sum</span> = <span class="number">0</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="built_in">var</span> i = <span class="number">0</span>; i &lt; <span class="built_in">array</span>.<span class="built_in">length</span>; i++) &#123;</span><br><span class="line">    <span class="built_in">
1003sum</span>++</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">return</span> <span class="built_in">sum</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
1004<p>一个读过SICP的程序员是这么写的:<br><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">function <span class="built_in">length</span>(<span class="built_in">array</span>) &#123;</span><br><span class="line">  <span class="keyword">if</span>(<span class="built_in">array</span>.<span class="built_in">length</span> === <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="built_in">return</span> <span class="number">0</span></span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="built_in">return</span> <span class="number">1</span> + <span class="built_in">length</span>(<span class="built_in">array</span>.slice(<span class="number">1</span>))</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
1005<p>另一个读过SICP的程序员:<br><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">function <span class="built_in">length</span>(<span class="built_in">array</span>) &#123;</span><br><span class="line">  function helper(<span class="built_in">array</span>, <span class="built_in">num</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">array</span>.<span class="built_in">length</span> === <span class="number">0</span>) &#123;</span><br><span class="line">      <span class="built_in">return</span> <span class="built_in">num</span></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="built_in">return</span> helper(<span class="built_in">array</span>.slice(<span class="number">1</span>), <span class="built_in">num</span> + <span class="number">1</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">return</span> helper(<span class="built_in">array</span>, <span class="number">0</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
1006<p>再函数式一点:<br><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">length</span><span class="params">(array)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">array</span>.reduce(<span class="function"><span class="keyword">function</span><span class="params">(prev, cur)</span> </span>&#123; <span class="keyword">return</span> prev + <span class="number">1</span> &#125;, <span class="number">0</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
1007<p>一个我面试过的人…<br><figure class="highlight matlab"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">length</span><span class="params">(array)</span> &#123;</span></span><br><span class="line">  <span class="keyword">return</span> array.<span class="built_in">length</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
1008
1009            
1010          </span>
1011        
1012      
1013    </div>
1014
1015    <footer class="post-footer">
1016      
1017
1018      
1019
1020      
1021      
1022        <div class="post-eof"></div>
1023      
1024    </footer>
1025  </article>
1026
1027
1028    
1029      
1030
1031  
1032  
1033
1034  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
1035
1036    
1037      <header class="post-header">
1038
1039        
1040        
1041          <h1 class="post-title" itemprop="name headline">
1042            
1043            
1044              
1045                <a class="post-title-link" href="/2015/12/31/AMD_CommonJS-1/" itemprop="url">
1046                  AMD 和 CommonJS -- 1. 基本概念
1047                </a>
1048              
1049            
1050          </h1>
1051        
1052
1053        <div class="post-meta">
1054          <span class="post-time">
1055            发表于
1056            <time itemprop="dateCreated" datetime="2015-12-31T12:03:15+08:00" content="2015-12-31">
1057              2015-12-31
1058            </time>
1059          </span>
1060
1061          
1062
1063          
1064          <span class="post-time">
1065          &nbsp; | &nbsp;
1066          
1067            <span itemprop="name">mac.meng</span>
1068          
1069
1070
1071          </span>
1072
1073          
1074
1075
1076          
1077            
1078              <span class="post-comments-count">
1079              &nbsp; | &nbsp;
1080              <a href="/2015/12/31/AMD_CommonJS-1/#comments" itemprop="discussionUrl">
1081                <span class="post-comments-count disqus-comment-count" data-disqus-identifier="2015/12/31/AMD_CommonJS-1/" itemprop="commentsCount"></span>
1082              </a>
1083            </span>
1084            
1085          
1086
1087          
1088
1089        </div>
1090      </header>
1091    
1092
1093
1094    <div class="post-body">
1095
1096      
1097      
1098
1099      
1100        
1101          <span itemprop="articleBody">
1102            
1103              <h3 id="AMD-Asynchronous-Module-Definition"><a href="#AMD-Asynchronous-Module-Definition" class="headerlink" title="AMD = Asynchronous Module Definition"></a>AMD = Asynchronous Module Definition</h3><p>它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。</p>
1104<figure class="highlight crystal"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">require</span>([<span class="class"><span class="keyword">module</span>], <span class="title">callback</span>);</span></span><br></pre></td></tr></table></figure>
1105<p>第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样:</p>
1106<figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">require([‘jquery’,’math'], function ($, math) &#123;</span><br><span class="line">	math.add(<span class="number">2</span>, <span class="number">3</span>)<span class="comment">;</span></span><br><span class="line">	$(<span class="name">body</span>).show()<span class="comment">;</span></span><br><span class="line">&#125;)<span class="comment">;</span></span><br></pre></td></tr></table></figure>
1107<h3 id="CommonJS"><a href="#CommonJS" class="headerlink" title="CommonJS"></a>CommonJS</h3><p>那什么是CommonJS呢, 总是拿他和AMD来对比使用<br>2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。<br>node.js的模块系统,就是参照<a href="http://wiki.commonjs.org/wiki/Modules/1.1" target="_blank" rel="external">CommonJS</a>规范实现的。在CommonJS中,有一个全局性方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载。<br><figure class="highlight oxygene"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> math = <span class="keyword">require</span>(<span class="string">'math'</span>);</span><br><span class="line">math.<span class="keyword">add</span>(<span class="number">2</span>,<span class="number">3</span>); <span class="comment">// 5</span></span><br></pre></td></tr></table></figure></p>
1108<h3 id="那么问题来了"><a href="#那么问题来了" class="headerlink" title="那么问题来了"></a>那么问题来了</h3><p>有了服务器端模块以后,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。<br>但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器环境。<br>服务器端的文件依赖只取决于磁盘IO,但是客户端会有网络的不确定性.取决于网速的快慢,可能要等很长时间,浏览器处于”假死”状态。</p>
1109<p>因此,浏览器端的模块,不能采用”同步加载”(synchronous),只能采用”异步加载”(asynchronous)。这就是AMD规范诞生的背景.</p>
1110<h3 id="CommonJS-到底是个什么鬼"><a href="#CommonJS-到底是个什么鬼" class="headerlink" title="CommonJS 到底是个什么鬼"></a>CommonJS 到底是个什么鬼</h3><p><a href="http://www.commonjs.org/" target="_blank" rel="external">CommonJS</a> 网站标题 : javascript: not just for browsers any more!</p>
1111<p>With CommonJS-compliant systems, you can use JavaScript to write:</p>
1112<ul>
1113<li>Server-side JavaScript applications</li>
1114<li>Command line tools</li>
1115<li>Desktop GUI-based applications</li>
1116<li>Hybrid applications (Titanium, Adobe AIR)</li>
1117</ul>
1118<p>感觉基本就是定义一些文档和API, 大家都遵循,自己去实现. 然后把JS写的到处都是,开枝散叶.</p>
1119<p><code>CommonJS是服务器端模块的规范,Node.js采用了这个规范。</code><br>根据CommonJS规范,一个单独的文件就是一个模块。 加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。</p>
1120<h3 id="预告"><a href="#预告" class="headerlink" title="预告"></a>预告</h3><p>接下来的几篇文章次会按照加载方式的不同,讲解一下 require.js 和 AngularJS 的加载机制 以及webwork 和 serverwork 的开发方式, 还有bower库和npm库的外壳的不同.</p>
1121
1122            
1123          </span>
1124        
1125      
1126    </div>
1127
1128    <footer class="post-footer">
1129      
1130
1131      
1132
1133      
1134      
1135        <div class="post-eof"></div>
1136      
1137    </footer>
1138  </article>
1139
1140
1141    
1142      
1143
1144  
1145  
1146
1147  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
1148
1149    
1150      <header class="post-header">
1151
1152        
1153        
1154          <h1 class="post-title" itemprop="name headline">
1155            
1156            
1157              
1158                <a class="post-title-link" href="/2015/12/27/gulp-browserify/" itemprop="url">
1159                  gulp_browserify
1160                </a>
1161              
1162            
1163          </h1>
1164        
1165
1166        <div class="post-meta">
1167          <span class="post-time">
1168            发表于
1169            <time itemprop="dateCreated" datetime="2015-12-27T11:23:09+08:00" content="2015-12-27">
1170              2015-12-27
1171            </time>
1172          </span>
1173
1174          
1175
1176          
1177
1178
1179          
1180            
1181              <span class="post-comments-count">
1182              &nbsp; | &nbsp;
1183              <a href="/2015/12/27/gulp-browserify/#comments" itemprop="discussionUrl">
1184                <span class="post-comments-count disqus-comment-count" data-disqus-identifier="2015/12/27/gulp-browserify/" itemprop="commentsCount"></span>
1185              </a>
1186            </span>
1187            
1188          
1189
1190          
1191
1192        </div>
1193      </header>
1194    
1195
1196
1197    <div class="post-body">
1198
1199      
1200      
1201
1202      
1203        
1204          <span itemprop="articleBody">
1205            
1206              <h2 id="babel-intergate-and-source-map"><a href="#babel-intergate-and-source-map" class="headerlink" title="babel intergate and source map"></a><a href="https://egghead.io/lessons/javascript-gulp-and-browserify-adding-babel-source-maps" target="_blank" rel="external">babel intergate and source map</a></h2><p>debugger 令人印象深刻<br>通过 exorcist 加了source.map 可以在未编译过的文件中打断点了</p>
1207<h2 id="Hooking-up-Watchify"><a href="#Hooking-up-Watchify" class="headerlink" title="Hooking up Watchify"></a><a href="https://egghead.io/lessons/javascript-gulp-and-browserify-hooking-up-watchify" target="_blank" rel="external">Hooking up Watchify</a></h2><p>Watchify 貌似只能跟 browserify一起用, 只对js其效果. 不知道html和css 应该怎么整.</p>
1208<h2 id="Live-Reload-with-Browsersync"><a href="#Live-Reload-with-Browsersync" class="headerlink" title="Live Reload with Browsersync"></a><a href="https://egghead.io/lessons/javascript-gulp-and-browserify-adding-live-reload-with-browsersync" target="_blank" rel="external">Live Reload with Browsersync</a></h2><p>Browsersync 不用在浏览器装livereload客户端了. 不知道如何指定端口</p>
1209<h2 id="Initial-Setup"><a href="#Initial-Setup" class="headerlink" title="Initial Setup"></a><a href="https://egghead.io/lessons/javascript-gulp-and-browserify-initial-setup" target="_blank" rel="external">Initial Setup</a></h2><p>browserify 的一些初始化操作, 目录结构等</p>
1210
1211            
1212          </span>
1213        
1214      
1215    </div>
1216
1217    <footer class="post-footer">
1218      
1219
1220      
1221
1222      
1223      
1224        <div class="post-eof"></div>
1225      
1226    </footer>
1227  </article>
1228
1229
1230    
1231  </section>
1232
1233  
1234
1235 </div>
1236
1237        
1238
1239        
1240      </div>
1241
1242      
1243        
1244  
1245  <div class="sidebar-toggle">
1246    <div class="sidebar-toggle-line-wrap">
1247      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
1248      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
1249      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
1250    </div>
1251  </div>
1252
1253  <aside id="sidebar" class="sidebar">
1254    <div class="sidebar-inner">
1255
1256      
1257
1258      
1259
1260      <section class="site-overview sidebar-panel  sidebar-panel-active ">
1261        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
1262          <img class="site-author-image" src="/images/default_avatar.png" alt="Alan, Bob, Mactive, Victor, Raven, Luka, Yak, Uni.Bell" itemprop="image"/>
1263          <p class="site-author-name" itemprop="name">Alan, Bob, Mactive, Victor, Raven, Luka, Yak, Uni.Bell</p>
1264        </div>
1265        <p class="site-description motion-element" itemprop="description"></p>
1266        <nav class="site-state motion-element">
1267          <div class="site-state-item site-state-posts">
1268            <a href="/archives">
1269              <span class="site-state-item-count">10</span>
1270              <span class="site-state-item-name">日志</span>
1271            </a>
1272          </div>
1273
1274          <div class="site-state-item site-state-categories">
1275            
1276              <span class="site-state-item-count">0</span>
1277              <span class="site-state-item-name">分类</span>
1278              
1279          </div>
1280
1281          <div class="site-state-item site-state-tags">
1282            <a href="/tags">
1283              <span class="site-state-item-count">23</span>
1284              <span class="site-state-item-name">标签</span>
1285              </a>
1286          </div>
1287
1288        </nav>
1289
1290        
1291
1292        <div class="links-of-author motion-element">
1293          
1294            
1295              <span class="links-of-author-item">
1296                <a href="http://www.github.com/xbay" target="_blank">
1297                  <i class="fa fa-github"></i> GitHub
1298                </a>
1299              </span>
1300            
1301              <span class="links-of-author-item">
1302                <a href="http://www.twitter.com/xbay" target="_blank">
1303                  <i class="fa fa-twitter"></i> Twitter
1304                </a>
1305              </span>
1306            
1307          
1308        </div>
1309
1310        
1311        
1312
1313        <div class="links-of-author motion-element">
1314          
1315        </div>
1316
1317      </section>
1318
1319      
1320
1321    </div>
1322  </aside>
1323
1324
1325      
1326    </main>
1327
1328    <footer id="footer" class="footer">
1329      <div class="footer-inner"> <div class="copyright" >
1330  
1331  &copy; 
1332  <span itemprop="copyrightYear">2016</span>
1333  <span class="with-love">
1334    <i class="icon-next-heart fa fa-heart"></i>
1335  </span>
1336  <span class="author" itemprop="copyrightHolder">Alan, Bob, Mactive, Victor, Raven, Luka, Yak, Uni.Bell</span>
1337</div>
1338
1339<div class="powered-by">
1340  由 <a class="theme-link" href="http://hexo.io">Hexo</a> 强力驱动
1341</div>
1342
1343<div class="theme-info">
1344  主题 -
1345  <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">
1346    NexT
1347  </a>
1348</div>
1349
1350
1351 </div>
1352    </footer>
1353
1354    <div class="back-to-top"></div>
1355  </div>
1356
1357  
1357<script type="text/javascript" src="/vendors/jquery/index.js?v=2.1.3"></script>
1357
1358
1359  
1360  
1361  
1362    
1363    
1364
1365  
1366
1367    
1367<script type="text/javascript">
1368      var disqus_shortname = 'xbay';
1369      var disqus_identifier = 'index.html';
1370      var disqus_title = '';
1371      var disqus_url = '';
1372
1373      function run_disqus_script(disqus_script){
1374        var dsq = document.createElement('script');
1375        dsq.type = 'text/javascript';
1376        dsq.async = true;
1377        dsq.src = '//' + disqus_shortname + '.disqus.com/' + disqus_script;
1378        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
1379      }
1380
1381      run_disqus_script('count.js');
1382      
1383    </script>
1383
1384  
1385
1386
1387  
1388  
1389  
1389<script type="text/javascript" src="/vendors/fancybox/source/jquery.fancybox.pack.js"></script>
1389
1390  
1390<script type="text/javascript" src="/js/fancy-box.js?v=0.4.5.2"></script>
1390
1391
1392
1393  
1393<script type="text/javascript" src="/js/helpers.js?v=0.4.5.2"></script>
1393
1394  
1395
1396  
1396<script type="text/javascript" src="/vendors/velocity/velocity.min.js"></script>
1396
1397  
1397<script type="text/javascript" src="/vendors/velocity/velocity.ui.min.js"></script>
1397
1398
1399  
1399<script type="text/javascript" src="/js/motion_global.js?v=0.4.5.2" id="motion.global"></script>
1399
1400
1401
1402
1403
1404  
1404<script type="text/javascript" src="/js/nav-toggle.js?v=0.4.5.2"></script>
1404
1405  
1405<script type="text/javascript" src="/vendors/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
1405
1406
1407  
1408
1409  
1409<script type="text/javascript">
1410    $(document).ready(function () {
1411      if (CONFIG.sidebar === 'always') {
1412        displaySidebar();
1413      }
1414      if (isMobile()) {
1415        FastClick.attach(document.body);
1416      }
1417
1418      motionIntegrator.bootstrap();
1419    });
1420  </script>
1420
1421
1422  
1423  
1424
1425  
1426  
1427
1428  
1429  
1429<script type="text/javascript" src="/js/lazyload.js"></script>
1429
1430  
1430<script type="text/javascript">
1431    $(function () {
1432      $("#posts").find('img').lazyload({
1433        placeholder: "/images/loading.gif",
1434        effect: "fadeIn"
1435      });
1436    });
1437  </script>
1437
1438</body>
1439</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.