<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title><![CDATA[Simplicity + Power = Elegance]]></title>
    <link>http://fancymax.github.io/</link>
    <atom:link href="//atom.xml" rel="self" type="application/rss+xml"/>
    <description></description>
    <pubDate>Sun, 08 Jul 2018 12:39:11 GMT</pubDate>
    <generator>http://hexo.io/</generator>
    
    <item>
      <title><![CDATA[搬家卖书]]></title>
      <link>http://fancymax.github.io/2018/07/08/sellbook2018/</link>
      <guid>http://fancymax.github.io/2018/07/08/sellbook2018/</guid>
      <pubDate>Sun, 08 Jul 2018 12:38:10 GMT</pubDate>
      <description>
      <![CDATA[<p>这两年买纸质书买的越来越少了。但是有些书没有电子版还是要买纸质的。</p>
<p>很多书买的时候都很兴奋，但是经常买回来看两眼就放弃了。趁着这次要搬家的机会，打算把珍藏多年的一些纸质书卖了，一方面让这些书继续发光发热，另一方面通过这个也能交到一些志同道合的朋友，也算是一举两]]>
      </description>
      <content:encoded><![CDATA[<p>这两年买纸质书买的越来越少了。但是有些书没有电子版还是要买纸质的。</p>
<p>很多书买的时候都很兴奋，但是经常买回来看两眼就放弃了。趁着这次要搬家的机会，打算把珍藏多年的一些纸质书卖了，一方面让这些书继续发光发热，另一方面通过这个也能交到一些志同道合的朋友，也算是一举两得啦。</p>
<p>新书5折，旧书3折。相关书目如下，深圳面交，先到先得。</p>
<ul>
<li><p>mac相关</p>
<ul>
<li>Cocoa Programming for OS X (英文原版书，原价49.9 dolar)</li>
<li>AVFoundation开发秘籍(主要讲Mac/iOS平台音视频相关技术)</li>
<li>硅谷革命 (讲述Mac机的研发历史，之前在极客时间买的)</li>
</ul>
</li>
</ul>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/2018070815310289329749.jpg" alt="test"></p>
<ul>
<li>网络相关</li>
</ul>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/20180708153103095346687.jpg" alt=""></p>
<ul>
<li>测试相关</li>
</ul>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/20180708153103126235703.jpg" alt=""></p>
<ul>
<li>深度学习&amp;算法</li>
</ul>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/20180708153103146911476.jpg" alt=""></p>
<ul>
<li>人文类</li>
</ul>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/20180708153103165789492.jpg" alt=""></p>
<ul>
<li><p>UI设计</p>
<ul>
<li><p>about Face 最新英文原版 (原价50 dolar)<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/20180708153103174856562.jpg" alt=""></p>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/20180708153103180192471.jpg" alt=""></p>
</li>
</ul>
</li>
<li><p>其他杂类<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/20180708153105335931689.jpg" alt=""></p>
</li>
</ul>
]]></content:encoded>
      <comments>http://fancymax.github.io/2018/07/08/sellbook2018/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[macOS 进程间通信方案探究]]></title>
      <link>http://fancymax.github.io/2018/06/13/xpc/</link>
      <guid>http://fancymax.github.io/2018/06/13/xpc/</guid>
      <pubDate>Wed, 13 Jun 2018 12:00:23 GMT</pubDate>
      <description>
      <![CDATA[<p>macOS上的进程间通信的方案很多，总的来说分为以下三种，如下图所示</p>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/2017120715126509008638.jpg" alt="IPC"></p>
<ul>]]>
      </description>
      <content:encoded><![CDATA[<p>macOS上的进程间通信的方案很多，总的来说分为以下三种，如下图所示</p>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/2017120715126509008638.jpg" alt="IPC"></p>
<ul>
<li>第一种也是历史最悠久的是基于POSIX的实现，其中常见的是基于Socket (TCP/IP)的实现。同时这种方案和平台无关。</li>
<li>第二种基于 底层信号机制，mac系统的内核组件就是使用mach_mag来传递消息。参考</li>
<li>第三种就是Foundation库里提供的一些类库。github上有个不错的范例实现基于CFMachPort—<a href="https://github.com/Syphon/Simple" target="_blank" rel="external">https://github.com/Syphon/Simple​</a></li>
</ul>
<h2 id="XPC方式">XPC方式</h2><p>估计是苹果觉得这些方案，都不够好，要么不够易用，要么不够安全。所以在2011年的时候推出的全新的XPC进程间通信的方案。</p>
<blockquote>
<p>The XPC Services API provides a low-level (libSystem) interprocess communication mechanism based on serialized property lists.</p>
</blockquote>
<p>这是一套C的API，范例代码可以参考<a href="https://github.com/objcio/issue-14-xpc" target="_blank" rel="external">https://github.com/objcio/issue-14-xpc</a>。</p>
<p>但是这个技术有一个挺大的局限性，XPC进程只能是一个不带界面的控制台进程。关于 XPC 比较特别的尝试可以看看这个代码: <a href="https://github.com/OpenEmu/OpenEmuXPCCommunicator" target="_blank" rel="external">https://github.com/OpenEmu/OpenEmuXPCCommunicator</a>。</p>
<p>这可能一般的简单的程序架构是没问题的，但是有些特殊的要求，比如我就要求主程序和子程序都是界面程序 并且可以互相调用，这样子就完全搞不定了，由于XPC这套东西并不开源，所以要实现这种更灵活的互相调用，只能另辟蹊径。</p>
<p>为了实现这种两个界面程序，主程序和子程序可以互相调用的需求推荐就采取 Socket的方式实现，因为这种最标准的方式和操作系统无关，相比其他方案 不会因为 系统API被废弃而 变得不可用。</p>
<h2 id="Socket方式">Socket方式</h2><p>一个理想的调用关系大概是这样的:<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/20180613152888929566604.png" alt=""></p>
<p>子程序放在主程序的程序包内。</p>
<p>当主程序需要调用子程序时 主程序先创建一个 TCP Server，并且把Server的地址和端口 作为参数 调起子程序(NSTask)。</p>
<p>子程序启动后，获取参数中的TCP地址和端口, 然后创建TCP Client进行连接。然后通过TCP的通信来互相调用。</p>
<p>至此 通信层的功能和大体框架基本确定了。</p>
<p>那应用层应该以何种方式调用呢？是否要定义一个特别的通信规约？</p>
<p>如果定义了通信规约，那每次别人要使用这套框架的时候还要先熟悉一下规约，这就显得不灵活 和 不易用了。有没有可能借用XPC的思路，直接以Objc消息传递的方式来互相调用呢？借助于Objective-C的runtime，这是可以实现的。我个人XPC的内部实现思路估计也是一样的。</p>
<p>先复习一下Objc的消息转发流程:</p>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/20180613152888996391921.png" alt=""></p>
<p>注意最后一步 forwardInvocation 可以获取函数的所有调用参数。</p>
<blockquote>
<p>-(void)forwardInvocation:(NSInvocation *)anInvocation;</p>
</blockquote>
<p>假如 主程序利用 forwardInvocation 把需要调用的子程序的 函数信息 序列化 通过 Tcp 传递给子程序，子程序 反序列化后再根据函数信息调用对应功能。这就完成了一个互相调用的过程。</p>
<blockquote>
<p>实现对应功能的时候注意一下 NSInvocation 在Swift已经被废弃了，所以相关功能只能使用OC编写。</p>
<p>另外 NSInvocation 在ARC环境下跑的时候有个坑，可以参考一下 <a href="https://stackoverflow.com/questions/8811498/nsinvocation-and-arc-automatic-reference-counting" target="_blank" rel="external">nsinvocation-and-arc-automatic-reference-counting</a>。</p>
</blockquote>
<p>具体的代码就不放上来，大家有兴趣可以按照上述思路 实现一下看看，一方面可以熟悉一下TCP流程，另一方面也可以熟悉 OC的 转发流程，另外实现完这一套之后 也会 对苹果XPC内部实现有一定的了解。</p>
<p>参考链接:</p>
<ul>
<li><a href="https://www.objc.io/issues/14-mac/xpc/" target="_blank" rel="external">https://www.objc.io/issues/14-mac/xpc/</a></li>
<li><a href="https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/Introduction.html#//apple_ref/doc/uid/10000172i" target="_blank" rel="external">https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/Introduction.html#//apple_ref/doc/uid/10000172i</a></li>
<li><a href="https://www.mikeash.com/pyblog/friday-qa-2009-01-16.html" target="_blank" rel="external">https://www.mikeash.com/pyblog/friday-qa-2009-01-16.html</a></li>
<li><a href="https://www.mikeash.com/pyblog/friday-qa-2009-02-20-the-good-and-bad-of-distributed-objects.html" target="_blank" rel="external">https://www.mikeash.com/pyblog/friday-qa-2009-02-20-the-good-and-bad-of-distributed-objects.html</a></li>
</ul>
]]></content:encoded>
      <comments>http://fancymax.github.io/2018/06/13/xpc/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[mpv源码学习-1编译调试]]></title>
      <link>http://fancymax.github.io/2018/05/20/mpv-1/</link>
      <guid>http://fancymax.github.io/2018/05/20/mpv-1/</guid>
      <pubDate>Sun, 20 May 2018 09:36:05 GMT</pubDate>
      <description>
      <![CDATA[<p>最近对mpv的源码产生了兴趣，打算研究研究。</p>
<p><a href="https://github.com/mpv-player/mpv" target="_blank" rel="external">mpv</a>是属于一个大型的C语言的跨平台的播放器项目，其代码]]>
      </description>
      <content:encoded><![CDATA[<p>最近对mpv的源码产生了兴趣，打算研究研究。</p>
<p><a href="https://github.com/mpv-player/mpv" target="_blank" rel="external">mpv</a>是属于一个大型的C语言的跨平台的播放器项目，其代码源自MPlayer和mplayer2，是一个历史非常悠久的项目了。</p>
<p>先来分析一下此项目大致存在的难点：</p>
<ul>
<li>针对跨平台的编译处理。</li>
<li>运用了C语言的奇淫巧技。</li>
<li>图形学的一些专业知识，主要是编解码、图形渲染等。</li>
</ul>
<p>先从github上下载了mpv的源码，然后进行编译，整个过程比较人性化。</p>
<h2 id="waf编译系统">waf编译系统</h2><p>mpv项目自己定制了一个叫<a href="https://github.com/waf-project/waf" target="_blank" rel="external">waf</a>的编译系统，至于为什么使用waf编译系统在他们的项目文档里有做介绍<a href="https://github.com/mpv-player/mpv/blob/master/DOCS/waf-buildsystem.rst" target="_blank" rel="external">why waf</a>，总结下来大致以下几点：</p>
<ul>
<li>编译脚本 在跨平台时比较保持一致的代码，并且尽量减少重复和胶水代码。</li>
<li>配置(configuration)步骤 和 编译(buid)步骤必须是分离的。</li>
<li>脚本比较好理解、好修改。(waf使用python编写)</li>
</ul>
<h2 id="源码编译">源码编译</h2><p>(本文的编译过程基于Mac)先从github上下载了mpv的源码。</p>
<p>第一步先跑个脚本下载最新的waf执行文件。</p>
<figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./<span class="keyword">bootstrap</span>.py</span><br></pre></td></tr></table></figure>
<p>第二步跑一下waf的configuration步骤，这个过程会告诉你缺少哪些组件。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./waf configure</span><br></pre></td></tr></table></figure>
<p>根据第二步的结果，对一些缺少的组件进行安装。</p>
<figure class="highlight armasm"><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">brew </span>install libass</span><br><span class="line"><span class="keyword">brew </span>install ffmpeg</span><br></pre></td></tr></table></figure>
<p>然后 再跑一遍 ./waf configure 就成功生成了config.h，用于最后的编译过程。</p>
<p>在configure阶段可以使用下列选项，不进行代码优化，便于后续调试。</p>
<p><code>--disable-optimize</code></p>
<p>第三部 跑一下build步骤，这样就生成了最终的执行文件了。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./waf</span><br></pre></td></tr></table></figure>
<p>最后一步 运行一下 安装步骤，就把mpv 放到了 /usr/local/bin/mpv 系统目录。</p>
<figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./waf <span class="keyword">install</span></span><br></pre></td></tr></table></figure>
<h2 id="代码调试">代码调试</h2><p>安装好mpv之后，就可以先尝试运行一下程序，可以正常的播放视频。</p>
<figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title">mpv</span>  xx/xx.mp4</span><br></pre></td></tr></table></figure>
<p>接下来尝试用lldb来调试一下代码。由于mpv在mac系统上运行，所以代码的入口在 xx/mpv/osdep/macosx_application.m 这个文件。所以先用lldb在这里设置一个断点。</p>
<figure class="highlight nimrod"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">#Launch a process for with arguments a.out 1 2 3 without having to supply the args every time</span></span><br><span class="line">lldb -- mpv xx/xx.mp4</span><br><span class="line"></span><br><span class="line"><span class="comment">#enter lldb env then set breakpoint</span></span><br><span class="line">breakpoint <span class="type">set</span> --name cocoa_main</span><br><span class="line"></span><br><span class="line"><span class="comment">#star debug</span></span><br><span class="line">run</span><br><span class="line"></span><br><span class="line"><span class="comment">#运行结果</span></span><br><span class="line">* thread <span class="comment">#1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1</span></span><br><span class="line">    frame <span class="comment">#0: 0x00000001000d711b mpv`cocoa_main(argc=2, argv=0x00007ffeefbff940) at macosx_application.m:338 [opt]</span></span><br><span class="line">   <span class="number">335</span> 	</span><br><span class="line">   <span class="number">336</span> 	<span class="type">int</span> cocoa_main(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span><br><span class="line">   <span class="number">337</span> 	&#123;</span><br><span class="line">-&gt; <span class="number">338</span> 	    @autoreleasepool &#123;</span><br><span class="line">   <span class="number">339</span> 	        application_instantiated = <span class="literal">true</span>;</span><br><span class="line">   <span class="number">340</span> 	        [[<span class="type">EventsResponder</span> sharedInstance] setIsApplication:<span class="type">YES</span>];</span><br><span class="line">   <span class="number">341</span> 	</span><br><span class="line"><span class="type">Target</span> <span class="number">0</span>: (mpv) stopped.</span><br></pre></td></tr></table></figure>
<p>这之后 就可以一边看代码一边通过调试来理解代码行为了。</p>
<p>最后再补充几条常用lldb的命令，更多内容可以查看lldb的官方文档。</p>
<figure class="highlight livecodeserver"><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"><span class="comment"># Show the stack backtrace for the current thread</span></span><br><span class="line">thread backtrace</span><br><span class="line"></span><br><span class="line"><span class="comment"># List all breakpoints</span></span><br><span class="line"><span class="built_in">breakpoint</span> list</span><br><span class="line"></span><br><span class="line"><span class="comment"># Delete a breakpoint</span></span><br><span class="line"><span class="built_in">breakpoint</span> <span class="built_in">delete</span> <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">breakpoint</span> disable/enable</span><br></pre></td></tr></table></figure>]]></content:encoded>
      <comments>http://fancymax.github.io/2018/05/20/mpv-1/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[Live photo导入到Final Cut Pro]]></title>
      <link>http://fancymax.github.io/2018/02/10/livePhoto/</link>
      <guid>http://fancymax.github.io/2018/02/10/livePhoto/</guid>
      <pubDate>Sat, 10 Feb 2018 13:43:08 GMT</pubDate>
      <description>
      <![CDATA[<p>Live photo是近两年来我最喜欢的新特性之一(只能在iPhone 6S/SE上使用)。</p>
<p>它的功能简单来说就是拍一张照片时候 顺便 记录拍摄瞬间 1～2秒的视频。这就非常有用了，原因有这么几个：</p>
<ol>
<li>有人被拍的时候就喜欢眨眼，有了这个]]>
      </description>
      <content:encoded><![CDATA[<p>Live photo是近两年来我最喜欢的新特性之一(只能在iPhone 6S/SE上使用)。</p>
<p>它的功能简单来说就是拍一张照片时候 顺便 记录拍摄瞬间 1～2秒的视频。这就非常有用了，原因有这么几个：</p>
<ol>
<li>有人被拍的时候就喜欢眨眼，有了这个功能就可以在在视频的关键帧中挑一张不眨眼的靓照做为主图像。</li>
<li>一般拍照的时候 都是 精彩瞬间，所以5、6张live photo完全可以直接组合成一个有趣的视频，而静态图片是做不到的。</li>
</ol>
<h2 id="如何生成Live_Photo">如何生成Live Photo</h2><p>其实live photo内容构成很简单，就是 JPG + MOV，即一张高清大图，加上一段720P的视频。</p>
<p>所以如果你要自己制作一个live photo也很简单，<a href="https://github.com/mzp/LoveLiver" target="_blank" rel="external">LoveLiver</a> 这个开源软件可以选择一个视频，然后把其中一段转换成live photo，并导入到系统的Photos中。</p>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/20180208151808958441671.jpg" alt="2"></p>
<h2 id="Live_Photo导入iMovie/Final_Cut_Pro">Live Photo导入iMovie/Final Cut Pro</h2><p>虽然Live Photo这么好，但是苹果原生的iMovie/Final Cut Pro却不支持直接导入，真是坑爹。</p>
<p>只能曲线救国，通过系统 原生应用 Image Capture.app(在 spotlight搜索 image即可找到) ，插入手机 连接线，然后选择对应的Live Photo，然后点击 Import，它会下载到指定的文件夹里。</p>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/2018020815180905305502.jpg" alt="1"></p>
<p>最终Live Photo会导入成 JPG + MOV，还有可能有AAE文件(这是个XML文件，记录着这张照片的修改信息)，然后你把对应MOV文件导入到iMovie/Final Cut Pro就完成任务啦。</p>
]]></content:encoded>
      <comments>http://fancymax.github.io/2018/02/10/livePhoto/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[我是如何管理照片的]]></title>
      <link>http://fancymax.github.io/2018/01/10/imageCaptureCore/</link>
      <guid>http://fancymax.github.io/2018/01/10/imageCaptureCore/</guid>
      <pubDate>Wed, 10 Jan 2018 13:50:11 GMT</pubDate>
      <description>
      <![CDATA[<h2 id="I_Hired_Myself">I Hired Myself</h2><p><a href="http://atp.fm/episodes/259" target="_blank" rel="external">ATP 259期 I Hired Myself</a]]>
      </description>
      <content:encoded><![CDATA[<h2 id="I_Hired_Myself">I Hired Myself</h2><p><a href="http://atp.fm/episodes/259" target="_blank" rel="external">ATP 259期 I Hired Myself</a> 中Casey 提到他写了一个“Mac App”(其实是个命令行工具)用于管理他的照片流，让他有种I Hired Myself的感觉。我听了之后也很有感触，这也是我选择 当程序员的初衷。</p>
<h2 id="我的照片流">我的照片流</h2><p>今天谈一谈Mac上导入照片工作流，其中利用到了ImageCaptureCore框架，功能原理如下：</p>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/20180203151764086753471.png" alt="1"></p>
<p>这个框架支持手机 以及 相机的SD卡，一般的应用场景是 </p>
<ul>
<li><p>插上相机的SD卡</p>
</li>
<li><p>电脑自动检测到且自动打开photos这个Mac上的原生图片工具</p>
</li>
<li><p>然后列出SD卡里的所有照片</p>
<p>​<br>之后，你可以选择 照片 导入到Mac里。photo里还提供了导入后删除的选项。</p>
</li>
</ul>
<p>不知道各位第一次使用的时候什么感想，反正我当时是被惊艳到了。</p>
<p>至此之后 我就喜欢上了这样的照片管理流程。</p>
<ul>
<li>我会定期把 相机 和 手机导入到 Mac里，然后把源文件删除。</li>
<li>然后定期 对 Mac 进行TimeMachine 备份,这样就同时把照片给备份了。</li>
<li>然后每次浏览 Mac的照片库的时候 都会标注 为 最喜爱 的照片。</li>
<li>同时把所有最喜欢的照片 添加到 iCloud photo Sharing里。这样我和女朋友都可以在photo的共享里找到这些照片。</li>
</ul>
<p>这样做有很多好处:</p>
<ul>
<li>还是使用 5G 的iCloud空间就够了。</li>
<li>可以定期清理 手机 中大量照片占用的空间。</li>
<li>照片进行了定期备份。</li>
<li>相机 和 手机的照片可以统一存放处理。</li>
<li>在任意设备 都可以获取 自己挑选的最喜欢的照片。</li>
</ul>
<h2 id="ImageCaptureCore">ImageCaptureCore</h2><p>下面再来说说ImageCaptureCore, 其实Photos里的那一系列功能都是 通过它实现的。<br>它的主要结构如下，基本上是提供了一个树形结构 方便 你来处理导入 照片。<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/20180203151764241678581.png" alt="2"><br>大家有兴趣可以下载 苹果的范例程序<a href="https://developer.apple.com/library/content/samplecode/CameraBrowser/Introduction/Intro.html" target="_blank" rel="external">CameraBrowser</a>来研究一下。</p>
]]></content:encoded>
      <comments>http://fancymax.github.io/2018/01/10/imageCaptureCore/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[开源直播工具OBS研究]]></title>
      <link>http://fancymax.github.io/2017/10/28/obs/</link>
      <guid>http://fancymax.github.io/2017/10/28/obs/</guid>
      <pubDate>Sat, 28 Oct 2017 11:03:17 GMT</pubDate>
      <description>
      <![CDATA[<h1 id="项目简介">项目简介</h1><p>OBS - Free and open source software for live streaming and screen recording(OBS是一款开源的用于录屏直播的工具软件)。</p>
<p>旧版的OBS只能]]>
      </description>
      <content:encoded><![CDATA[<h1 id="项目简介">项目简介</h1><p>OBS - Free and open source software for live streaming and screen recording(OBS是一款开源的用于录屏直播的工具软件)。</p>
<p>旧版的OBS只能支持Windows，目前已经停止开发。作者为了支持Windows/Mac/Linux重写了整个软件，项目地址为<a href="https://github.com/jp9000/obs-studio" target="_blank" rel="external">obs-studio in Github</a>。</p>
<p>新版的OBS的目标有以下几点：</p>
<ol>
<li>Make it multiplatform.(<strong>跨平台支持</strong>)</li>
<li>Separate the application from the core, allowing custom application of the core if desired, and easier extending of the user interface.(<strong>模块化、易扩展</strong>)</li>
<li>Simplify complex systems to not only make it easier to use, but easier to maintain.(<strong>简化系统，使其易用易维护</strong>)</li>
<li>Make a better/cleaner code base, use better coding standards, use standard libraries where possible (not just STL and C standard library, but also things like ffmpeg as well), and improve maintainability of the project as a whole.(<strong>尽量利用其他开源软件成果</strong>)</li>
<li>Implement a new API-independent shader/effect system allowing better and easier shaders usage and customization without having to duplicate shader code.(<strong>实现独立于API的shader/effect系统</strong>)</li>
<li>Better device support.  (<strong>更好的支持有录屏需求的设备</strong>)</li>
</ol>
<p>OBS项目的语言分布：</p>
<ol>
<li>C: 57.6%</li>
<li>C++: 36.3%</li>
<li>Objective-C/Objective-C++: 4%</li>
<li>others: 3%</li>
</ol>
<p>OBS代码主要包含这些部分：</p>
<ol>
<li>libobs: 核心代码，定义项目框架以及核心API，主要用C语言编写。</li>
<li>UI: 界面代码，采用C++的QT框架，开发出适用三大平台的界面。</li>
<li>plugins: 插件代码，可独立编译成dll(windows平台)或so(*nix平台)，包含Source(录屏输入源)、Output、Service(各种流播服务)等全部被定义为插件。</li>
<li>libobs-d3d11: 基于D3D的图形子系统，主要用在Windows系统。</li>
<li>libobs-opengl: 基于opengl的图形子系统，主要用在*uix系统。</li>
</ol>
<h2 id="OBS软件功能概述">OBS软件功能概述</h2><p>OBS项目工程中以场景组的方式呈现给用户,可以自由设置场景、输入源、效果处理，配置直播服务。<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/2016121483032obs_ui.png" alt="2016121483032obs_ui.png"></p>
<h3 id="OBS项目工程结构">OBS项目工程结构</h3><p>OBS项目中一个工程结构如下<img src="http://7xpbra.com1.z0.glb.clouddn.com/201612147022obs_project_stucture.png" alt="201612147022obs_project_stucture.png"></p>
<p>一个场景组包含多个场景，OBS直播的时候是把整个场景流播给用户，那为什么需要多个场景？因为播主在直播时有快速切换场景的需要，所以播主需要在直播前编辑好多个场景(比如纯游戏场景；游戏+头像；解说；休息场景等)，然后直播的过程中可以根据不同的需要快速切换。</p>
<h3 id="OBS场景的转场">OBS场景的转场</h3><p>OBS中的转场，是场景切换时的动画效果，目前支持 Fade和Switch等多种效果。</p>
<h3 id="OBS输入源的种类">OBS输入源的种类</h3><p>一个场景可以包含多个输入源，一个直播工具可以支持的输入源种类反应了其强大性。OBS支持 输入源种类如下<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/2016121460496obs_source_type.png" alt="2016121460496obs_source_type.png"></p>
<h3 id="OBS输入源的效果设置">OBS输入源的效果设置</h3><p>针对每个输入源可以增加各种滤镜效果,以下列出我觉得最实用的几种：<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/2016122283761OBS_Effect.png" alt="2016122283761OBS_Effect.png"></p>
<ol>
<li>音频效果：<ol>
<li>Video Delay: 设置延迟时间，用来处理音视频不同步的的场景。</li>
<li>Noise Suppression: 噪音抑制</li>
<li>Gain: 音频增益</li>
<li>Noise Gate: 噪声门，把小噪音去掉</li>
</ol>
</li>
<li>视频效果:<ol>
<li>Crop: 就是最实用的Crop，不过OBS里不能用鼠标拖拽来控制Crop区域，略显不便</li>
<li>Chroma Key: 如果有绿幕背景，可以用来去背景，在摄像头的输入源中最常用。</li>
<li>Image Mask: 打水印</li>
<li>Scroll: 滚动效果，在一些浏览器的输入源上最实用。</li>
</ol>
</li>
</ol>
<h3 id="OBS工作室模式">OBS工作室模式</h3><p><img src="http://7xpbra.com1.z0.glb.clouddn.com/2016122215478OBS_StdioMode.png" alt="2016122215478OBS_StdioMode.png"><br>左边是预览界面，可以进行编辑。右边是正在直播的界面。中间是把预览界面切到直播界面的各种转场效果。</p>
<p>一般比较专业的直播都是使用这个模式，可以在预览界面编辑好画面之后再推送到直播画面。</p>
<h2 id="OBS插件系统">OBS插件系统</h2><p>OBS项目中把除了核心框架以及渲染系统之外的 部件全部抽象成了Module，一个或多个Module最后封装到插件中(以dll或so的形式)，只要把插件放入特定的目录即可被主程序使用。</p>
<p>Mac版OBS的插件目录在/Applications/OBS.app/Contents/Resources/obs-plugins,其中</p>
<ol>
<li>mac-avcapture.so 对应Mac的视频捕获设备</li>
<li>mac-capture.so 对应屏幕捕获 和 窗口捕获</li>
<li>mac-syphon.so 对应注入捕获游戏画面</li>
</ol>
<h3 id="OBS插件定义">OBS插件定义</h3><p>一个典型的OBS插件代码包含三个部分：</p>
<ol>
<li>插件定义 -&gt; plugin-main.c</li>
<li>编译打包 -&gt; CMakeList.txt</li>
<li>内部实现代码 -&gt; XX.c/YY.c …</li>
</ol>
<p>下面以mac-capture.so插件为例来看看它的插件定义代码</p>
<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><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="preprocessor">#<span class="keyword">include</span> &lt;obs-module.h&gt;</span></span><br><span class="line"></span><br><span class="line">OBS_DECLARE_MODULE()</span><br><span class="line">OBS_MODULE_USE_DEFAULT_LOCALE(<span class="string">"mac-capture"</span>, <span class="string">"en-US"</span>) <span class="comment">//多语言支持</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">extern</span> <span class="keyword">struct</span> obs_source_info coreaudio_input_capture_info; <span class="comment">//输入源1</span></span><br><span class="line"><span class="keyword">extern</span> <span class="keyword">struct</span> obs_source_info coreaudio_output_capture_info;<span class="comment">//输入源2</span></span><br><span class="line"><span class="keyword">extern</span> <span class="keyword">struct</span> obs_source_info display_capture_info;<span class="comment">//输入源3</span></span><br><span class="line"><span class="keyword">extern</span> <span class="keyword">struct</span> obs_source_info window_capture_info;<span class="comment">//输入源4</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">obs_module_load</span><span class="params">(<span class="keyword">void</span>)</span> <span class="comment">//注册支持的输入源</span></span><br><span class="line"></span>&#123;</span><br><span class="line">	obs_register_source(&amp;coreaudio_input_capture_info);</span><br><span class="line">	obs_register_source(&amp;coreaudio_output_capture_info);</span><br><span class="line">	obs_register_source(&amp;display_capture_info);</span><br><span class="line">	obs_register_source(&amp;window_capture_info);</span><br><span class="line">	<span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>可以看出这个插件定义了四个输入源，这里你可能有个疑问，为什么不是一个插件 对应 一个输入源，因为功能相近的输入源集成到一个插件里可以减少冗余代码。</p>
<p>所以OBS中插件可以定义为 包含 一个 或 多个 输入(或 输出/编码/服务)模块的动态库代码。<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/2016121549583obs_module_define.png" alt="2016121549583obs_module_define.png"><br>任意一个开源库比如FFmpeg,经过OBS统一的接口定义封装即可编译成OBS的一个插件为OBS系统所用。</p>
<h3 id="OBS插件加载流程">OBS插件加载流程</h3><p>插件系统大体都有一个类似的套路，OBS的也不例外。简单来说就是定义插件存放在特定目录，在程序启动时，动态加载所有的插件(存储为对象或一系列函数指针)，存储在字典 或者 链表这样的数据结构里。</p>
<p>下面来详细分析一下OBS中插件加载流程：<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/2016121573630OBS_import_module-2.png" alt="2016121573630OBS_import_module-2.png"></p>
<p>然后以mac-capture.so中的display_capture_info，来看看它的结构定义,可以看出它主要定义了id、type、name 以及一些接口API。</p>
<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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> obs_source_info display_capture_info = &#123;</span><br><span class="line">	.id             = <span class="string">"display_capture"</span>,</span><br><span class="line">	.type           = OBS_SOURCE_TYPE_INPUT,</span><br><span class="line">	.get_name       = display_capture_getname,</span><br><span class="line"></span><br><span class="line">	.create         = display_capture_create,</span><br><span class="line">	.destroy        = display_capture_destroy,</span><br><span class="line"></span><br><span class="line">	.output_flags   = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW |</span><br><span class="line">	                  OBS_SOURCE_DO_NOT_DUPLICATE,</span><br><span class="line">	.<span class="keyword">video_t</span>ick     = <span class="keyword">display_capture_video_t</span>ick,</span><br><span class="line">	.video_render   = display_capture_video_render,</span><br><span class="line"></span><br><span class="line">	.get_width      = display_capture_getwidth,</span><br><span class="line">	.get_height     = display_capture_getheight,</span><br><span class="line"></span><br><span class="line">	.get_defaults   = display_capture_defaults,</span><br><span class="line">	.get_properties = display_capture_properties,</span><br><span class="line">	.update         = display_capture_update,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>其中需要注意一下get_properties这个接口，这个接口是干啥用的？顾名思义是获取模块的属性数据，按我的理解 UI层可以利用这个属性数据来构建这个模块对应的界面，并设置这个模块的属性参数。</p>
<h3 id="OBS视频处理流程">OBS视频处理流程</h3><h4 id="视频渲染输出流程">视频渲染输出流程</h4><p>OBS视频渲染和输出是系统的核心流程，我们以Mac桌面录制输入，以及ffmpeg输出为例来分析一下整个流程(多路输入 和 多路输出道理也是类似的), 图中为了简单起见忽略了输出编码流程仅包含非编码流程。<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/2016122060378OBS_Video_Render_2.png" alt="2016122060378OBS_Video_Render_2.png"><br>可以看出OBS创建了两个线程，一个用于显示渲染，另一个用于编码输出。<br>渲染部分最终调用的是 输入模块里的渲染代码，而编码输出部分最终也是调用 输出模块的代码。</p>
<p><strong>另外在渲染线程中 也负责把图形系统的数据 拷贝到 输出数据的缓存中，以便于输出线程进行处理</strong>。</p>
<h4 id="视频输出数据结构分析">视频输出数据结构分析</h4><p>OBS的核心数据结构定义在libobs/obs-internal.h中 主体结构为obs_core如下图所示(仅保留的主要的数据结构)<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/2016121631085OBS_CoreVideo-2.jpg" alt="2016121631085OBS_CoreVideo-2.jpg"></p>
<p>右下方的video(结构为video_output)用在输出模块的raw_video接口进行处理，把 输出数据中的cache转成实际的输出，以下是video_output详细数据结构：<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/2016121665597OBS_Video_detail_2.jpg" alt="2016121665597OBS_Video_detail_2.jpg"></p>
<h3 id="OBS音频处理流程">OBS音频处理流程</h3><p>OBS音频处理是在一个线程中完成了先渲染后输出的过程。而视频处理则是 分别开了渲染线程 和 输出线程。</p>
<p>具体流程如下, 在输出函数中在判断是否需要编码，再调用对应的非编码流程 或 编码流程：<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/2016122068454OBS_Audio_Render.png" alt="2016122068454OBS_Audio_Render.png"></p>
<h1 id="OBS图形系统架构">OBS图形系统架构</h1><p>OBS的图形系统主要负责 场景的渲染、场景的切换、以及各种输入源的音视频效果的处理，属于OBS的核心之一。<br>通过使用软件以及视频渲染流程的分析 得到OBS图形系统的大体的逻辑关系。</p>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/2016122324058OBS_Graphic_Structure_1.png" alt="2016122324058OBS_Graphic_Structure_1.png"></p>
<p>针对图形系统主要分析以下三个问题：</p>
<ol>
<li>多滤镜叠加的渲染处理。</li>
<li>滤镜和转场效果的实现与集成。</li>
<li>图形API的封装。</li>
</ol>
<h2 id="单个场景的渲染流程">单个场景的渲染流程</h2><p>场景(Scene)也被封装成输入源(Source)的一种,所以UI层只要把当前的场景取出来，调用它的obs_source_video_render即可。</p>
<p><img src="http://7xqmjb.com1.z0.glb.clouddn.com/2017011791095OBS_sceneRender.png" alt="2017011791095OBS_sceneRender.png"></p>
<p>在场景内部会渲染其包含的renderitem(也就是实际的输入源),比如前一个图所展示的游戏录制、桌面录制输入源等。</p>
<h2 id="多个滤镜叠加的输入源渲染流程">多个滤镜叠加的输入源渲染流程</h2><p>这部分分析了很长时间一直没看懂，主要有两个原因：</p>
<ol>
<li>之前不熟悉OpenGl的渲染流程，所以搞不懂滤镜的渲染流程。</li>
<li>这部分的逻辑比较绕，没分析出多个滤镜是怎么叠加渲染的。</li>
</ol>
<p>前段时间花了点时间好好学习了一下OpenGl(仅仅学习和音视频处理相关的章节)，写了一些<a href="https://github.com/fancymax/LearnOpenGL_Mac" target="_blank" rel="external">demo</a>。</p>
<p>现在再来分析这部分相对轻松一些，简述一下：当渲染带滤镜的输入源时，会先渲染它最后一个滤镜，然后在这个滤镜的渲染代码又会调用渲染前一个滤镜，最后调用第一个滤镜的渲染代码。</p>
<p>在第一个滤镜的渲染代码里 直接渲染 调用输入源的渲染流程，然后生成texture。</p>
<p>每个滤镜都在前一个滤镜渲染生成的texture的基础渲染生成新的texture。</p>
<p>流程图如下：<br><img src="http://7xqmjb.com1.z0.glb.clouddn.com/2017011799373OBS_filterRender.png" alt="2017011799373OBS_filterRender.png"></p>
<h2 id="滤镜和转场效果的实现与集成">滤镜和转场效果的实现与集成</h2><p>OBS项目中，滤镜和转场效果都被抽象成插件。</p>
<p>以Mac版OBS为例:</p>
<ol>
<li>所有的滤镜都在obs-filters.so这个插件里;</li>
<li>所有的转场效果都在obs-transitions.so里;</li>
</ol>
<p>滤镜和转场其实分析起来是类似的，所以后续的暂时以滤镜为例来加以说明。</p>
<p>首先如果滤镜个数太多，拆分到两个插件里是没问题的。不过OBS项目中全部集中在一个插件里。</p>
<p>每个滤镜其实都被定义成了输入模块，以obs_source_info定义暴露API，以crop_filter为例见如下定义，唯一和普通输入模块不同的是类型定义(.type)。 </p>
<figure class="highlight nix"><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></pre></td><td class="code"><pre><span class="line">struct obs_source_info <span class="variable">crop_filter =</span> &#123;</span><br><span class="line">	.<span class="variable">id                            =</span> <span class="string">"crop_filter"</span>,</span><br><span class="line">	.<span class="variable">type                          =</span> OBS_SOURCE_TYPE_FILTER,</span><br><span class="line">	.<span class="variable">output_flags                  =</span> OBS_SOURCE_VIDEO,</span><br><span class="line">	.<span class="variable">get_name                      =</span> crop_filter_get_name,</span><br><span class="line">	.<span class="variable">create                        =</span> crop_filter_create,</span><br><span class="line">	.<span class="variable">destroy                       =</span> crop_filter_destroy,</span><br><span class="line">	.<span class="variable">update                        =</span> crop_filter_update,</span><br><span class="line">	.<span class="variable">get_properties                =</span> crop_filter_properties,</span><br><span class="line">	.<span class="variable">get_defaults                  =</span> crop_filter_defaults,</span><br><span class="line">	.<span class="variable">video_tick                    =</span> crop_filter_tick,</span><br><span class="line">	.<span class="variable">video_render                  =</span> crop_filter_render,</span><br><span class="line">	.<span class="variable">get_width                     =</span> crop_filter_width,</span><br><span class="line">	.<span class="variable">get_height                    =</span> crop_filter_height</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>细心观察每个滤镜的代码组成发现都是一个套路,主要由两部分组成:</p>
<ol>
<li>XXX.c  (C代码，用于暴露API以及例行处理)</li>
<li>XXX.effect (自定义文件，实际效果处理逻辑)</li>
</ol>
<p>关于这个effect留到后续讲解。</p>
<p>看到这里得出一个结论，OBS项目要增加新的滤镜效果只要编写对应的XXX.c 和 XXX.effect，放到对应的插件以输入模块的API暴露出来，并注册就可以了。</p>
<h2 id="图形API的封装处理">图形API的封装处理</h2><p>目前OBS系统的图形API包括OpenGl以及d3d11:</p>
<ol>
<li>在图形库加载层利用了<a href="https://github.com/Dav1dde/glad" target="_blank" rel="external">Multi-Language GL/GLES/EGL/GLX/WGL Loader-Generator</a>对不同平台加载图形库代码进行了封装。</li>
<li>在API调用层面也进行了抽象统一，具体可以查看 libobs/graphics/graphics-imports.c的定义。</li>
<li>自定义了效果描述文件 XXX.effect，这样就不用针对OpenGl和d3d11写两遍Shader。</li>
</ol>
<p>我们以chroma-key-filter为例分析一下它的创建流程:<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/2016122616450OBS_filter_create.png" alt="2016122616450OBS_filter_create.png"></p>
<ol>
<li>其中AddNewFilter是在界面中触发的添加效果的功能。</li>
<li>ep_parse把xxx.effect配置文件解析成对应的配置结构。</li>
<li>ep_compile把对应的配置结构解析 效果数据结构。</li>
</ol>
<p>effect文件的作用可以参考程序中的注释</p>
<figure class="highlight applescript"><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">/*</span><br><span class="line"> * Effects introduce a means <span class="keyword">of</span> bundling together shader <span class="type">text</span> <span class="keyword">into</span> one</span><br><span class="line"> * <span class="type">file</span> <span class="keyword">with</span> shared functions <span class="keyword">and</span> parameters.  This <span class="keyword">is</span> done because often</span><br><span class="line"> * shaders must be duplicated when you need <span class="keyword">to</span> alter minor aspects <span class="keyword">of</span> <span class="keyword">the</span> code</span><br><span class="line"> * <span class="keyword">that</span> cannot be done via constants.  Effects allow developers <span class="keyword">to</span> easily</span><br><span class="line"> * switch shaders <span class="keyword">and</span> <span class="keyword">set</span> constants <span class="keyword">that</span> can be used <span class="keyword">between</span> shaders.</span><br><span class="line"> *</span><br><span class="line"> * Effects are built via <span class="keyword">the</span> effect parser, <span class="keyword">and</span> shaders are automatically</span><br><span class="line"> * generated <span class="keyword">for</span> each technique's pass.</span><br><span class="line"> */</span><br></pre></td></tr></table></figure>
<p>effect文件包括这几个部分:<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/2016122616242effect_file_structure.png" alt="2016122616242effect_file_structure.png"></p>
<p>pass对应 vertex_shader 和 pixel_shader</p>
<p>technique 对应一个具体效果的渲染设置，包含多个pass</p>
<p>effect文件包含多个technique渲染设置、可以共享文件中的参数和函数。</p>
<figure class="highlight ceylon"><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></pre></td><td class="code"><pre><span class="line">SolidVertInOut VSSolid(SolidVertInOut vert<span class="number">_</span><span class="keyword">in</span>)</span><br><span class="line">&#123;</span><br><span class="line">	SolidVertInOut vert<span class="number">_</span><span class="keyword">out</span>;</span><br><span class="line">	vert<span class="number">_</span><span class="keyword">out</span>.pos = mul(float<span class="number">4</span>(vert<span class="number">_</span><span class="keyword">in</span>.pos.xyz, <span class="number">1.0</span>), ViewProj);</span><br><span class="line">	<span class="keyword">return</span> vert<span class="number">_</span><span class="keyword">out</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">float<span class="number">4</span> PSSolid(SolidVertInOut vert<span class="number">_</span><span class="keyword">in</span>) : TARGET</span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">return</span> color;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">technique Solid</span><br><span class="line">&#123;</span><br><span class="line">	pass</span><br><span class="line">	&#123;</span><br><span class="line">		vertex<span class="number">_</span>shader = VSSolid(vert<span class="number">_</span><span class="keyword">in</span>);</span><br><span class="line">		pixel<span class="number">_</span>shader  = PSSolid(vert<span class="number">_</span><span class="keyword">in</span>);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>effect文件支持基本的C语法，支持宏定义和include包含其他文件，由libobs/util中的cf-lexer.c和cf-parser.c提供解析支持。</p>
<h1 id="支持OBS插件">支持OBS插件</h1><p>OBS的插件是在OBS项目定义的比较宽泛，插件的范畴包括 整个录屏、处理、推流 中的各个功能模块.</p>
<p>如果我们的软件中可以直接支持OBS插件，就可以节省大量的开发、测试的时间。但由于我们的程序框架和OBS的完全不同，要如何支持OBS项目的插件呢？</p>
<p>想了两种方法，并尝试分析一下优缺点。</p>
<h2 id="支持OBS的方法分析">支持OBS的方法分析</h2><h3 id="二进制级别的支持">二进制级别的支持</h3><p>顾名思义，就是把OBS的插件直接放到我们程序的相应目录就可以用。这种方式下维护、更新、新增 OBS插件 代价是最小的。也是我心中理想的支持方式。</p>
<p>但是以这种方式支持的遇到较大困难。先看OBS项目中的代码的各个模块：</p>
<ol>
<li>libobs: 核心代码，定义项目框架以及核心API，主要用C语言编写。</li>
<li>UI: 界面代码，采用C++的QT框架，开发出适用三大平台的界面。</li>
<li>plugins: 插件代码，可独立编译成dll(windows平台)或so(*nix平台)，包含Source(录屏输入源)、Output、Service(各种流播服务)等全部被定义为插件。</li>
<li>libobs-d3d11: 基于D3D的图形子系统，主要用在Windows系统。</li>
<li>libobs-opengl: 基于opengl的图形子系统，主要用在*uix系统。</li>
</ol>
<p>我们想要支持plugins中的插件，</p>
<p>但是plugins中的插件要依赖libobs，</p>
<p>而libobs又要依赖libobs-opengl 和 QT界面库。</p>
<p>也就是除非 我们的项目支持基于OBS项目改写，否则这种支持方式的不太现实。</p>
<h3 id="代码级别的支持">代码级别的支持</h3><p>这是退而求其次的方式，简单的说就是把OBS的插件代码扣出来，确保其不依赖于libobs，然后集成我们的项目中。这种方式每支持一个插件都存在集成的工作量，也可能会引入Bug，不过不失为一个较为可行的方案。</p>
<h3 id="OBS项目编译">OBS项目编译</h3><p>尝试了在Mac平台上编译OBS项目，还比较顺利。具体可以参考<a href="https://github.com/jp9000/obs-studio/wiki/Install-Instructions#mac-osx" target="_blank" rel="external">install help</a></p>
<p>有个小问题，在cmake后报错提示无法找到QT5的cmake模块。需要给cmake指定一下QT5的安装目录，以我的安装目录为例，命令如下：<br><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cmake .. -DCMAKE_PREFIX_PATH=<span class="regexp">/usr/</span>local<span class="regexp">/Cellar/</span>qt5<span class="regexp">/5.3.1/</span></span><br></pre></td></tr></table></figure></p>
<p>cmake命令我是不熟悉的，不过看了<a href="http://hahack.com/codes/cmake/" target="_blank" rel="external">这篇文章</a>也基本懂了。</p>
<p>OBS项目最后的编译结果如下：</p>
<p><img src="http://7xqmjb.com1.z0.glb.clouddn.com/201702058452OBS_Compile.png" alt="201702058452OBS_Compile.png"></p>
<p>主要有三个目录：</p>
<ol>
<li>bin:  主程序</li>
<li>data: 国际化资源 以及 视频effect效果资源</li>
<li>obs-plugin: 插件编译结果</li>
</ol>
<p>使用otool -L分析中其中主要动态库和插件(仅以mac-capture为例)的依赖关系如下:</p>
<p><img src="http://7xqmjb.com1.z0.glb.clouddn.com/2017020524659OBS_dependency.png" alt="2017020524659OBS_dependency.png"></p>
<p>前面在代码层面分析直接二进制支持OBS插件感觉很困难。但这里基于编译后的动态库依赖关系分析好像又有一定可行性。我们把mac-capture.so、libobs.0.dylib、ffmpeg独立出来，去掉OBS主程序和QT等库，自己写代码来调用libobs.0.dylib提供的功能，以此直接支持OBS的插件。</p>
<p>后续进行完相关的实验，看看到底是否可行，再来补充。</p>
<h3 id="模块列表">模块列表</h3><p>OBS项目中类型为OBS_SOURCE_TYPE_INPUT是我们可以考虑优先支持的模块。以下是功能说明。</p>
<table>
<thead>
<tr>
<th style="text-align:center">插件</th>
<th style="text-align:center">子模块</th>
<th style="text-align:center">功能描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">mac-capture</td>
<td style="text-align:center">coreaudio_input_capture</td>
<td style="text-align:center">音频输入获取</td>
</tr>
<tr>
<td style="text-align:center"></td>
<td style="text-align:center">coreaudio_output_capture</td>
<td style="text-align:center"></td>
</tr>
<tr>
<td style="text-align:center"></td>
<td style="text-align:center">display_capture</td>
<td style="text-align:center">桌面获取</td>
</tr>
<tr>
<td style="text-align:center"></td>
<td style="text-align:center">window_capture</td>
<td style="text-align:center">窗体获取</td>
</tr>
<tr>
<td style="text-align:center">mac-avcapture</td>
<td style="text-align:center">av_capture</td>
<td style="text-align:center">摄像头获取</td>
</tr>
<tr>
<td style="text-align:center">mac-syphon</td>
<td style="text-align:center">syphon</td>
<td style="text-align:center">程序注入获取界面</td>
</tr>
<tr>
<td style="text-align:center">obs-ffmpeg</td>
<td style="text-align:center">ffmpeg_source</td>
<td style="text-align:center">ffmpeg输入源</td>
</tr>
<tr>
<td style="text-align:center">obs-browser</td>
<td style="text-align:center">browser_source</td>
<td style="text-align:center">浏览器输入</td>
</tr>
<tr>
<td style="text-align:center">text-freetype2</td>
<td style="text-align:center">freetype2_source</td>
<td style="text-align:center">text输入</td>
</tr>
<tr>
<td style="text-align:center">decklink</td>
<td style="text-align:center">decklink-input</td>
<td style="text-align:center"></td>
</tr>
<tr>
<td style="text-align:center">image-source</td>
<td style="text-align:center">image_source</td>
<td style="text-align:center">图片输入</td>
</tr>
<tr>
<td style="text-align:center"></td>
<td style="text-align:center">slideshow</td>
<td style="text-align:center"></td>
</tr>
<tr>
<td style="text-align:center">vlc-video</td>
<td style="text-align:center">vlc_source</td>
<td style="text-align:center">vlc输入</td>
</tr>
</tbody>
</table>
<h1 id="mac-syphon插件分析">mac-syphon插件分析</h1><p>mac-syphon是OBS项目中用于获取游戏画面(仅用于mac平台)的插件，十分重要。下面来分析一下它的实现原理。</p>
<p>首先mac-syphon是OBS的输入源插件，所以遵循OBS的插件API设定，具体可以查看OBS插件分析章节的介绍。<br>mac-syphon内部其实组合了多个开源项目功能来完成:获取游戏画面，并展示到OBS界面上的功能。</p>
<p>我们以OBS获取MineCraft这个游戏的画面为例，来看一张总的实现原理图:<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/2017010524004OBS_SyphonInject.png" alt="2017010524004OBS_SyphonInject.png"></p>
<p>简单解释一下这个过程：</p>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/2017010568862OBS_SyphonInject_flow.png" alt="2017010568862OBS_SyphonInject_flow.png"></p>
<p>注入游戏进程的方法这里用的是Scripting Additions的方式，这是macOS独有的技术，windows上肯定要用其他方式，到时候再单独研究。除了注入方式的区别，其他流程Win和Mac平台应该类似的。</p>
<p>另外由于注入的函数中替换的是OpenGL的渲染API，所以这个插件支持的游戏必然是使用OpenGL渲染的。假如某个程序或游戏不使用OpenGL则无法注入。</p>
<h2 id="ScriptingAdditions">ScriptingAdditions</h2><p>ScriptingAdditions就是macOS中Applescript中一个技术，不太好解释，反正它的作用就是帮助注入到游戏进程里，直接上两个文档:</p>
<ol>
<li><a href="https://developer.apple.com/library/content/technotes/tn1164/_index.html" target="_blank" rel="external">Scripting Additions for Mac OS X</a></li>
<li><a href="https://developer.apple.com/library/content/documentation/AppleScript/Conceptual/AppleScriptX/Concepts/osa.html" target="_blank" rel="external">Open Scripting Architecture</a></li>
</ol>
<h2 id="mach_override">mach_override</h2><p>这里其实利用了两个项目<a href="https://github.com/rentzsch/jrswizzle" target="_blank" rel="external">jrswizzle</a>和<a href="https://github.com/rentzsch/mach_override" target="_blank" rel="external">mach_override</a>,功能就是把游戏进程中的flushBuffer替换为自己写的flushBufferSyphon，把orig_CGLFlushDrawable替换为CGLFlushDrawableOverride，从而实现把自己写的功能注入到游戏的渲染API中。</p>
<h2 id="Syphon">Syphon</h2><p><a href="https://github.com/Syphon" target="_blank" rel="external">Syphon</a>项目是一套传输图形画面的Client/Server框架。</p>
<p>项目还提供了<a href="https://github.com/Syphon/Simple" target="_blank" rel="external">Client/Server Demo</a>可以很方便的测试画面传输的功能。</p>
<h2 id="Syphon_Inject">Syphon Inject</h2><p><a href="https://github.com/zakk4223/SyphonInject" target="_blank" rel="external">SyphonInject</a>项目组合Syphon的功能以及注入游戏的功能，提供了一个Demo。</p>
<p>下图中我用Syphon Inject注入到Dota2游戏，然后把界面传送给<a href="https://github.com/Syphon/Simple" target="_blank" rel="external">Client Demo</a>。</p>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/2017010588377SyphonInject_1.png" alt="2017010588377SyphonInject_1.png"></p>
<p><img src="http://7xpbra.com1.z0.glb.clouddn.com/2017010548405SyphonInject_2.png" alt="2017010548405SyphonInject_2.png"></p>
<p><strong>SyphonInject编译注意事项</strong>: SyphonInject项目早期利用mach_inject项目进行注入，后期已经修改为ScriptingAdditions方式注入。最新的代码没有依赖mach_inject，所以可以把mach_inject项目依赖去掉再编译。</p>
<p>最后OBS项目其实上述项目组合，用插件的API包装成了mac-Syphon插件。</p>
<h1 id="mac-capture插件分析">mac-capture插件分析</h1><p>mac-capture插件是OBS项目中对应mac平台的 屏幕界面获取、窗口界面获取、输入音频获取、输出音频获取 四大模块的具体实现。</p>
<p>obs模块的具体结构和API不再列出，详细情况可查看之前的文档，这里着重分析模块内部的功能实现。</p>
<h2 id="mac-display模块">mac-display模块</h2><p>主要利用<a href="https://developer.apple.com/reference/coregraphics/1655858-quartz_display_services" target="_blank" rel="external">Quartz Display Services</a>获取界面图像数据转换成texuture提供给主程序。</p>
<p>其中调用CGDisplayStreamCreateWithDispatchQueue接口获取的显示界面图像数据，数据结构是IOSurfaceRef。具体用法可以查看上述Quartz Display Services链接文档。</p>
<p>主要的实现流程如下图：<br><img src="http://7xqmjb.com1.z0.glb.clouddn.com/2017011827400OBS_macDisplay.png" alt="2017011827400OBS_macDisplay.png"></p>
<h2 id="mac-window-capture模块">mac-window-capture模块</h2><p>获取其他程序窗体界面的模块，也是利用了Quartz Display Services。</p>
<p>使用CGWindowListCreateImage接口，通过windowID把对应程序的界面以 CGImage 的形式返回,直接放到输出的Cache里。</p>
<p>这里有个疑问，模块没有实现.video_render这个渲染API。可能和模块的output_flags设置的是异步有关。<br>OBS_SOURCE_ASYNC_VIDEO异步的渲染流程可能未放到模块内部实现。</p>
<h2 id="mac-audio模块">mac-audio模块</h2><p>音频模块包括两个，音频输入模块 和 音频输出模块。</p>
<p>音频输入设备比如 iMac上自带的外置麦克风；音频输出模块 比如 插入的耳机等。</p>
<p>由于mac平台限制，无法直接录制 音频输出设备的声音。比如我要录制浏览器上youtube视频的声音，在不借助第三方程序的情况下是做不到。使用OBS也做不到。</p>
<p>不过利用第三方开源程序soundflower可以解决这个问题。在安装soundflower之后，OBS可以设置 音频输出捕获模块，设备选择soundflower。同时系统声音输出设备 也选择为soundflower。<br><img src="http://7xqmjb.com1.z0.glb.clouddn.com/2017011845343OBS_audioOutput.png" alt="2017011845343OBS_audioOutput.png"><br><img src="http://7xqmjb.com1.z0.glb.clouddn.com/2017011866291OBS_audioOutput1.png" alt="2017011866291OBS_audioOutput1.png"></p>
<p>这样系统的任何程序发出的声音就会先经过soundflower，然后被OBS捕获。</p>
<p>mac-audio中的音频输入模块 和 输出模块 实现流程基本相同，如下所示：<br><img src="http://7xqmjb.com1.z0.glb.clouddn.com/2017011826851obs_macAudio.png" alt="2017011826851obs_macAudio.png"></p>
<p>基本原理是 在初始化模块的时候 使用系统接口AudioObjectAddPropertyListener注册音频的回调函数。然后在回调函数中完成音频渲染以及输出音频数据缓存。</p>
]]></content:encoded>
      <comments>http://fancymax.github.io/2017/10/28/obs/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[亲密关系]]></title>
      <link>http://fancymax.github.io/2017/07/10/relation/</link>
      <guid>http://fancymax.github.io/2017/07/10/relation/</guid>
      <pubDate>Mon, 10 Jul 2017 14:01:31 GMT</pubDate>
      <description>
      <![CDATA[<h2 id="吸引力">吸引力</h2><ul>
<li><p>要进一步探讨的微妙之处是，某些相像或许十分重要，而其他的相像 (或者相异) 可能无关痛痒。尤其是，如果伴侣在一些重要的议题上能和我们看法一致，这将特别具有奖赏价值。宗教就是这样一个议题：如果伴侣双方在宗教上都非常虔]]>
      </description>
      <content:encoded><![CDATA[<h2 id="吸引力">吸引力</h2><ul>
<li><p>要进一步探讨的微妙之处是，某些相像或许十分重要，而其他的相像 (或者相异) 可能无关痛痒。尤其是，如果伴侣在一些重要的议题上能和我们看法一致，这将特别具有奖赏价值。宗教就是这样一个议题：如果伴侣双方在宗教上都非常虔诚，共同的宗教信仰就非常有满足意义；而如果双方都不是积极的信徒，宗教上的相像就没什么影响，甚至有分歧也不打紧 (Lutz-Zois et al., 2006)。</p>
<ul>
<li>两个人三观要相同</li>
</ul>
</li>
<li><p>“相异”还能相吸的一种方式：互朴性</p>
<ul>
<li>一个愿打一个愿挨</li>
</ul>
</li>
</ul>
<h2 id="社会认知">社会认知</h2><ul>
<li><p>另外来看，如果人们了解伴侣各方面的情况，但却能以一种善意、大度的方式来进行诠释，这样的“错觉”就对亲密关系十分有益。</p>
</li>
<li><p>此外，保护自己免遭幻想破灭的聪明方法是：随着对伴侣了解程度的增加，不断调整自己对理想伴侣的期望，这样，对伴侣的期望标准就能切合伴侣的现状 (Fletcher et al., 2000a)。</p>
</li>
<li><p>人们常常能深切地感受到影响自己行为的外部压力，因而对自己行为的解释容易作出外部归因。但他们注意不到同样的环境也会影响他人，从而在解释他人的行为时，常常归因于他们的内部原因，如意图或性格。</p>
</li>
<li><p>即使苦闷的夫妻彼此示好，但双方都会认为对方的体贴只不过是消极常态中短暂的、不具代表性的片刻安宁。当善意被看做是偶然的，伤害被看做蓄意的，亲密关系就很难得到满足。</p>
</li>
<li><p>不良的归因方式会引起更多的纠纷和降低解决问题的效率，从而导致了那些本可避免的失落和不满。</p>
</li>
<li><p>不过，在相互依赖更多、更投入的人际关系 (如婚姻) 中，自我证实的动机居于主导 (这种现象叫婚姻转变)，人们需要支持他们自我概念的反馈 (Swann et al.,1994) (见图4.5)。</p>
</li>
<li><p>设想有位男士把从妻子那里得到的表扬都解读为过誉之词。虽然这些表扬起初让他觉得乐观而幸福，但如果他最后判断妻子只不过是言不由衷…或者干脆认为妻子是个傻子，正面光辉就会慢慢消退。无论哪种情况，从自己非常熟悉的人那里得到了过分称赞的评价，都会使自己不安、并认为对方很虚伪 (Swann, 1996，p_118) 。</p>
</li>
<li><p>显然自恋者结交的都是相当糟糕的伴侣，但要一开始就看穿他们却非常困难(Hotchkiss,2003)。他们的自信早先是能打动人的，常常要花很多时间才能认识到他们是多么自私、小气和暴躁。所以自恋者常以“致命的吸引”(fatalattraction)形式出现，一开始或许具有吸引力，但长期来看却是要人命的(Brunelletal.,2004)。这就给人们带来了挑战，在判断未来的亲密伴侣时要尽可能地运用辨别力和洞察力。</p>
</li>
<li><p>举一点来说，我们都知道自己的好友和爱人已经喜欢上我们，所以营造迷人形象以贏得他们赞许的动机不足。也可能是因为他们十分了解我们，要改变他们的看法比较困难。不过，还有可能仅仅是因为我们变懒了。要表现出最好的言谈举止需要专心和努力。有礼貌的举止通常意味着某种形式的自我约束。在已经了解并喜爱我们的人身边，我们可以放松，无拘无束。但这也意味着人们对亲密伴侣常比对认识的其他人更为坦荡不羁 (Miller, 1997b)。</p>
</li>
<li><p>一般而言，关系越亲近，人们越把好友的形象当成自己的，只要有可能就尽量美化好友的形象。</p>
</li>
<li><p>有些人或许很难被人了解，但也有些人却善于观察别人。具备优秀社交技能的人往往擅长于评价和判断他人 (Letzring, 2008),这可能是因为他们的情绪智力 (e-motional intelligence) 很高。情绪智力指人们觉知、利用、理解和管理情绪的能力 (Salovey &amp; Grewal, 2005)。如果人们有较高的情绪智力，就能驾轻就熟地调控自己的情感，从而很少在遭受打击和挫折时反应过度。他们还能敏感地体验到别人的感受，所以他们的人际交往更加满意和亲密 (Mirgain &amp; Cordova, 2007)</p>
</li>
</ul>
<h2 id="友谊">友谊</h2><ul>
<li><p>当同居的情侣有一方正在准备紧张的律师资格考试时，双方把各自给予和得到的支持都记录下来，结果发现最能减少应试者焦虑的，是情侣提供而应试者并未觉察到的帮助 (Bolger et al., 2000)。</p>
</li>
<li><p>当受援者觉察到有形的支持时，如果这种支持能切合受援者当前的需要和目标就会更加有效。</p>
</li>
<li><p>当受援者觉察到有形的支持时，如果这种支持能切合受援者当前的需要和目标就会更加有效。另一项研究考察了备考律师资格的学生，结果发现物质支持——比如情侣为之下厨烹调——有帮助作用，而感情支持只会使应试者更加焦虑 (Shrout et al., 2006)。</p>
</li>
<li><p>事实上，如果夫妻双方没有共同的朋友，这样的婚姻通常很艰难。</p>
</li>
<li><p>只要我们活着，友谊就是无价之宝。</p>
</li>
</ul>
<h2 id="爱情">爱情</h2><ul>
<li><p>年轻人应该离开父母，自由地恋爱，自主地决定婚姻，并把恋人带回家与父母碰面，这样一种恋爱婚姻观，许多地方的人现在仍认为这是一种荒谬之极的婚恋观 (Macdonald &amp; Jessica, 2006)。</p>
</li>
<li><p>这些研究表明肾上腺素增强了人们的爱情体验。</p>
</li>
<li><p>对于成人，爱人的触碰会刺激催产素的释放，尤其在性高潮时会释放大量的催产素；催产素可能正是夫妻在做爱之后会感到放松和嗜睡的原因之一 (Floyd, 2006)。</p>
</li>
<li><p>浪漫的夫妻一起参加新奇、兴奋的活动会让他们彼此更加相爱</p>
</li>
<li><p>浪漫因新奇、神秘和危险而繁盛；却因了解熟识而消亡。</p>
</li>
<li><p>就浪漫的爱情而言，当伴侣变得熟悉时大脑可能根本无法产生足够多的多巴胺，所以即使你的伴侣能一如既往地完美，你也不能同样地被唤醒。</p>
</li>
<li><p>当爱情关系变得重复、单调和沉闷时就会止步不前，并非一出现坏事情就会发生厌倦，而是在婚姻生活变得没有情趣、难以让人兴奋或者没有挑战性时才会滋生厌倦 (Harasymchuk &amp; Fehr, 2007)。</p>
</li>
<li><p>这就是你的爱情策略。享受激情，但不要把它作为维持爱情关系的基础。培养与爱人之间的友谊。努力保持新鲜感；把握住每一个与配偶共同进行新奇探索的机会</p>
</li>
</ul>
<h2 id="压力与紧张">压力与紧张</h2><ul>
<li><p>的确，那些坚持认为“相异相吸”的人可能会得到一些重大教训，只要他们与差异显著的人生活在一起，相异只会增加摩擦，而不会让关系一帆风顺。</p>
</li>
<li><p>批评者的评论或行为所要表达的内容并不重要，要紧的是被批评者把这种行动诠释为不公正和吹毛求疵。伴侣一方提出如何使用洗碗机效率更高，这本来是一个温和的建议，但如果伴侣另一方认为这一建议是不必要的批评，就会受到伤害并引起冲突。</p>
</li>
<li><p>我们不要低估公平争斗和进行“优质”争斗的难度。这需要自律和对自己伴侣真正的关爱。但正面结果通常值得努力。因此，冲突并不是可怕的问题，而是具有挑战性的机遇——理解自己和伴侣的机会，个体亲密关系变得更满意更亲密的契机。</p>
</li>
</ul>
]]></content:encoded>
      <comments>http://fancymax.github.io/2017/07/10/relation/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[macOS命令行小秘笈]]></title>
      <link>http://fancymax.github.io/2017/06/10/macCommand/</link>
      <guid>http://fancymax.github.io/2017/06/10/macCommand/</guid>
      <pubDate>Sat, 10 Jun 2017 14:17:54 GMT</pubDate>
      <description>
      <![CDATA[<p>macOS基于Unix系统，日常使用总会用到一些Terminal命令用来提高工作效率，或更好的完成任务。</p>
<h2 id="开发类">开发类</h2><ul>
<li>查看<strong>Swift</strong>真正方法：</li>
</ul>
<p><code>]]>
      </description>
      <content:encoded><![CDATA[<p>macOS基于Unix系统，日常使用总会用到一些Terminal命令用来提高工作效率，或更好的完成任务。</p>
<h2 id="开发类">开发类</h2><ul>
<li>查看<strong>Swift</strong>真正方法：</li>
</ul>
<p><code>xcrun swift-demangle SYMBOL xxx</code></p>
<ul>
<li>检查执行程序或库的依赖以及依赖路径</li>
</ul>
<p><code>otool -L  &lt;Path&gt;</code></p>
<ul>
<li>动态库<strong>dylib</strong>无法加载的原因调试</li>
</ul>
<p><code>printf(dlerror());</code></p>
<ul>
<li>检查执行程序或库的导出函数</li>
</ul>
<p><code>nm -m &lt;Path&gt;</code></p>
<ul>
<li>查看使用了<strong>XPC</strong>的程序</li>
</ul>
<p><code>find /Applications/ -name \*.xpc &gt;&gt; xpc.txt</code></p>
<ul>
<li>卸载<strong>kext</strong>脚本</li>
</ul>
<p><code>sudo kextunload /Library/Extensions/xxx.kext</code></p>
<p><code>sudo rm -rf /Library/Extensions/xxx.kext</code></p>
<ul>
<li>检查程序是否签名</li>
</ul>
<p><code>codesign -vvv &lt;Path&gt;/xxx.app</code></p>
<ul>
<li>查看<strong>Swift</strong>版本</li>
</ul>
<p><code>xcrun swift -version</code></p>
<ul>
<li>默认xcode路径以及选择</li>
</ul>
<p><code>xcode-select -print-path</code></p>
<p><code>xcode-select -s /Applications/Xcode.app/Contents/Developer/</code></p>
<ul>
<li>Frameworks 的放置位置</li>
</ul>
<p>系统的Frameworks放在/System/Library/Frameworks/下;</p>
<p>一般App的Frameworks就放在App的Bundle里;</p>
<p>如果需要多个App共享Frameworks则可以放在/Library/Frameworks/下;</p>
<h2 id="日常使用">日常使用</h2><ul>
<li>卸载<strong>kext</strong>脚本</li>
</ul>
<p><code>sudo kextunload /Library/Extensions/xxx.kext</code></p>
<p><code>sudo rm -rf /Library/Extensions/xxx.kext</code></p>
<ul>
<li>检查程序是否签名</li>
</ul>
<p><code>codesign -vvv &lt;Path&gt;/xxx.app</code></p>
<ul>
<li><strong>10.12</strong>之后允许第三方App运行命令</li>
</ul>
<p><code>sudo spctl --master-disable</code></p>
<ul>
<li><strong>Mount DMG</strong></li>
</ul>
<p>命令自动挂载DMG包</p>
<p><code>hdiutil mount &lt;Path&gt;.dmg</code></p>
<p><code>hdiutil unmount &lt;Path&gt;</code></p>
<ul>
<li>重启<strong>FaceTime </strong>摄像头</li>
</ul>
<p><code>sudo killall VDCAssistant</code></p>
<ul>
<li>开启以太网服务</li>
</ul>
<p><code>networksetup  -setnetworkserviceenabled ethernet on</code></p>
<p><code>networksetup  -setnetworkserviceenabled ethernet off</code></p>
<ul>
<li>开启<strong>VPN</strong></li>
</ul>
<p><code>scutil --nc start &#39;hkVPN &#39; --user xxx --password yyy —secret zzz</code></p>
<p><code>scutil --nc stop  &#39;hkVPN &#39;</code></p>
<ul>
<li>屏幕共享</li>
</ul>
<p>在Finder -&gt; 前往连接服务器 -&gt; 输入</p>
<p><code>vnc://共享地址</code></p>
]]></content:encoded>
      <comments>http://fancymax.github.io/2017/06/10/macCommand/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[新文新民新世界-梁启超家族]]></title>
      <link>http://fancymax.github.io/2017/05/10/liang/</link>
      <guid>http://fancymax.github.io/2017/05/10/liang/</guid>
      <pubDate>Wed, 10 May 2017 14:06:40 GMT</pubDate>
      <description>
      <![CDATA[<ul>
<li>人、家庭、社会是无法分割的一个整体。不过，家庭和社会可以制约人，人却没有选择父母、家庭、社会或时代的自由。</li>
</ul>
<ul>
<li><p>然而，话又说回来，人是活的，有巨大的创造力，可以能动地、有限度地改变家庭乃至社会。</p>
</li>
<l]]>
      </description>
      <content:encoded><![CDATA[<ul>
<li>人、家庭、社会是无法分割的一个整体。不过，家庭和社会可以制约人，人却没有选择父母、家庭、社会或时代的自由。</li>
</ul>
<ul>
<li><p>然而，话又说回来，人是活的，有巨大的创造力，可以能动地、有限度地改变家庭乃至社会。</p>
</li>
<li><p>本书在阐述梁启超家族浮沉的过程中特别注重了两点：一是将梁氏家族在近百年的起伏与特殊的时代和社会变动有机结合起来，以大社会为背景来考察小家庭，以小家庭为窗口来理解大社会，妥善处理全貌和一点的关系；二是着力研究梁启超家族独有的特色和长期积淀的文化内涵，写出知识分子家庭的文化底蕴、文化氛围、文化品位和文化走向，也就是注重了梁氏家族文化个性的表述。</p>
</li>
</ul>
<h2 id="百年一梦-解读梁家">百年一梦-解读梁家</h2><ul>
<li><p>从梁启超的家庭文化中，可以切实感受到西方文化的潜移默化，更能够追寻到颇为深厚的中国文化底蕴，还会提炼出一种古今中西文化相融合后再合理升华的新的文化因子，感悟出近代中国新文化的一些新特点。</p>
</li>
<li><p>梁启超因病住进了当时中国最好的协和医院之后，该院院长亲自主刀为其手术，但由于将X光片看错了，他有病的肾没有被割掉，反而把好肾给割掉了，造成手术失误，让梁过早地离开了人世。</p>
</li>
<li><p>1925年7月10日，梁在给远在海外的孩子们的信中讲到对人生的体验时称：“其实我们刻刻在轮回中，一生不知经过多少天堂地狱。”[</p>
</li>
</ul>
<h2 id="探源——南国飞出个金凤凰">探源——南国飞出个金凤凰</h2><ul>
<li><p>耆老会设有自己的地方武装——乡团，由敢于争斗的青年自愿报名，耆老会批准。团民备受优待，分东西时可以领双份。每人或数人发一支枪，弹药则由值理统一保管。有盗卖枪支弹药者，必从重处罚。乡团定时操练，学一些武术和作战技能，有一定的战斗力。在维护茶坑村社会秩序方面，乡团具有举足轻重的地位；当遇到外来侵略，又可发挥组织民众，保卫家乡的积极作用。应该说，清末的乡团是较复杂而作用多变的乡村武装集团。</p>
</li>
<li><p>犯有奸淫罪者，村民最恨，要将全村的猪杀光，平分给每户，钱则由犯罪者偿付，美其名曰“倒猪”。</p>
</li>
<li><p>村里的娱乐活动，以正月十五的灯节和七月的祭神最为热闹。梁启超小时候，最喜欢这两个节日，往往不知疲倦地尽情玩耍。 1873年2月13日，当梁启超发出第一声啼哭时，给他提供的就是这样一个古朴、有序、守成、封闭而又缺少生机的小乡村的生活环境。</p>
</li>
</ul>
<h2 id="成也，政治；败也，政治">成也，政治；败也，政治</h2><ul>
<li><p>因为在梁启超看来，中国国民素质太低，如果一下子推行民主共和，不仅行不通，而且会天下大乱，唯一可行的方法是选择一位开明的专制“伟人”，既有集权的本领，又愿意一步一步向民主过渡。如此，社会不会大乱，民主政治也会逐步实现。在他的心目中，袁世凯就是这样的合适人选。</p>
</li>
<li><p>他满怀信心地扬言，如果这个内阁干不出一点名堂，或者在方针政策上出现什么失误，他“即行辞职”。梁启超亲自草拟了《政府大政方针宣言书》，洋洋万言，俨然要依法治国，全面推行资产阶级民主政治。具体方案是：第一，实行完全责任内阁制，划清总统府与国务院之权限；第二，司法独立，制订切合实际的法律；第三，重视教育；第四，军民分治，废省改道，整顿吏治，严定考试之制；第五，实施县和城镇或乡两级地方自治。</p>
</li>
</ul>
<h2 id="永垂不朽是文章">永垂不朽是文章</h2><ul>
<li><p>什么是历史的目的？简单一句话，历史的目的在将过去的真事实予以新意义或新价值，以供现代人活动之资鉴。”[6]</p>
</li>
<li><p>晚年，随着政治上的失意，精神上的匮乏，梁启超对佛学简直到了如痴如狂的地步。1922年在南京讲学期间，梁定期到支那内学院听欧阳竟无讲佛。他在致儿女的书信中，不仅把佛学誉为“宇宙间唯一真理”，而且要他们也经常念佛。如有闲空，梁还和夫人一起念经求佛，以解脱心中苦闷，寄托无限情思。</p>
</li>
<li><p>关于佛学理论的研究，梁启超用力最深。梁的信佛和一般平民百姓的拜佛不同，他不是用迷信来解脱自己，而是用新的理念来重新塑造自己的人生观，解决心灵深处的疾苦，构筑新的思想理论体系。</p>
</li>
<li><p>。在梁看来，佛教本身就是一种心理修养，它是通过各种人生的哲理并杂以不少玄学，再结合现实生活来规劝世人如何做人，如何解除烦恼，从而达到一种“无我”的生活境界。</p>
</li>
</ul>
<h2 id="妻爱无限_儿女情长">妻爱无限　儿女情长</h2><ul>
<li><p>然而，光绪十五年（1889年）的一次举人考试，不仅改变了她的命运，也再次演绎了一场传统士人梦寐以求的传奇式婚姻，今天我们可以称之灰姑娘式的爱情故事，只不过这位“灰姑娘”是才华横溢的青年梁启超。</p>
</li>
<li><p>养猫是李蕙仙的一大爱好，尤其是从日本归国后，梁家生活日趋稳定，在她的带动下，养猫成了梁家老老少少的一大兴趣。这一嗜好从她身上传给了几乎所有的儿女，又传给了第三代，以至于梁家被誉为“爱猫家庭”。吴</p>
</li>
<li><p>王桂荃是四川广元人，又名来喜，出生于1886年。王桂荃的童年非常悲惨，尚未尽情享受童年的幸福与欢乐，悲剧就不断向她袭来。先是母亲早逝，继母借口她命硬，是父母的克星，经常虐待她，动不动就不给饭吃，还拳打脚踢。四岁时，父亲又不幸抱病而亡，继母进城办丧事，账房先生乘机把家产席卷一空，还将她卖给了人贩子。从此以后，年幼的她先后被转卖了四次，其间所受的折磨与痛苦更是令人难以想象。后来，她被卖到了李蕙仙的娘家，转机发生了。1894年，李夫人回家探亲，见她年龄虽小，但聪明伶俐又勤快，便把她带到梁家作贴身丫鬟，并取名桂荃。从此年幼的王桂荃走进了梁家，这一进，就是七十年，直至1968年病逝于北京。</p>
</li>
<li><p>梁家虽不乏近代民主意识，但传统伦理观念仍很深厚，尤其是李夫人，而梁启超又十分尊重与挚爱这位和梁家生死与共的结发夫人，对于李夫人能否接受王桂荃和自己结婚这一事实，他感到忐忑不安，曾一度将王桂荃接至上海，并在那里生下了一个男孩，取名梁思永。然而后来事实却出乎梁启超意料，当李夫人知道这一切时，尽管很震惊，但很快就平静地接受了这一现实。其一，作为结发妻子，她是深爱并了解梁启超的，知道他需要有人悉心照顾，而自己要管家教子，实在是力不从心；其二，梁家将振兴家族的希望完全寄予梁启超，在梁启超已成家立业，并名扬海内外之际，对于一位男性继承人的迫切需要也日益表现出来，而自己在1893年生下长女思顺后，</p>
</li>
<li><p>王夫人出身贫苦，没有机会读书识字，但她聪明、好学，接受新事物很快，尤其是和梁启超一起流亡日本后，接触到了日本现代文明，大大开拓了眼界，成为全家第一个学会日常日语的人，能讲一口流利的东京话。她不识字，后来干脆等孩子长大一些时，和他们一起学习认字，很快，“她学会了读书，并且读得富有表情。她不仅精通看护和家务管理，而且学会了游泳、滑旱冰、滚铁环、编织、勾花边，会打桥牌、麻将，还学会了针灸”。[9]</p>
</li>
<li><p>当年二舅思成学建筑，三舅思永学考古，四舅思忠学军事。她曾经非常风趣又得意地对别人说：‘我这几个儿子真有趣，思成盖房子，思忠炸房子，房子垮了埋在地里，思永又去挖房子。’”[</p>
</li>
<li><p>1966年文化大革命爆发，梁家子女无一例外受到冲击，已经八十高龄的王桂荃也未能逃脱此劫。她被戴上了“保皇党梁启超的老婆”这顶“高帽”，家产被抄，房屋被占，被赶到一间阴冷潮湿的小屋居住，每天还要作为劳改队队员上街劳动，而此时，她已患晚期肠癌。在战火连天、朝不保夕的战争年代都挺过来的王夫人，这回却垮了，精神和肉体的折磨使她再也无法站起来。此时此刻，她是多么想念自己的儿孙们。然而，命运已使他们再也无法相见。怀着对亲人无限的爱，怀着对昔日家庭幸福生活的无限眷恋，也怀着莫名的困惑与痛苦，王夫人于1968年离开了人世。社会造成了她悲惨的童年，又导致了凄凉的晚年。她又是幸福的，因为，作为一个平凡的女性，她的大半生都是在爱与被爱之中度过的。</p>
</li>
<li><p>数日后，梁将照片赐何蕙珍，何亦回赠亲手织绣的两把精美的小扇。梁启超不仅感慨：“见其事、闻其言，觉得心中时时刻刻有此人，不知何故也。”[13]</p>
</li>
<li><p>宴毕，何蕙珍又深情地表示：“先生他日维新成功后，莫忘我，但有创办女学堂之事，以一电召我，我必来。我之心惟有先生！”两人握手珍重而别。回公寓后，梁启超激动的心情再也无法平静下来，自称“归寓后，愈益思念蕙珍，由敬重之心，生出爱恋之念来，几乎不能自持。明知待大家闺秀，不应起如是念头，然不能制也。酒阑人散，终夕不能成寐，心头小鹿，忽上忽落，自顾生平二十八年，未有如此可笑之事者。”[14]</p>
</li>
<li><p>上学之余，“双涛园”后面的山林就成了孩子们真正的“天堂”。樱花烂漫的时节，大人们登山赏花，孩子们则前后奔跑，嬉戏不息，都要玩“疯”了。有时，孩子们会跟着父母，带着小炉子和酱油等佐料，在松林中，踩着厚厚的松叶，四处寻找鲜鲜的松蘑，边烤边吃。风景之美，松蘑之香，气氛之和谐，以至于已长大成人的“双涛园群童”每每想起，都会留恋不已。</p>
</li>
<li><p>今吾朝受命而夕饮冰”。</p>
</li>
<li><p>梁思忠问道：“为什么一个爱国的政治家要在一个通商口岸的外国租界里安家并造起书房和图书室？”显然，梁启超没有想到儿子会提出这么敏感的问题。但梁很认真地对待儿子的疑问，他非常平和地对儿子解释道：“别把私人的事情同国际事务搅在一起。除了我的家庭以外。我眼前主要关心的是我的图书室。我需要我的书，我必须使它们保持能用的状态。比起放在可能被某些愤怒的学生不明智地放火烧掉的易燃的宫殿来，放在附近港口城市的外国租界里可能更安全些。而要使用这些书，我必须时时住在它们的旁边。”[</p>
</li>
<li><p>在梁启超看来，爱国绝不是单纯的空喊口号和自我标榜，爱国的行动可以而且应该包括各个方面，既有政治救国，军事救国，也应该有科学救国，学术救国等。不能为了标榜爱国而与一切“洋”事物都划清界限；也不能为了表明爱国，都去当兵或参与政治斗争，毕竟尺有所短，寸有所长。基于此种认识，尽管在政治上屡遭挫折，但梁启超的爱国热情始终未减，而且愈发坚定了自己学术救国的信念，直至生命最后一息。</p>
</li>
<li><p>1925年4月17日，他在写给远在加拿大的女儿们的信中将自己的情绪毫无遮拦地表达出来，他这样写道：“宝贝思顺、小宝贝庄庄：你们走后，我很寂寞。当晚带着忠忠听一次歌剧，第二日整整睡了十三个钟头起来，还是无聊无赖……庄庄这几个月来天天挨着我，一旦远行，我心里着实有点难过。但为你成就学业起见，不能不忍耐这几年。庄庄跟着你姊姊，我是十二分放心了；但我十五日早晨吩咐你那几段话，你要常常记在心里，等到再见我时，把实行这话的成绩交还我，我便欢喜无量了。”[</p>
</li>
</ul>
<h2 id="“思成梁启超”">“思成梁启超”</h2><ul>
<li><p>当年，同在美国留学的梁思永为此专门做对联一副：“林小姐千妆万扮始出来；梁公子一等再等终成配。”</p>
</li>
<li><p>我主张你们在坎京（旧译，指加拿大）行礼，你们意思如何？我想没有比这样再好的了。你们在美国两个小孩子自己实张罗不来，且总觉太草率，有姊姊代你们请些客，还在中国官署内行谒祖礼（婚礼还是在教堂内好），才庄严像个体统。 婚礼只要庄严不要侈靡，衣服首饰之类，只要相当过得去便够，一切都等回家再行补办，宁可从中节省点钱作旅行费。”[28]</p>
</li>
<li><p>在大学生时代，他们性格上的差异就在工作作风上表现出来。满脑子创造性的徽因常常先画出一张草图或建筑图样。随着工作的进展，就会提出并采纳各种修正或改进的建议，它们自己又由于更好的意见的提出而被丢弃。当交图的最后限期快到的时候，就是在画图板前加班加点拼命赶工，也交不上所要求的齐齐整整的设计图定稿了。这时候思成就参加进来，以他那准确和漂亮的绘图功夫，把那乱七八糟的草图变成一张清楚整齐能够交卷的作品。他们的这种合作，每个人都向建筑事业贡献出他的（或她的）特殊天赋，在他们今后共同的专业生涯中一直坚持着。”[</p>
</li>
<li><p>面对重病中的妻子，梁思成心如刀绞，但环境的恶劣、条件的简陋，丝毫没有令他畏缩，这位从未接受过医学训练的建筑学家开始学习打针，为了自己的妻子，天大的困难也要克服。爱能战胜一切，梁思成不仅学会了肌肉注射，而且学会了难度很大的静脉注射，成为林徽因最好的“护士”。</p>
</li>
</ul>
<h2 id="思成，有成；也难成">思成，有成；也难成</h2><ul>
<li>很遗憾，他们的建议并未引起中央领导的足够重视。新中国的领导们对北京实现工业化的决心是不容许丝毫动摇的。彭真的一句话最具代表性，他告诉梁思成：毛主席希望北京成为一个现代化的大城市，并希望从天安门上望去，下面是一片烟囱。</li>
</ul>
<h2 id="群星闪烁——渐为平常百姓家">群星闪烁——渐为平常百姓家</h2><ul>
<li><p>在子女教育的问题上，一个重要的误区就是父母企图按照自己的人生理念和价值判断去改造子女，像制造产品一样重新创造自我，也就是说，是改造子女而不是引导子女。</p>
</li>
<li><p>他在家信中说：“我以为一个人什么病都可以医，唯有‘悲观病’最不可医，悲观是腐蚀人心的最大毒菌。”[4]而悲观的产生往往是源于过高估计自己，目标过高而达不到，于是常常悲观失望。</p>
</li>
<li><p>我生平最服膺曾文正两句话：‘莫问收获，但问耕耘。’将来成就如何，现在想他则甚？着急他则甚？一面不可骄盈自慢，一面又不可怯懦自馁，尽自己能力做去，做到那里是那里，如此则可以无入而不自得，</p>
</li>
</ul>
]]></content:encoded>
      <comments>http://fancymax.github.io/2017/05/10/liang/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[写给大家看的设计书]]></title>
      <link>http://fancymax.github.io/2017/04/06/book201704/</link>
      <guid>http://fancymax.github.io/2017/04/06/book201704/</guid>
      <pubDate>Thu, 06 Apr 2017 14:27:48 GMT</pubDate>
      <description>
      <![CDATA[<ul>
<li><p>一旦能够说出什么东西的名字，就会很容易注意到它。你就会掌握它，拥有它，使它在你的控制中。</p>
<ul>
<li>所以凡事要先掌握概念</li>
</ul>
</li>
<li><p>尽管我在后面将逐个讨论各个原则，不过要记住，它们实际上是相互关联的。只]]>
      </description>
      <content:encoded><![CDATA[<ul>
<li><p>一旦能够说出什么东西的名字，就会很容易注意到它。你就会掌握它，拥有它，使它在你的控制中。</p>
<ul>
<li>所以凡事要先掌握概念</li>
</ul>
</li>
<li><p>尽管我在后面将逐个讨论各个原则，不过要记住，它们实际上是相互关联的。只应用某一个原则的情况很少。</p>
<ul>
<li>4大设计原则是互相关联的</li>
</ul>
</li>
<li><p>亲密性的思想并不是说所有一切都要更靠近，其真正的含义是：如果某些元素在理解上存在关联，或者相互之间存在某种关系，那么这些元素在视觉上也应当有关联。除此以外，其他孤立的元素或元素组则不应存在亲密性。</p>
</li>
<li><p>要有意识地注意你是怎样阅读的，你的视线怎样移动：从哪里开始；沿着怎样的路径；到哪里结束；读完之后，接下来看哪里？整个过程应当是一个合理的过程，有确定的开始，而且要有确定的结束。</p>
</li>
<li><p>亲密性的根本目的是实现组织性。</p>
</li>
<li><p>利用亲密性原则，还可以使空白（这是设计者们最喜欢的）更美观（也更有条理）。</p>
</li>
<li><p>微微眯起眼睛，统计你的眼睛停顿的次数来数一数页面上有多少个元素。如果页面上的项超过3～5个（当然，这取决于具体情况），就要看看哪些孤立的元素可以归在一组建立更近的亲密性，使之成为一个视觉单元。</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>可以把重复认为是“一致性”。</p>
</li>
<li><p>不过重复还不只是自然的一致，这是一种统一设计各个部分的有意识的行为。</p>
</li>
<li><p>如果一个出版物有非常好的一致性，则可以放入一些与众不同的元素，使读者真正注意到你希望他们关注的内容。</p>
</li>
<li><p>不要把重复用得太滥，而应当尽量“采用多样性实现统一”。也就是说，如果一个重复元素很明确（如一个圆），那么可以采用多种不同方式重复这个圆，而不是简单地重复同一个圆。</p>
<ul>
<li>感觉重复性原则很难应用好</li>
</ul>
</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>希望你已经看出，对比对于设计作品来说是何等重要，另外也应该能看出增加对比实际上是何等容易。</p>
</li>
<li><p>对比的根本目的有两方面，这两个方面相辅相成，无法分开。一个目的是增强页面的效果，如果一个页面看起来很有意思，往往更有可读性。另一个目的是有助于信息的组织。读者应当能立即了解信息以何种方式组织，以及从一项到另一项的逻辑流程。对比元素不能让读者混淆，也不能错误地强调重点（即本不该是重点的元素）。</p>
</li>
<li><p>重要的是：对比一定要强烈。</p>
</li>
</ul>
]]></content:encoded>
      <comments>http://fancymax.github.io/2017/04/06/book201704/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[订票助手12306ForMacV1.6更新]]></title>
      <link>http://fancymax.github.io/2017/03/17/12306ForMacV16/</link>
      <guid>http://fancymax.github.io/2017/03/17/12306ForMacV16/</guid>
      <pubDate>Fri, 17 Mar 2017 14:39:55 GMT</pubDate>
      <description>
      <![CDATA[<h2 id="支持多日期串行查询">支持多日期串行查询</h2><p><img src="https://ooo.0o0.ooo/2017/03/17/58cbec3120077.png" alt="test"></p>
<h2 id="查到票后Reminder提醒">查到票后]]>
      </description>
      <content:encoded><![CDATA[<h2 id="支持多日期串行查询">支持多日期串行查询</h2><p><img src="https://ooo.0o0.ooo/2017/03/17/58cbec3120077.png" alt="test"></p>
<h2 id="查到票后Reminder提醒">查到票后Reminder提醒</h2><p>若查到票时不在电脑旁边，则会通过iPhone提醒</p>
<p><img src="https://ooo.0o0.ooo/2017/03/17/58cbf3af6f4c4.png" alt="test"></p>
<p>或者Apple Watch提醒</p>
<p><img src="https://ooo.0o0.ooo/2017/03/17/58cbedd88996b.png" alt="test"></p>
<h2 id="改进车次筛选功能">改进车次筛选功能</h2><p><img src="https://ooo.0o0.ooo/2017/03/17/58cbef7d1c82f.png" alt="test"></p>
<h2 id="日历显示更多节假日">日历显示更多节假日</h2><p><img src="https://ooo.0o0.ooo/2017/03/17/58cbf00a000c7.png" alt="test"></p>
<h2 id="登录界面快捷键">登录界面快捷键</h2><p>右手用鼠标或触控板选择验证码，左手按Space键确定，非常方便<br><img src="https://ooo.0o0.ooo/2017/03/17/58cbf42df0f20.png" alt="test"></p>
<h2 id="节假日预售期提醒">节假日预售期提醒</h2><p>可以一键生成 之后一年的节假日 预售期的日历提醒，提前做好买票计划。<br><img src="https://ooo.0o0.ooo/2017/03/17/58cbf165b3d06.png" alt="test"></p>
]]></content:encoded>
      <comments>http://fancymax.github.io/2017/03/17/12306ForMacV16/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[2016到2017]]></title>
      <link>http://fancymax.github.io/2017/01/04/2016end/</link>
      <guid>http://fancymax.github.io/2017/01/04/2016end/</guid>
      <pubDate>Wed, 04 Jan 2017 13:38:04 GMT</pubDate>
      <description>
      <![CDATA[<p>2016最后一天: 早起爬梧桐山，又从梧桐山门口骑车回家。<br>]]>
      
      </description>
      <content:encoded><![CDATA[<p>2016最后一天: 早起爬梧桐山，又从梧桐山门口骑车回家。<br><a id="more"></a><br>在家旁边发现一个箱子里有只被遗弃的小奶猫，在可怜的喵叫~<br><img src="http://7xqmjb.com1.z0.glb.clouddn.com/2017010474719cat1.jpeg" alt="2017010474719cat1.jpeg"></p>
<p>和婷妹子商量纠结了很久，最后决定挽救这个可怜又可爱的小生命。<br>马上抱去楼下的宠物医院做检查和护理。小奶猫估约五天大，眼睛还没睁开。宠物医生说没有猫妈的小猫很难养活。不过我和婷妹子都没有被吓倒，不可能再把小猫放回路边，于是我们当起了临时猫妈，买了猫奶嘴，针管，猫奶粉。开始了每两个小时给猫喂奶，把屎把尿的艰难过程。一开始两人都搞不过来。一弄一个小时就过去。半夜弄完之后就没睡着。</p>
<p>2017第一天婷妹子加班去了，我居然能独自一人喂了小猫一天。局外人绝对无法理解我见证小猫能拉出屎时有多高兴，感觉这样它又增加了几分存活的概率。<br><img src="http://7xqmjb.com1.z0.glb.clouddn.com/2017010450559cat2.jpeg" alt="2017010450559cat2.jpeg"></p>
<p>傍晚时分婷妹子说找到了爱猫人士愿意收留小猫。虽然自己挺不舍得，但也没有条件继续24小时照顾小猫。于是最后一次给小猫喂完奶做好保暖措施，然后送走了，愿这只可爱的小奶猫可以健康的活下来。<br><img src="http://7xqmjb.com1.z0.glb.clouddn.com/2017010477858cat3.jpeg" alt="2017010477858cat3.jpeg"><br><img src="http://7xqmjb.com1.z0.glb.clouddn.com/201701045192cat4.jpeg" alt="201701045192cat4.jpeg"></p>
]]></content:encoded>
      <comments>http://fancymax.github.io/2017/01/04/2016end/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[老友记]]></title>
      <link>http://fancymax.github.io/2016/03/24/friends/</link>
      <guid>http://fancymax.github.io/2016/03/24/friends/</guid>
      <pubDate>Thu, 24 Mar 2016 12:05:10 GMT</pubDate>
      <description>
      <![CDATA[<p>老友记 又称 六人行，英译称“friend”是一部由David Crane与Marta Kauffman共同创作的美国情景喜剧。该剧于1994年9月22日在NBC播出，于2004年5月6日结束，共播出十季，剧情主要围绕着一众好友在纽约曼哈顿的生活展开。]]>
      
      </description>
      <content:encoded><![CDATA[<p>老友记 又称 六人行，英译称“friend”是一部由David Crane与Marta Kauffman共同创作的美国情景喜剧。该剧于1994年9月22日在NBC播出，于2004年5月6日结束，共播出十季，剧情主要围绕着一众好友在纽约曼哈顿的生活展开。<a id="more"></a></p>
<blockquote>
<p>此剧由Bright/Kauffman/Crane Productions联合华纳兄弟电视公司制作。《老友记》最初的三位执行制片人分别为Kevin S. Bright、Marta Kauffman以及David Crane，而在之后的季度中亦陆续加入多位执行制片人。该剧在超过一百个国家播放，而且后续的重播依然有着良好的收视。据估计在美国有5110万人观看了该剧的大结局。</p>
</blockquote>
<p>最早知道老友记应该是在高中的时候吧，那时候流行看剧学英语，所以知道了这部剧之后就特意买光盘回来看，抱着这样功利的态度努力的看，可惜直到高中毕业都没看完，也没领会到剧中的搞笑桥段。</p>
<p>之后上大学的时候，某次机缘之下下载到了老友记的全部资源，然后又想起我看剧学英语的未竟事宜，不禁还想尝试一下，就偶尔翻出来看看，发现这剧还真是有点意思。于是每当我烦闷的时候，都会拿几集老友记来看看逗自己开心一下，发现还真是超级有效果。大学四年转瞬而过，而我还真的把十季的老友记完整的看下来一遍。里面好多搞笑的桥段、令人感动的情节方佛给我的大学时光抹上一层色彩，让人回忆起来更有味道。</p>
<p>工作后在上午下班 到 下午上班之间的这段时间比较无聊，于是我又捡起老友记来看，老友记一集不到半小时，正好从12:10看到12:40，然后睡半小时午觉。虽然是重看老友记，但是我一点也不觉得无聊，搞笑的桥段还是会让我开怀大笑，好多次都差点吵到旁边的同事。几年下来，我居然又把老友记完整的看了两次，连我自己到惊到了，😄</p>
<p>说了这么多，一方面是缅怀一下自己的看剧时光，另一方面是想把它推荐给大家。😜，如果你既想排解一下烦闷的心情，又想学学英语，这部经典的老友记不妨一看。</p>
]]></content:encoded>
      <comments>http://fancymax.github.io/2016/03/24/friends/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[又是一年深圳百公里]]></title>
      <link>http://fancymax.github.io/2016/03/03/100miles/</link>
      <guid>http://fancymax.github.io/2016/03/03/100miles/</guid>
      <pubDate>Thu, 03 Mar 2016 11:12:11 GMT</pubDate>
      <description>
      <![CDATA[<p>深圳每年这个时候都会举办百公里徒步]]>
      
      </description>
      <content:encoded><![CDATA[<p>深圳每年这个时候都会举办百公里徒步<a id="more"></a>，这种类型的徒步比马拉松难度小很多，但也颇具挑战性。你想象一下从第一天晚上六点开始走一直走到第二天晚上六点，全程24小时只能进行短暂的休息，没有一定的毅力和体力是走不完的。</p>
<p>正是由于参加的门槛低，但是完成全程又极具有挑战性，所以每年百公里都吸引的大量的“徒步爱好者”参加，甚至听说有人专程从厦门赶来参加，足以想见它的火爆程度。俗语有云“没有逛过东门街，就相当于没有来过深圳”，我再加一句“没有走过百公里，就相当于没有深入了解过深圳”。</p>
<p>都说深圳是个年轻的城市，由百公里徒步可见一斑，我见到过年轻夫妇推着婴儿车来参加的，也见到过50多岁的大叔脚绑沙袋负重参加徒步的。当然大部分人都是来凑凑热闹的，只有很少的一部分人能走完全程。毕竟连续走24小时也不是闹着玩，如果不是追求自虐或者有什么特殊的信念很难坚持下来。所以走完全程然后表白 或者 求婚算是一个good idea，单身的同志不妨一试。</p>
<p>我也参加了好几次百公里活动了，在2014年的那次有幸走完了全程。然后一开始我是很酱油的，想的就是走完10公里回去睡觉，所以别人都是背包＋装备，而我两手空空。<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/bgl1.jpg" alt="demo"></p>
<p>当我们到第一个十公里的时候，一个同行的小伙伴就要撤了回家睡觉，而我却突然想继续走下去，想任性一把，－－！本来第二天要加班的。于是我继承了她的全部装备(包括充电宝和和各种吃的)。</p>
<p>走到第二个签到点还发现有人求婚。<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/bgl2.jpg" alt="demo"><br><img src="http://7xpbra.com1.z0.glb.clouddn.com/bgl5.jpg" alt="demo"></p>
<p>第二个签到点后已经凌晨1点了，后面基本上都是一个人在走，听着ipod，漫漫长夜也别有一番趣味。<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/bgl4.jpg" alt="demo"><br><img src="http://7xpbra.com1.z0.glb.clouddn.com/bgl3.jpg" alt="demo"></p>
<p>等到了第三个签到点，基本上天快亮了，到了梧桐山门口，这时候其实状态还是不错的。<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/bgl6.jpg" alt="demo"></p>
<p>之后就是非常艰难的上山之旅，和我一起来的小伙伴已经全部溃败，只剩下我一个人，十分艰难的走到了大梅沙。<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/bgl7.jpg" alt=""></p>
<p>之后的路程基本上就是在自虐了，而我只能尽量减少休息的时间，基本就一直在走，累了就喝红牛，因为脚已经非常疼了，走的特别慢，一路上老是被别人超过，那些走的特别轻快的基本上都是从大梅沙这个半程开始走的。另外这时候太阳是非常晒的，如果没有做好防护措施的话，第二天全部要晒脱皮，所以我半路买了头巾和帽子把自己包的紧紧的。<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/bgl9.jpg" alt=""><br><img src="http://7xpbra.com1.z0.glb.clouddn.com/bgl10.jpg" alt=""></p>
<p>也不知道哪来的毅力，我竟真的走完了全程。老实说今年我是没有信心可以走完的。<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/bgl12.jpg" alt=""><br><img src="http://7xpbra.com1.z0.glb.clouddn.com/bgl11.jpg" alt=""></p>
<p>Boss在朋友圈看到我走完全程的照片还问要不要给我放三天假。当然我第二天还是照常去上班了，只是走路有点瘸，然后惊喜的发现当天的报纸头条是百公里。<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/bgl13.jpg" alt=""></p>
]]></content:encoded>
      <comments>http://fancymax.github.io/2016/03/03/100miles/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[卖书]]></title>
      <link>http://fancymax.github.io/2016/02/18/sellbook/</link>
      <guid>http://fancymax.github.io/2016/02/18/sellbook/</guid>
      <pubDate>Thu, 18 Feb 2016 06:20:53 GMT</pubDate>
      <description>
      <![CDATA[<p>其实很早就有卖书的想法，工作这两年买的纸质书已经已经堆满了我的书架，有些甚至都堆放到了同事的书架上。]]>
      
      </description>
      <content:encoded><![CDATA[<p>其实很早就有卖书的想法，工作这两年买的纸质书已经已经堆满了我的书架，有些甚至都堆放到了同事的书架上。<a id="more"></a>再加上我这人有点强迫症，那些不再使用的东西，我都不希望它出现在我的眼前，否则会浑身难受。</p>
<p>有些书我已经完整的看过一遍，感觉未来不可能再看第二遍了。还有一些书我买回来看了一眼就没再看过第二眼，我感觉未来也不可能再去拾起来看。这些书我都想统统清理掉，直接扔掉肯定是不舍得的，直接送给朋友吧他们可能未必需要，反而弄得自己费力不讨好。</p>
<p>之前看到有些技术大牛在自己的博客上卖书，感觉是一个不错的方式，一方面有人愿意花钱买的书证明了你的书对他是有价值的，另一方面可以满足我的强迫症－－! 把这些看完的书消灭掉；当然啦，还可以赚一点零花钱。</p>
<p>想法和方法都有了，但真正实施起来发现相当困难；自己的博客基本没啥访问量，要想靠它卖出去不知道要等到猴年马月了；另外尝试了一些地方也都以失败告终，比如豆瓣二手书市场、淘宝的闲鱼、孔夫子二手书，究其原因我不就是卖几本书吗？还要折腾淘宝的卖家账号太费劲了，另外我也没兴趣去做包装，快递之类的事情，我的理想就是同城现场交易，大家一手交钱，一手交书，还能坐下来聊聊天；还有就是这些服务定位的是更为广义的二手市场，而我手上就是一些IT和人文之类的书，很难快速匹配到需要它的人。</p>
<p>转折点在最近我喜欢上的一个叫V2ex的创意社区网站，上面聚集了很多程序员、设计师、创意者。</p>
<p>V2ex上有一个二手交易的节点，同时上面又聚集了大量的程序员，所以我发了一个卖书的帖子没多久，就吸引到了目标读者。不到一天就敲定了第一笔交易（就在发帖的第二天，也就是今晚）：木心的两册《文学回忆录》 ＋ 《程序员的数学》，我们还约定随便吃个饭，聊聊天，这效果真是好的不要不要的。</p>
<p>马上就要成功把书卖掉了，但是心里却有点的不舍。尤其这两册《文学回忆录》，它陪我度过了一些美好的时光，时间拉回到2013年的时候，我经常下班的时候拿着这两本书找个安静的角落细细咀嚼，虽然我的文学素养不太好，但还是看的很有意思，摘录一段：</p>
<blockquote>
<p>   乔治.戈登.拜伦(George Gordon Byron, 1788-1824)。得年三十六岁，标准天才型人物的死亡。生于伦敦。家父是家事不管的花花公子。母亲脾气坏。拜伦从小不相让，吵。拜伦是个穷的贵族。妈妈骂他是“拐子”，他说：说这话的还是个人吗？</p>
<p>   不幸的童年，使人性格尖锐。</p>
<p>   上大学后是个捣蛋鬼，受罚。向往异国情调—这是十九世纪的特征。二十一岁游西班牙、希腊、土耳其，边游边写诗，就是《哈罗德游记》，哈罗德是诗中的主角名字。诗寄回英国，头两卷发表后，拜伦说：“我一早醒来，一夜成名，成为诗台上的拿破仑。”</p>
<p>   这就是拜伦：说得出，做得到；做得到，说得出。</p>
<p>   1815年结婚，1816年离婚。就是这样。这种婚姻，就是拜伦风格。当时英国多么保守，舆论大哗。他一怒之下，离国出走，说永远不回来了。</p>
<p>   拜伦的脾气。</p>
<p>   他是贵族、诗人、美男子、英雄，是多重性质的象征。我小时候一看这名字，还没读作品，就受不了了。再看画像，更崇拜。宝玉见黛玉，说这位妹妹好像哪儿见过。我见拜伦，这位哥哥好像哪儿见过。精神血统就是这样。席勒，我总隔一层；雪莱，我视为邻家男孩；拜伦，我称为兄弟。</p>
<p>   人类文化至今，最强音是拜伦：反对权威，崇尚自由，绝对个人自由。</p>
<p>   真挚磅礴的热情，独立不羁的精神，是我对拜伦最心仪的。自古以来，每个时代都以这样的性格最为可贵。<br>   英国文学，莎士比亚之后，公推拜伦。</p>
</blockquote>
<p>当时看了对拜伦的评价之后也是很激动，还特意去图书馆借了拜伦的书来看。转眼两年过去了，当初立下目标要把文学回忆录上列出的每本经典书籍都看一遍终究是没能做到，不过《文学回忆录》我是完整的看了一遍下来的，也算一段珍贵的回忆吧。现在已经答应别人要卖了，也不好反悔，假如以后我还想看就去买一份Kindle版的做珍藏好了。</p>
<p>书目如下：</p>
<ul>
<li>人文类<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/book5.pic.jpg" alt="book"></li>
</ul>
<ul>
<li><p>C&amp;C++<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/book4.pic.jpg" alt="book"></p>
</li>
<li><p>iOS相关<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/book1.pic.jpg" alt="book"></p>
</li>
</ul>
<ul>
<li><p>C#相关<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/book3.pic.jpg" alt="book"></p>
</li>
<li><p>其他<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/book2.pic.jpg" alt="book"><br><img src="http://7xpbra.com1.z0.glb.clouddn.com/book6.pic.jpg" alt="book">  </p>
</li>
</ul>
]]></content:encoded>
      <comments>http://fancymax.github.io/2016/02/18/sellbook/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[智能车]]></title>
      <link>http://fancymax.github.io/2016/02/11/LittleCar/</link>
      <guid>http://fancymax.github.io/2016/02/11/LittleCar/</guid>
      <pubDate>Thu, 11 Feb 2016 05:48:40 GMT</pubDate>
      <description>
      <![CDATA[<div class="video-container"><iframe src="//player.vimeo.com/video/154949001" frameborder="0" allowfullscreen></iframe></div>]]>
      
      </description>
      <content:encoded><![CDATA[<div class="video-container"><iframe src="//player.vimeo.com/video/154949001" frameborder="0" allowfullscreen></iframe></div>
<a id="more"></a>
<p>今天偶然翻出大学时做智能车的一个视频，这是当时校内比赛现场，我负责车子的软件部分。</p>
<p>车子没拿到什么名次，但我们也付出了巨大努力。我的自学能力大概就是在那个时候锻炼的。也是那个时候开始喜欢上做产品（软件）的那种感觉。正因为此我是系里仅有的几个没有选择进到体制内而是进软件公司的人。现在工作五年了，虽然当初的理想还没有实现，但至少毕业的选择让我保有这个可能性。</p>
<p>所以我打心底里很感谢这个智能车项目，它让我体验到从无到有做出一个产品的乐趣，它改变了我的职业生涯和人生轨迹。</p>
]]></content:encoded>
      <comments>http://fancymax.github.io/2016/02/11/LittleCar/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[iPhone维修记]]></title>
      <link>http://fancymax.github.io/2016/01/24/iPhone/</link>
      <guid>http://fancymax.github.io/2016/01/24/iPhone/</guid>
      <pubDate>Sun, 24 Jan 2016 02:20:05 GMT</pubDate>
      <description>
      <![CDATA[<p>这几周耗费大量时间，精力和金钱的修手机心酸史，有必要总结一下(￣▽￣)<br>]]>
      
      </description>
      <content:encoded><![CDATA[<p>这几周耗费大量时间，精力和金钱的修手机心酸史，有必要总结一下(￣▽￣)<br><a id="more"></a></p>
<h1 id="维修地点">维修地点</h1><p>如果是在HK苹果直营店买的听说可以在内地Apple售后维修(^没有实践过^)。<br>其次可以在HK的几大直营店维修。<br>如果是在其他地方购买，不能在Genius Bar维修。需要看当时购买的单据上描述的维修地址。<br>丰泽的最近维修地址是九龙旺角西洋菜南街2A至2H号银城广场12楼01-03室。地方蛮好找的，就是地铁到旺角东站，走过波鞋街和女人街，在旺角地铁站出口，电器一条街的尽头，百脑汇和SaSa的楼上，如下图(今天在没网没地图听不懂白话讲不出英语的前提下找到这个地方实属不易)。这些地方只有周一到周六上班，上周日去了，白跑一趟-_-#<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/hk_1.jpg" alt="2"></p>
<h1 id="预约方式">预约方式</h1><p>如果在直营店购买的手机可以预约香港Apple官网的Genius Bar，预约需要香港手机，一个手机号一周只能收到三次验证码，需要先输入验证码才能看到还能不能预约。<br>听闻早上8点半放号，害得我借了手机卡，早上起来蹲点，比自己抢火车票还积极，然并卵，这个号比火车票还难抢，最好花80大洋，去淘宝买了个号，感谢万能的淘宝。<br>听闻可以直接去直营店排队维修，实际貌似是能帮你检查，如果真的要维修，还的预约。<br>非直营店手机貌似也可以网上预约，或者去Genius Bar让他们给你开证明直接去维修。<br><img src="http://7xpbra.com1.z0.glb.clouddn.com/hk_2.jpg" alt="1"></p>
<h1 id="维修结果">维修结果</h1><p>个人感觉苹果维修很容易换机，这次维修手机已经交给她们，得等12个工作日出结果。</p>
<h1 id="维修准备">维修准备</h1><p>直营购买香港维修需要手机，不需要包装和配件，就算是换机也只是机子。<br>非直营购买手机需要购买时的收据。</p>
<h1 id="温馨提示">温馨提示</h1><p>过年过关人太多，大叔大妈行李箱太挤，小心踩踏事件。<br>「最最重要，珍爱生命，远离Apple」。</p>
]]></content:encoded>
      <comments>http://fancymax.github.io/2016/01/24/iPhone/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[OS X编程资料总结]]></title>
      <link>http://fancymax.github.io/2015/12/31/OS-X-learning/</link>
      <guid>http://fancymax.github.io/2015/12/31/OS-X-learning/</guid>
      <pubDate>Thu, 31 Dec 2015 13:10:45 GMT</pubDate>
      <description>
      <![CDATA[<p>iOS的开发特别火爆，OS X的却很冷淡。新手常常觉得OS X的开发难以入门，原因是资料特别少，包括当初的我在内。所以我整理了一些入门必备的工具 或 资源，希望对大家有所帮助。<br>]]>
      
      </description>
      <content:encoded><![CDATA[<p>iOS的开发特别火爆，OS X的却很冷淡。新手常常觉得OS X的开发难以入门，原因是资料特别少，包括当初的我在内。所以我整理了一些入门必备的工具 或 资源，希望对大家有所帮助。<br><a id="more"></a></p>
<ul>
<li><p><a href="https://kapeli.com/dash" target="_blank" rel="external">Dash for OS X</a></p>
</li>
<li><p>科学上网:<a href="https://itxs.li" target="_blank" rel="external">土行孙</a>或者<a href="https://getlantern.org" target="_blank" rel="external">lantern</a></p>
</li>
<li><p>OS X Human Interface Guidelines</p>
</li>
<li><p>The Swift Programming</p>
</li>
<li><p><a href="http://www.amazon.cn/Swifter-100个Swift-2-开发必备Tip-王巍/dp/B019CRN7TW/ref=sr_1_2?ie=UTF8&amp;qid=1451567750&amp;sr=8-2&amp;keywords=Swifter" target="_blank" rel="external">Swifter 100 tips</a></p>
</li>
<li><p><a href="http://www.amazon.cn/Cocoa-Programming-for-OS-X-The-Big-Nerd-Ranch-Guide-Hillegass-Aaron/dp/0134076958/ref=sr_1_4?ie=UTF8&amp;qid=1451567670&amp;sr=8-4&amp;keywords=Cocoa" target="_blank" rel="external">Cocoa Programming for Mac OS X(5th)</a>里面用的是Swift讲解，介绍的例子通俗易懂，全书的例子做一遍下来，开发简单的OS X程序不成问题。</p>
</li>
<li><p>Advanced Mac OS X Programming(2011)</p>
</li>
<li><p>Cocoa® Programming Developer’s Handbook(2010)</p>
</li>
<li><p>Cocoa Design Patterns</p>
</li>
<li><p>AppleScript For Absolute Starters</p>
</li>
<li><p>Effective Objective-C 2.0_ 52 S - Galloway, Matt</p>
</li>
<li><p>Pro Multithreading and Memory Management for iOS and OS X with ARC, Grand Central Dispatch, and Blocks</p>
</li>
<li><p><a href="https://www.cocoacontrols.com" target="_blank" rel="external">Cocoacontrols</a>，网站收集了各种iOS和OS X的自定义控件，很有参考意义。</p>
</li>
</ul>
<p>最后当然还可以查各种Apple的文档。</p>
]]></content:encoded>
      <comments>http://fancymax.github.io/2015/12/31/OS-X-learning/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[我的2015]]></title>
      <link>http://fancymax.github.io/2015/12/29/my2015/</link>
      <guid>http://fancymax.github.io/2015/12/29/my2015/</guid>
      <pubDate>Tue, 29 Dec 2015 12:30:05 GMT</pubDate>
      <description>
      <![CDATA[<p>年末最适合像这样，拿杯最喜欢的饮料，调暗灯光，舒舒服服的靠在沙发上，想一想这过去的十二月你都做了什么。<br>]]>
      
      </description>
      <content:encoded><![CDATA[<p>年末最适合像这样，拿杯最喜欢的饮料，调暗灯光，舒舒服服的靠在沙发上，想一想这过去的十二月你都做了什么。<br><a id="more"></a><br>记忆中的关键词有：欢乐谷跨年、陈奕迅演唱会、香港代购、去女朋友家过年、穿越东西冲、去香港的南丫岛、五一长假、买房装修搬家、玩PS4、开发12306ForMac。</p>
<p>总的感觉自己这一年过的还是比较滋润的。<br>尤其是前半年玩的比较多，印象最深的就是五一请了9天假和女友去了一趟成都和重庆玩了九寨沟、峨眉山等地。虽然只有短短的9天，但是感觉好像放了长长的一个寒假一样，特别的放松，同时也加深了女友的感情。</p>
<p>后半年相对就平淡和辛苦一些，主要原因就是折腾着买房了，同时还有一堆装修搬家的事情，越是在这种时候越是能体现出有女朋友的好处了。<br>在我们马上要搬入新家的前一个月，前房东通知我必须要搬走，而且没得商量，不得已我们只能再找个房子住一个月，当时真是别提多闹心了。不过还好有个人可以一起承担，郁闷减半。</p>
<p>经过两个多月的努力新房总算搞定，顺利入住。现在再去想想以前那些租房住的痛苦回忆，恍若隔世一般。</p>
<p>另外，今年开始计划着做一些个人作品，比如12306ForMac，它是一款针对Mac的火车票抢票客户端，目前完成了50%左右。<br>同时为了方便后期的产品的推广和维护，我开通了fancywt.cn这个博客，当然我也会把平常的一些想法和感悟总结在这里。</p>
<p>2015是个充满回忆的一年，期待我的2016更加精彩。</p>
]]></content:encoded>
      <comments>http://fancymax.github.io/2015/12/29/my2015/#disqus_thread</comments>
    </item>
    
    <item>
      <title><![CDATA[敏捷开发培训总结]]></title>
      <link>http://fancymax.github.io/2014/05/22/summaryOnSoftDevelopment/</link>
      <guid>http://fancymax.github.io/2014/05/22/summaryOnSoftDevelopment/</guid>
      <pubDate>Thu, 22 May 2014 06:08:18 GMT</pubDate>
      <description>
      <![CDATA[<p>前段时间参加了两天敏捷开发管理培训，收获挺大，在这里做一下总结。</p>]]>
      
      </description>
      <content:encoded><![CDATA[<p>前段时间参加了两天敏捷开发管理培训，收获挺大，在这里做一下总结。</p>
<a id="more"></a>
<h2 id="理解敏捷">理解敏捷</h2><p>整个培训过程中一直穿插着敏捷软件开发的原则进行讲解，这里摘录给我感触最深的几个：</p>
<ul>
<li>我们最重要的目标，是通过持续不断地及早交付有价值的软件使客户满意，经常地交付可工作的软件，相隔几星期或一两个月，倾向于较短的周期。</li>
<li>业务人员和开发人员必须相互合作，项目中的每一天都不例外。</li>
<li>团队定期反思如何能提高成效，并依此调整自身的举止表现。</li>
<li>激发个体的斗志，以他们为核心搭建项目。提供所需的环境和支援，辅以信任，从而达成目标。</li>
</ul>
<p>敏捷流派主要有两个：Scrum 和 极限编程。Scrum侧重项目协作流程，极限编程侧重提高编程效率的技术实践。两者应该相辅相成。这里着重讲讲Scrum。</p>
<h2 id="团队与角色">团队与角色</h2><p>Scrum中有Product Owner、Team、Scrum Master三类角色。一个好的Scrum团队以下特点：</p>
<ul>
<li>通常5~9人；</li>
<li>跨职能，跨模块人员构成；</li>
<li>成员应全职投入；</li>
<li>团队自组织管理；</li>
<li>迭代内保持团队成员稳定。</li>
</ul>
<p>做好一个Product Owner要点如下：</p>
<ul>
<li>定义产品功能；</li>
<li>定义产品发布的日期和功能；</li>
<li>对产品的投入产出比负责；</li>
<li>根据市场情况对需求排列优先级；</li>
<li>如果需要，在每个迭代合理调整产品特性及其优先级；</li>
<li>介绍或拒绝开发团队的工作成果。</li>
</ul>
<p>Scrum Master要引导团队自己去找答案，而不是做一个发号司令的人，做好一个Scrum Master要点如下：</p>
<ul>
<li>Scrum正常运作的守护者；</li>
<li>激发团队创造力；</li>
<li>改善开发团队的外部环境；</li>
<li>辅导团队提升运作效率；</li>
<li>排除团队遇到的困难；</li>
<li>保持团队紧密合作；</li>
</ul>
<p>Team就是团队中的开发、测试或ui设计人员。</p>
<h2 id="需求管理">需求管理</h2><p>Scrum通过编写用户故事来管理需求，好的用户故事的原则如下：</p>
<ul>
<li>Independent独立的；</li>
<li>Negotiable：可讨论的；</li>
<li>Valuable：对用户或客户有价值的；</li>
<li>Estimatable：可估计的；</li>
<li>Small：小的；</li>
<li>Testable：可测试的。</li>
</ul>
<p>之后要进行工作量估算，Product Owner(业务人员)必须在场梳理需求，每个项目成员针对用户故事的疑问向Product Owner提问，所有人弄清楚需求后开始。</p>
<p>大家先找一个参照需求，确定它的工作量，然后其他的需求就按照这个参照需求来估计，这种相对估计法确保每个人估计出来的工作量是一致的。</p>
<p>使用扑克牌，大家同时给出需求的估计值（而不是轮流进行），估值最高和最低的必须分别给出原因，这样做的好处让大家都独立思考。通过多轮估值让所有人了解需求，并估算出一个较为合理的工作量。</p>
<h2 id="Scrum中的各项活动">Scrum中的各项活动</h2><p>简单来说，划分如下</p>
<p>项目计划|<br>Sprint0|<br>Sprint1|<br>Sprint2|<br>Sprint3|<br>项目总结|</p>
<p>按一个迭代周期来说，主要划分如下：迭代计划和评审一般要占用两个小时，而站立会议一般15分钟。</p>
<p>迭代计划1|<br>迭代计划2|<br>站立会议|<br>…|<br>站立会议|<br>迭代评审|<br>迭代回顾|</p>
<p>Spint0要做一些准备活动，如高层的业务流程图、初始的用户故事列表、测试策略、发布计划、团队建设、技术架构的选择、设计UI的风格等。</p>
<h3 id="站立会议">站立会议</h3><p>晨会的要点：</p>
<ul>
<li>轮流发言，持Token者才可以发言；</li>
<li>不讨论深入细节；</li>
<li>不是对领导汇报，让团队中每个人都了解你的发言；</li>
<li>不能单独讨论，自发的有序的进行发言；</li>
<li>时间在15分钟以内。</li>
</ul>
<p>站立晨会的三个经典问题：昨天我完成了哪些工作；明天我打算做什么；完成我的目标是否存在什么障碍。<br>站立晨会的目的不是为了让大家都回答那三个问题，而是让团队围绕这三个问题，制定当天的工作计划并暴露问题。</p>
<h3 id="迭代验收和回顾">迭代验收和回顾</h3><p>迭代验收会议，通过演示可工作的软件检查需求是否满足客户要求；迭代验收的好处：</p>
<ul>
<li>通过演示可工作的软件来确认项目的进度，具有真实性；</li>
<li>能尽早获得用户对产品的反馈，使产品更贴近客户需求。</li>
<li>收集反馈。</li>
</ul>
<p>迭代回顾会议，目的是分享好的经验和发现改进点，促进团队不断进步，迭代回顾的好处：</p>
<ul>
<li>激励团队成员；</li>
<li>帮助团队挖掘优秀经验并继承；</li>
<li>避免团队犯重复的错误；</li>
<li>营造团队自主改进的氛围。</li>
</ul>
<h2 id="利用敏捷改进现有工作">利用敏捷改进现有工作</h2><p>即使不使用敏捷方式开发，也可以利用它的一些好的想法和实践可以用来提升目前的工作效率。</p>
<ul>
<li>比如敏捷开发中如何调动团队积极性，让每个人看到的是团队目标，而不是个人目标。</li>
<li>比如经常地交付可工作的软件：以此提高软件开发的质量和可交付性。</li>
<li>比如借鉴敏捷中设定Sprint(冲刺)的开发过程，调动开发人员的积极性以及明确每个开发阶段的目的性。</li>
</ul>
<h2 id="其他问题">其他问题</h2><ul>
<li>需求文档 或者 使用说明文档 写了100多页，但是写完之后基本没人看，这样的问题应该很普遍，该如何解决？<br>把Word文档迁移到Wiki上，大文档切细分成一个个独立的Wiki页面，Wiki可以统计页面的访问次数，有了足够的数据支撑之后就可以把访问次数少的页面去掉，以此来精简文档，这样留下来的文档内容就是真正有用的。</li>
</ul>
<ul>
<li>业务部门的需求太多而且每个都非常紧急，怎么处理？<br>业务部门拉一个人对需求按价值进行排序；需求收集例行化，主动收集，需求有一定的清晰度；回顾哪些需求不重要，做为武器。</li>
</ul>
]]></content:encoded>
      <comments>http://fancymax.github.io/2014/05/22/summaryOnSoftDevelopment/#disqus_thread</comments>
    </item>
    
  </channel>
</rss>
