<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Chance</title>
  <icon>https://www.gravatar.com/avatar/3458ca35c2ea49bdfd4dd2f81d6274b4</icon>
  <subtitle>Stay hungry, stay foolish.</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://chanceli.com/"/>
  <updated>2025-09-04T11:02:16.756Z</updated>
  <id>http://chanceli.com/</id>
  
  <author>
    <name>Chance</name>
    <email>lc@chanceli.com</email>
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>【Harmony】鸿蒙开发经验</title>
    <link href="http://chanceli.com/%E3%80%90Harmony%E3%80%91%E9%B8%BF%E8%92%99%E5%BC%80%E5%8F%91%E7%BB%8F%E9%AA%8C/"/>
    <id>http://chanceli.com/【Harmony】鸿蒙开发经验/</id>
    <published>2025-08-04T07:15:00.000Z</published>
    <updated>2025-09-04T11:02:16.756Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-下拉刷新不支持边缘滑动效果"><a href="#1-下拉刷新不支持边缘滑动效果" class="headerlink" title="1.下拉刷新不支持边缘滑动效果"></a>1.下拉刷新不支持边缘滑动效果</h2><h3 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h3><p>页面下拉刷新无响应，拉不动</p><h3 id="原因"><a href="#原因" class="headerlink" title="原因"></a>原因</h3><p>The edgeEffect value must be EdgeEffect.None<br><img src="/images/harmony/image-1.png" alt="img"><br><img src="/images/harmony/image-2.png" alt="img"><br>文档链接：<a href="https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fpulltorefresh" target="_blank" rel="noopener">https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Fpulltorefresh</a><br>另外关联的scroller也需要设置</p><h2 id="2-lottie-name不能相同"><a href="#2-lottie-name不能相同" class="headerlink" title="2.lottie name不能相同"></a>2.lottie name不能相同</h2><h3 id="问题描述-1"><a href="#问题描述-1" class="headerlink" title="问题描述"></a>问题描述</h3><p>从首页跳转到租约页，首页loading消失前，切换到租约页，租约页loading会提前消失。</p><h3 id="原因-1"><a href="#原因-1" class="headerlink" title="原因"></a>原因</h3><p><img src="/images/harmony/image-3.png" alt="img"><br>KeLoadingDialog传入Lottie的name是写死的，从A页面切换到B页面时，A的lottie destroy会导致B的lottie也destroy，提前消失了<br>解决方案<br>lottie name随机</p><h2 id="3-HTTPS图片连代理加载不出来"><a href="#3-HTTPS图片连代理加载不出来" class="headerlink" title="3.HTTPS图片连代理加载不出来"></a>3.HTTPS图片连代理加载不出来</h2><p>装证书或不连代理</p><p>鸿蒙端安装证书地址： charlesproxy.com/getssl</p><h2 id="4-CustomDialog作为临时变量内部无法关闭"><a href="#4-CustomDialog作为临时变量内部无法关闭" class="headerlink" title="4.CustomDialog作为临时变量内部无法关闭"></a>4.CustomDialog作为临时变量内部无法关闭</h2><h3 id="问题描述-2"><a href="#问题描述-2" class="headerlink" title="问题描述"></a>问题描述</h3><p>CustomDialogController作为临时变量，在弹窗内部无法关闭，controller为undefined<br><img src="/images/harmony/image-4.png" alt="img"></p><h3 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h3><p>dialog作为组件的成员变量<br>private dialogController: CustomDialogController | null = null;<br><img src="/images/harmony/image-5.png" alt="img"><br>推荐不依赖UI组件的弹窗方式，类似RPLoadingUtil<br><img src="/images/harmony/image-6.png" alt="img"><br><img src="/images/harmony/image-7.png" alt="img"></p><h2 id="5-类型转换不能使用as"><a href="#5-类型转换不能使用as" class="headerlink" title="5.类型转换不能使用as"></a>5.类型转换不能使用as</h2><h3 id="问题描述-3"><a href="#问题描述-3" class="headerlink" title="问题描述"></a>问题描述</h3><p>接口下发的某字段text有时是number类型，有时是string类型<br>某个方法的入参是string类型，用 text as string 断言为string<br>运行时 number 会报TypeError crash</p><h3 id="原因-2"><a href="#原因-2" class="headerlink" title="原因"></a>原因</h3><p><img src="/images/harmony/image-8.png" alt="img"><br>解决方案<br>String(text)</p><h2 id="6-关闭H5白名单校验"><a href="#6-关闭H5白名单校验" class="headerlink" title="6.关闭H5白名单校验"></a>6.关闭H5白名单校验</h2><h3 id="问题描述-4"><a href="#问题描述-4" class="headerlink" title="问题描述"></a>问题描述</h3><p>未加过白名单的链接无法打开</p><h3 id="解决方案-1"><a href="#解决方案-1" class="headerlink" title="解决方案"></a>解决方案</h3><p>摇一摇-调试选项-取消使用Web白名单<br><img src="/images/harmony/image-9.png" alt="img"></p><h2 id="7-List组件不设置高度滑动不到底"><a href="#7-List组件不设置高度滑动不到底" class="headerlink" title="7.List组件不设置高度滑动不到底"></a>7.List组件不设置高度滑动不到底</h2><h3 id="解决方案-2"><a href="#解决方案-2" class="headerlink" title="解决方案"></a>解决方案</h3><p>layoutWeight(1)可以自适应占满剩余空间父容器尺寸</p><h2 id="8-merge分支后changeid为空"><a href="#8-merge分支后changeid为空" class="headerlink" title="8.merge分支后changeid为空"></a>8.merge分支后changeid为空</h2><p>git merge –no-f dev-1215  自动生成一个changeid</p><h2 id="9-切换分支后清理缓存"><a href="#9-切换分支后清理缓存" class="headerlink" title="9.切换分支后清理缓存"></a>9.切换分支后清理缓存</h2><p>ohpm clean<br>sh lj-clean<br>ohpm install</p><h2 id="10-字号、色值使用常量类"><a href="#10-字号、色值使用常量类" class="headerlink" title="10.字号、色值使用常量类"></a>10.字号、色值使用常量类</h2><p>好处:</p><ul><li>色值：主题色统一修改</li><li>字号：字号统一修改单位</li></ul><h2 id="11-真机调试"><a href="#11-真机调试" class="headerlink" title="11.真机调试"></a>11.真机调试</h2><p>需要登录账号 自动签名：<a href="https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-signing-V5#section18815157237" target="_blank" rel="noopener">https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-signing-V5#section18815157237</a></p><h2 id="12-环境配置"><a href="#12-环境配置" class="headerlink" title="12.环境配置"></a>12.环境配置</h2><h3 id="问题描述-5"><a href="#问题描述-5" class="headerlink" title="问题描述"></a>问题描述</h3><figure class="highlight plain"><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">ERR_PNPM_FETCH_404</span><br><span class="line">Installing dependencies...</span><br><span class="line"> ERR_PNPM_FETCH_404  GET https://repo.huaweicloud.com/repository/npm/@ke%2Frouter-plugin: Not Found - 404</span><br><span class="line">This error happened while installing a direct dependency of /Users/lichang/.hvigor/project_caches/b8db3eae16304d1d8731239702645cb2/workspace</span><br><span class="line">@ke/router-plugin is not in the npm registry, or you have no permission to fetch it.</span><br><span class="line">No authorization header was set for the request.</span><br><span class="line">Error: /Users/lichang/.hvigor/wrapper/tools/node_modules/.bin/pnpm install execute failed.See above for details.</span><br></pre></td></tr></table></figure><h3 id="解决方案-3"><a href="#解决方案-3" class="headerlink" title="解决方案"></a>解决方案</h3><p>配置npm<br>将下面配置添加到~/.npmrc中，邮箱地址替换为自己的邮箱，//部分保持原样，不要删除<br><figure class="highlight plain"><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">@ke:registry=http://artifactory.intra.ke.com/artifactory/api/npm/npm-mobile-release/</span><br><span class="line">@ohos:registry=https://repo.harmonyos.com/npm/</span><br><span class="line">registry=https://registry.npmmirror.com/</span><br><span class="line">//artifactory.intra.ke.com/artifactory/api/npm/npm-mobile-release/:_password=&quot;bW9iaWxlZnVsbDEyMzQ1Ng==&quot;</span><br><span class="line">//artifactory.intra.ke.com/artifactory/api/npm/npm-mobile-release/:username=mobilefull</span><br><span class="line">//artifactory.intra.ke.com/artifactory/api/npm/npm-mobile-release/:email=邮箱地址</span><br><span class="line">//artifactory.intra.ke.com/artifactory/api/npm/npm-mobile-release/:always-auth=true</span><br><span class="line">loglevel=info</span><br><span class="line">strict-ssl=false</span><br></pre></td></tr></table></figure></p><p>参考链接：鸿蒙路由2.0的使用与原理</p><h2 id="13-富文本"><a href="#13-富文本" class="headerlink" title="13.富文本"></a>13.富文本</h2><h3 id="问题描述-6"><a href="#问题描述-6" class="headerlink" title="问题描述"></a>问题描述</h3><p><img src="/images/harmony/image-10.png" alt="img"><br>1.Text包裹Span<br>（1）Span不支持margin、padding、width、height，左右间距只能通过空格来处理，不精准<br>（2）标签不居中，需求里标签字号比第一个文本字号小，所以高度小，不居中，尝试设置lineHeight（高度不变）和baselineOffset不行（会文字挪上去了，背景不动）<br>（3)标签无法设置上下间距<br>2.RichText支持的html标签有限，实现不了</p><h3 id="解决方案-4"><a href="#解决方案-4" class="headerlink" title="解决方案"></a>解决方案</h3><p>标签使用图片，ImageSpan<br><img src="/images/harmony/image-11.png" alt="img"></p><h2 id="14-弹窗弹起键盘后，存在空隙"><a href="#14-弹窗弹起键盘后，存在空隙" class="headerlink" title="14.弹窗弹起键盘后，存在空隙"></a>14.弹窗弹起键盘后，存在空隙</h2><h3 id="问题描述-7"><a href="#问题描述-7" class="headerlink" title="问题描述"></a>问题描述</h3><p><img src="/images/harmony/image-12.jpeg" alt="img"><br>CustomDialog中有TextArea，输入时，软键盘顶起CustomDialog，此时，软键盘与CustomDialog之间存在一些空隙。</p><h3 id="原因-3"><a href="#原因-3" class="headerlink" title="原因"></a>原因</h3><p>系统特性：弹窗避让软键盘时，与软键盘之间存在16vp的安全间距。<br><img src="/images/harmony/image-13.png" alt="img"><br>文档链接：自定义弹窗 (CustomDialog)<br>参考链接：HarmonyOS CustomDialog弹窗被软键盘顶起后，存在空隙</p><h3 id="解决方案-5"><a href="#解决方案-5" class="headerlink" title="解决方案"></a>解决方案</h3><p>.offset({ y: 16 })</p><h2 id="15-Tab里的页面出现不走onShown"><a href="#15-Tab里的页面出现不走onShown" class="headerlink" title="15.Tab里的页面出现不走onShown"></a>15.Tab里的页面出现不走onShown</h2><h3 id="问题描述-8"><a href="#问题描述-8" class="headerlink" title="问题描述"></a>问题描述</h3><p><img src="/images/harmony/image-14.png" alt="img"><br>首页或租约页设置了keNavDestinationLifecycleCallback后，不生效。</p><h3 id="原因-4"><a href="#原因-4" class="headerlink" title="原因"></a>原因</h3><p>走了Tab的keNavDestinationLifecycleCallback<br><img src="/images/harmony/image-15.png" alt="img"></p><h3 id="解决方案-6"><a href="#解决方案-6" class="headerlink" title="解决方案"></a>解决方案</h3><p>方案一：Tab传参给对应页面<br>方案二：页面根组件onVisibleAreaChange</p><figure class="highlight plain"><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">.onVisibleAreaChange([0.0, 1], (isVisible: boolean, currentRatio: number) =&gt; &#123;</span><br><span class="line">  if (isVisible &amp;&amp; currentRatio &gt;= 1.0) &#123;</span><br><span class="line">    this.pageColumnFullyVisible();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h2 id="16-跨页面传值"><a href="#16-跨页面传值" class="headerlink" title="16.跨页面传值"></a>16.跨页面传值</h2><h3 id="问题描述-9"><a href="#问题描述-9" class="headerlink" title="问题描述"></a>问题描述</h3><p>租约页-&gt;合同详情-隐藏原因弹窗，隐藏租约后，租约页置空请求入参</p><h3 id="解决方案-7"><a href="#解决方案-7" class="headerlink" title="解决方案"></a>解决方案</h3><p>使用eventHub<br>租约页订阅事件：<br>const uiContext = appConfig.appContext.appContext;<br>// 订阅隐藏租约成功事件<br>uiContext.eventHub.on(RPLeaseEvent.HIDE_LEASE_SUCCESS, this.hideLeaseSuccessHandler.bind(this));<br>不用箭头函数时注意this指向<br>隐藏成功发送事件：<br>// 隐藏成功<br>const uiContext = appConfig.appContext.appContext;<br>uiContext.eventHub.emit(RPLeaseEvent.HIDE_LEASE_SUCCESS, this.order?.orderId ?? ‘’);</p><h2 id="17-调试技巧"><a href="#17-调试技巧" class="headerlink" title="17.调试技巧"></a>17.调试技巧</h2><p><img src="/images/harmony/image-17.png" alt="img"></p><h2 id="18-子孙组件传值"><a href="#18-子孙组件传值" class="headerlink" title="18.子孙组件传值"></a>18.子孙组件传值</h2><p>可以使用@provider、consumer，极其优雅</p><h2 id="19-禁用-fast-forward-合并"><a href="#19-禁用-fast-forward-合并" class="headerlink" title="19.禁用 fast-forward 合并"></a>19.禁用 fast-forward 合并</h2><h3 id="问题描述-10"><a href="#问题描述-10" class="headerlink" title="问题描述"></a>问题描述</h3><p>开发分支有提交记录，相比master有改动，为了合并开发分支上的提交<br>报错：<br> ! [remote rejected] HEAD -&gt; refs/for/master (no new changes)<br>error: failed to push some refs to ‘ssh://gerrit.lianjia.com:29418/mobile_harmony/beike_rentplat_module’</p><h3 id="解决方案-8"><a href="#解决方案-8" class="headerlink" title="解决方案"></a>解决方案</h3><p>git merge –no-ff feature-branch<br>即使可以 fast-forward 合并，也要生成一个合并提交。</p><h2 id="20-超出父容器"><a href="#20-超出父容器" class="headerlink" title="20.超出父容器"></a>20.超出父容器</h2><h3 id="问题描述-11"><a href="#问题描述-11" class="headerlink" title="问题描述"></a>问题描述</h3><p><img src="/images/harmony/image-18.png" alt="img"><br><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">Column() &#123;</span><br><span class="line">  // 姓名</span><br><span class="line">  Text(agent.name)</span><br><span class="line">    .fontSize($r(&apos;app.float.font_size_13&apos;))</span><br><span class="line">    .fontWeight(FontWeight.Medium)</span><br><span class="line">    .maxLines(1)</span><br><span class="line">    .textOverflow(&#123; overflow: TextOverflow.Ellipsis &#125;)</span><br><span class="line">    .margin(&#123; top: 1 &#125;)</span><br><span class="line"></span><br><span class="line">  // 描述</span><br><span class="line">  Text(agent.agentDesc)</span><br><span class="line">    .fontSize($r(&apos;app.float.font_size_11&apos;))</span><br><span class="line">    .fontColor($r(&apos;app.color.999999&apos;))</span><br><span class="line">    .maxLines(1)</span><br><span class="line">    .textOverflow(&#123; overflow: TextOverflow.Ellipsis &#125;)</span><br><span class="line">    .margin(&#123; top: 5 &#125;)</span><br><span class="line">&#125;</span><br><span class="line">.alignItems(HorizontalAlign.Start)</span><br><span class="line">.justifyContent(FlexAlign.Start)</span><br><span class="line">.margin(&#123; left: 6 &#125;)</span><br><span class="line">.layoutWeight(1)</span><br></pre></td></tr></table></figure></p><p>确保父级 Row() 或 Column() 的子项可以收缩/限制宽度</p><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">.layoutWeight(1)</span><br></pre></td></tr></table></figure><p>让 Column 可被压缩</p><p>飞书链接：<a href="https://quira9nj56.feishu.cn/docx/KVVfdgUiToMYYdxsz4vcni1mnsc" target="_blank" rel="noopener">https://quira9nj56.feishu.cn/docx/KVVfdgUiToMYYdxsz4vcni1mnsc</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;1-下拉刷新不支持边缘滑动效果&quot;&gt;&lt;a href=&quot;#1-下拉刷新不支持边缘滑动效果&quot; class=&quot;headerlink&quot; title=&quot;1.下拉刷新不支持边缘滑动效果&quot;&gt;&lt;/a&gt;1.下拉刷新不支持边缘滑动效果&lt;/h2&gt;&lt;h3 id=&quot;问题描述&quot;&gt;&lt;a href
      
    
    </summary>
    
      <category term="鸿蒙开发" scheme="http://chanceli.com/categories/%E9%B8%BF%E8%92%99%E5%BC%80%E5%8F%91/"/>
    
    
      <category term="鸿蒙开发" scheme="http://chanceli.com/tags/%E9%B8%BF%E8%92%99%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>【大模型】DeepSeek调研</title>
    <link href="http://chanceli.com/DeepSeek%E8%B0%83%E7%A0%94/"/>
    <id>http://chanceli.com/DeepSeek调研/</id>
    <published>2025-03-05T11:48:00.000Z</published>
    <updated>2025-09-02T14:53:43.501Z</updated>
    
    <content type="html"><![CDATA[<h2 id="本地部署"><a href="#本地部署" class="headerlink" title="本地部署"></a>本地部署</h2><h3 id="1-下载安装Ollama"><a href="#1-下载安装Ollama" class="headerlink" title="1.下载安装Ollama"></a>1.下载安装Ollama</h3><p><a href="https://ollama.com/download" target="_blank" rel="noopener">https://ollama.com/download</a> </p><h3 id="2-下载运行deepseek-r1"><a href="#2-下载运行deepseek-r1" class="headerlink" title="2.下载运行deepseek-r1"></a>2.下载运行deepseek-r1</h3><p><a href="https://ollama.com/library/deepseek-r1:1.5b" target="_blank" rel="noopener">https://ollama.com/library/deepseek-r1:1.5b</a><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">ollama run deepseek-r1:1.5b</span><br></pre></td></tr></table></figure></p><h3 id="3-局域网访问API"><a href="#3-局域网访问API" class="headerlink" title="3.局域网访问API"></a>3.局域网访问API</h3><p>（1）修改监听地址 ~/.zshrc<br><figure class="highlight plain"><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"># 设置Ollama监听所有网络接口（0.0.0.0表示允许所有IP访问）</span><br><span class="line">export OLLAMA_HOST=0.0.0.0:11434</span><br></pre></td></tr></table></figure></p><p>（2）立即生效<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">source ~/.zshrc</span><br></pre></td></tr></table></figure></p><p>（3）不生效可以用下面这个<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">OLLAMA_HOST=0.0.0.0 ollama serve</span><br></pre></td></tr></table></figure></p><p>可以访问了：<a href="http://10.33.158.12:11434/api/tags" target="_blank" rel="noopener">http://10.33.158.12:11434/api/tags</a> </p><h2 id="意图识别"><a href="#意图识别" class="headerlink" title="意图识别"></a>意图识别</h2><h3 id="实现方式"><a href="#实现方式" class="headerlink" title="实现方式"></a>实现方式</h3><p>本地部署 DeepSeek 后，要让它能够进行意图识别（例如把“这个房子的租金是多少”映射到“查租金”），有以下几种方式：</p><h4 id="1-通过-system-提示词-Prompt-设定规则"><a href="#1-通过-system-提示词-Prompt-设定规则" class="headerlink" title="1.通过 system 提示词 (Prompt) 设定规则"></a>1.通过 system 提示词 (Prompt) 设定规则</h4><p>你可以在 system 提示词中提供指导，告诉 DeepSeek 需要按照你的要求进行意图分类，例如：<br><figure class="highlight plain"><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><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">你是一个经验丰富的专业租赁经纪人，负责识别用户的意图，并返回 JSON 格式的结果。  </span><br><span class="line">你只需要返回 JSON，不要输出其他内容。  </span><br><span class="line"></span><br><span class="line">返回格式如下：</span><br><span class="line">&#123;</span><br><span class="line">  &quot;command&quot;: &quot;意图名称&quot;,</span><br><span class="line">  &quot;text&quot;: &quot;简短的回应&quot;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">意图类别：</span><br><span class="line">- &quot;查租金&quot;：如果用户询问房屋租金、价格或费用相关信息</span><br><span class="line">- &quot;发放优惠券&quot;: 如果用户询问房源优惠信息</span><br><span class="line">- &quot;看厨房&quot;: 我想看看厨房</span><br><span class="line">- &quot;预约实地带看&quot;: 如果用户希望安排看房，想实地看看</span><br><span class="line">- &quot;线上签约&quot;: 如果用户想在线签约</span><br><span class="line">- &quot;推荐房源&quot;：如果用户询问还有别的房子可以看吗？有其他推荐吗？</span><br><span class="line"></span><br><span class="line">示例：</span><br><span class="line">用户：这个房子的租金是多少？</span><br><span class="line">你应该回复：</span><br><span class="line">&#123;</span><br><span class="line">  &quot;command&quot;: &quot;查租金&quot;,</span><br><span class="line">  &quot;text&quot;: &quot;好的，帮你查一下租金&quot;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">用户：附近有什么房子出租？</span><br><span class="line">你应该回复：</span><br><span class="line">&#123;</span><br><span class="line">  &quot;command&quot;: &quot;推荐房源&quot;,</span><br><span class="line">  &quot;text&quot;: &quot;我来帮你找附近的房源&quot;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">用户：我想预约看房</span><br><span class="line">你应该回复：</span><br><span class="line">&#123;</span><br><span class="line">  &quot;command&quot;: &quot;预约实地带看&quot;,</span><br><span class="line">  &quot;text&quot;: &quot;好的，帮你预约看房&quot;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">用户：有优惠吗？</span><br><span class="line">你应该回复：</span><br><span class="line">&#123;</span><br><span class="line">  &quot;command&quot;: &quot;发放优惠券&quot;,</span><br><span class="line">  &quot;text&quot;: &quot;好的，帮你查看优惠信息&quot;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">请严格按照 JSON 格式返回，不要输出其他内容。</span><br></pre></td></tr></table></figure></p><p>然后，你在调用 DeepSeek 的时候，把这个 system 提示词加入 messages 参数中，让模型按此规则识别意图。</p><h4 id="2-微调-Fine-tuning"><a href="#2-微调-Fine-tuning" class="headerlink" title="2.微调 (Fine-tuning)"></a>2.微调 (Fine-tuning)</h4><p>如果你的需求很稳定，并且你有较多的训练数据（比如用户问题和对应意图的标注数据），可以微调 DeepSeek，让它专门适应你的任务。例如，你可以用类似这样的标注数据：<br><figure class="highlight plain"><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">&#123;&quot;input&quot;: &quot;这个房子的租金是多少？&quot;, &quot;output&quot;: &quot;查租金&quot;&#125;</span><br><span class="line">&#123;&quot;input&quot;: &quot;附近有什么房子出租？&quot;, &quot;output&quot;: &quot;查房源&quot;&#125;</span><br><span class="line">&#123;&quot;input&quot;: &quot;我想预约看房&quot;, &quot;output&quot;: &quot;预约看房&quot;&#125;</span><br></pre></td></tr></table></figure></p><p>然后对 DeepSeek 进行微调，让它更精准地匹配你的业务场景。</p><h4 id="3-使用向量检索（Embedding-检索）"><a href="#3-使用向量检索（Embedding-检索）" class="headerlink" title="3. 使用向量检索（Embedding + 检索）"></a>3. 使用向量检索（Embedding + 检索）</h4><p>如果你的意图库是固定的，你可以使用 DeepSeek 的 embedding 模型，把所有的意图示例转换成向量，并存入向量数据库（如 FAISS、Milvus 等）。当用户输入查询时，把它的向量与已有意图的向量匹配，找最相似的意图。</p><h4 id="4-结合规则（正则-关键词匹配）-LLM"><a href="#4-结合规则（正则-关键词匹配）-LLM" class="headerlink" title="4. 结合规则（正则/关键词匹配）+ LLM"></a>4. 结合规则（正则/关键词匹配）+ LLM</h4><p>如果意图识别的需求比较简单，你可以先用关键词匹配或正则表达式筛选出明显的意图，再用 LLM 处理复杂情况。例如：<br>●如果输入中包含“租金”、“价格”，直接返回“查租金”；<br>●否则，调用 DeepSeek 进行意图识别。</p><p>这种方法可以减少 LLM 处理的请求量，提高效率。</p><h3 id="选型建议："><a href="#选型建议：" class="headerlink" title="选型建议："></a>选型建议：</h3><p>如果意图较少，规则清晰 → system 提示词就够了，简单高效。<br>如果数据多，想优化效果 → 适合微调模型，让它更精准。<br>如果意图较多，且经常更新 → 适合向量检索方案，灵活扩展。</p><h3 id="本地部署DeepSeek-R1-1-5B-效果："><a href="#本地部署DeepSeek-R1-1-5B-效果：" class="headerlink" title="本地部署DeepSeek-R1 1.5B 效果："></a>本地部署DeepSeek-R1 1.5B 效果：</h3><p>耗时3.84s，结果不稳定。<br>curl：<br><figure class="highlight plain"><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">curl -X POST &quot;http://10.33.158.12:11434/api/chat&quot; \</span><br><span class="line">     -H &quot;Content-Type: application/json&quot; \</span><br><span class="line">     -d &apos;&#123;</span><br><span class="line">           &quot;model&quot;: &quot;deepseek-r1:1.5b&quot;,</span><br><span class="line">           &quot;messages&quot;: [</span><br><span class="line">             &#123;</span><br><span class="line">               &quot;role&quot;: &quot;system&quot;,</span><br><span class="line">               &quot;content&quot;: &quot;你是一个经验丰富的专业租赁经纪人，负责识别用户的意图，并返回 JSON 格式的结果。\n你只需要返回 JSON，不要输出其他内容。\n\n返回格式如下：\n&#123;\n  \&quot;command\&quot;: \&quot;意图名称\&quot;,\n  \&quot;text\&quot;: \&quot;简短的回应\&quot;\n&#125;\n\n意图类别：\n- \&quot;查租金\&quot;：如果用户询问房屋租金、价格或费用相关信息\n- \&quot;发放优惠券\&quot;: 如果用户询问房源优惠信息\n- \&quot;看厨房\&quot;: 我想看看厨房\n- \&quot;预约实地带看\&quot;: 如果用户希望安排看房，想实地看看\n- \&quot;线上签约\&quot;: 如果用户想在线签约\n- \&quot;推荐房源\&quot;：如果用户询问还有别的房子可以看吗？有其他推荐吗？\n\n示例：\n用户：这个房子的租金是多少？\n你应该回复：\n&#123;\n  \&quot;command\&quot;: \&quot;查租金\&quot;,\n  \&quot;text\&quot;: \&quot;好的，帮你查一下租金\&quot;\n&#125;\n\n用户：附近有什么房子出租？\n你应该回复：\n&#123;\n  \&quot;command\&quot;: \&quot;推荐房源\&quot;,\n  \&quot;text\&quot;: \&quot;我来帮你找附近的房源\&quot;\n&#125;\n\n用户：我想预约看房\n你应该回复：\n&#123;\n  \&quot;command\&quot;: \&quot;预约实地带看\&quot;,\n  \&quot;text\&quot;: \&quot;好的，帮你预约看房\&quot;\n&#125;\n\n用户：有优惠吗？\n你应该回复：\n&#123;\n  \&quot;command\&quot;: \&quot;发放优惠券\&quot;,\n  \&quot;text\&quot;: \&quot;好的，帮你查看优惠信息\&quot;\n&#125;\n\n请严格按照 JSON 格式返回，不要输出其他内容。&quot;</span><br><span class="line">             &#125;,</span><br><span class="line">             &#123;</span><br><span class="line">               &quot;role&quot;: &quot;user&quot;,</span><br><span class="line">               &quot;content&quot;: &quot;能便宜吗？&quot;</span><br><span class="line">             &#125;</span><br><span class="line">           ],</span><br><span class="line">           &quot;temperature&quot;: 0,</span><br><span class="line">           &quot;stream&quot;: false,</span><br><span class="line">           &quot;max_tokens&quot;: 100</span><br><span class="line">         &#125;&apos;</span><br></pre></td></tr></table></figure></p><h3 id="DeepSeek官网API效果："><a href="#DeepSeek官网API效果：" class="headerlink" title="DeepSeek官网API效果："></a>DeepSeek官网API效果：</h3><p>耗时9.45s，结果稳定<br>curl：<br><figure class="highlight plain"><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><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">curl -L -X POST &apos;https://api.deepseek.com/chat/completions&apos; \</span><br><span class="line">-H &apos;Content-Type: application/json&apos; \</span><br><span class="line">-H &apos;Accept: application/json&apos; \</span><br><span class="line">-H &apos;Authorization: Bearer sk-e856b3fef50d4da1902b2a41de3c1a10&apos; \</span><br><span class="line">--data-raw &apos;&#123;</span><br><span class="line">  &quot;messages&quot;: [</span><br><span class="line">    &#123;</span><br><span class="line">               &quot;role&quot;: &quot;system&quot;,</span><br><span class="line">               &quot;content&quot;: &quot;你是一个经验丰富的专业租赁经纪人，负责识别用户的意图，并返回 JSON 格式的结果。\n你只需要返回 JSON，不要输出其他内容。\n\n返回格式如下：\n&#123;\n  \&quot;command\&quot;: \&quot;意图名称\&quot;,\n  \&quot;text\&quot;: \&quot;简短的回应\&quot;\n&#125;\n\n意图类别：\n- \&quot;查租金\&quot;：如果用户询问房屋租金、价格或费用相关信息\n- \&quot;发放优惠券\&quot;: 如果用户询问房源优惠信息\n- \&quot;看厨房\&quot;: 我想看看厨房\n- \&quot;预约实地带看\&quot;: 如果用户希望安排看房，想实地看看\n- \&quot;线上签约\&quot;: 如果用户想在线签约\n- \&quot;推荐房源\&quot;：如果用户询问还有别的房子可以看吗？有其他推荐吗？\n\n示例：\n用户：这个房子的租金是多少？\n你应该回复：\n&#123;\n  \&quot;command\&quot;: \&quot;查租金\&quot;,\n  \&quot;text\&quot;: \&quot;好的，帮你查一下租金\&quot;\n&#125;\n\n用户：附近有什么房子出租？\n你应该回复：\n&#123;\n  \&quot;command\&quot;: \&quot;推荐房源\&quot;,\n  \&quot;text\&quot;: \&quot;我来帮你找附近的房源\&quot;\n&#125;\n\n用户：我想预约看房\n你应该回复：\n&#123;\n  \&quot;command\&quot;: \&quot;预约实地带看\&quot;,\n  \&quot;text\&quot;: \&quot;好的，帮你预约看房\&quot;\n&#125;\n\n用户：有优惠吗？\n你应该回复：\n&#123;\n  \&quot;command\&quot;: \&quot;发放优惠券\&quot;,\n  \&quot;text\&quot;: \&quot;好的，帮你查看优惠信息\&quot;\n&#125;\n\n请严格按照 JSON 格式返回，不要输出其他内容。&quot;</span><br><span class="line">             &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">      &quot;content&quot;: &quot;能便宜吗？&quot;,</span><br><span class="line">      &quot;role&quot;: &quot;user&quot;</span><br><span class="line">    &#125;</span><br><span class="line">  ],</span><br><span class="line">  &quot;model&quot;: &quot;deepseek-chat&quot;,</span><br><span class="line">  &quot;frequency_penalty&quot;: 0,</span><br><span class="line">  &quot;max_tokens&quot;: 2048,</span><br><span class="line">  &quot;presence_penalty&quot;: 0,</span><br><span class="line">  &quot;response_format&quot;: &#123;</span><br><span class="line">    &quot;type&quot;: &quot;text&quot;</span><br><span class="line">  &#125;,</span><br><span class="line">  &quot;stop&quot;: null,</span><br><span class="line">  &quot;stream&quot;: false,</span><br><span class="line">  &quot;stream_options&quot;: null,</span><br><span class="line">  &quot;temperature&quot;: 1,</span><br><span class="line">  &quot;top_p&quot;: 1,</span><br><span class="line">  &quot;tools&quot;: null,</span><br><span class="line">  &quot;tool_choice&quot;: &quot;none&quot;,</span><br><span class="line">  &quot;logprobs&quot;: false,</span><br><span class="line">  &quot;top_logprobs&quot;: null</span><br><span class="line">&#125;&apos;</span><br></pre></td></tr></table></figure></p><h2 id="本地部署支持的接口："><a href="#本地部署支持的接口：" class="headerlink" title="本地部署支持的接口："></a>本地部署支持的接口：</h2><h3 id="1-文本生成接口："><a href="#1-文本生成接口：" class="headerlink" title="1.文本生成接口："></a>1.文本生成接口：</h3><p>请求方式：POST /api/generate<br>功能：根据提供的提示词（prompt）生成文本。<br>请求参数：<br>●model（必需）：模型名称，例如 deepseek-r1:1.5b。<br>●prompt（必需）：要生成响应的提示词。<br>●stream（可选）：是否以流式方式返回响应。</p><p>curl示例：<br><figure class="highlight plain"><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">curl -X POST http://10.33.158.12:11434/api/generate \</span><br><span class="line">-H &quot;Content-Type: application/json&quot; \</span><br><span class="line">-d &apos;&#123;</span><br><span class="line">  &quot;model&quot;: &quot;deepseek-r1:1.5b&quot;,</span><br><span class="line">  &quot;prompt&quot;: &quot;请用专业经纪人给客户介绍房源的口吻帮我润色下：这是厨房&quot;,</span><br><span class="line">  &quot;stream&quot;: false</span><br><span class="line">&#125;&apos;</span><br></pre></td></tr></table></figure></p><p>效果：</p><h3 id="2-对话接口："><a href="#2-对话接口：" class="headerlink" title="2.对话接口："></a>2.对话接口：</h3><p>请求方式：POST /api/chat<br>功能：与模型进行多轮对话。<br>请求参数：<br>●model（必需）：模型名称。<br>●messages（必需）：一个包含对话历史的列表，每个消息包含 role（角色，如 user 或 assistant）和 content（内容）。<br>●stream（可选）：是否以流式方式返回响应。</p><p>curl示例：<br><figure class="highlight plain"><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">curl -X POST &quot;http://10.33.158.12:11434/api/chat&quot; \</span><br><span class="line">     -H &quot;Content-Type: application/json&quot; \</span><br><span class="line">     -d &apos;&#123;</span><br><span class="line">           &quot;model&quot;: &quot;deepseek-r1:1.5b&quot;,</span><br><span class="line">           &quot;messages&quot;: [</span><br><span class="line">             &#123;</span><br><span class="line">               &quot;role&quot;: &quot;system&quot;,</span><br><span class="line">               &quot;content&quot;: &quot;你是一个经验丰富的专业租赁经纪人，负责识别用户的意图，并返回 JSON 格式的结果。\n你只需要返回 JSON，不要输出其他内容。\n\n返回格式如下：\n&#123;\n  \&quot;command\&quot;: \&quot;意图名称\&quot;,\n  \&quot;text\&quot;: \&quot;简短的回应\&quot;\n&#125;\n\n意图类别：\n- \&quot;查租金\&quot;：如果用户询问房屋租金、价格或费用相关信息\n- \&quot;发放优惠券\&quot;: 如果用户询问房源优惠信息\n- \&quot;看厨房\&quot;: 我想看看厨房\n- \&quot;预约实地带看\&quot;: 如果用户希望安排看房，想实地看看\n- \&quot;线上签约\&quot;: 如果用户想在线签约\n- \&quot;推荐房源\&quot;：如果用户询问还有别的房子可以看吗？有其他推荐吗？\n\n示例：\n用户：这个房子的租金是多少？\n你应该回复：\n&#123;\n  \&quot;command\&quot;: \&quot;查租金\&quot;,\n  \&quot;text\&quot;: \&quot;好的，帮你查一下租金\&quot;\n&#125;\n\n用户：附近有什么房子出租？\n你应该回复：\n&#123;\n  \&quot;command\&quot;: \&quot;推荐房源\&quot;,\n  \&quot;text\&quot;: \&quot;我来帮你找附近的房源\&quot;\n&#125;\n\n用户：我想预约看房\n你应该回复：\n&#123;\n  \&quot;command\&quot;: \&quot;预约实地带看\&quot;,\n  \&quot;text\&quot;: \&quot;好的，帮你预约看房\&quot;\n&#125;\n\n用户：有优惠吗？\n你应该回复：\n&#123;\n  \&quot;command\&quot;: \&quot;发放优惠券\&quot;,\n  \&quot;text\&quot;: \&quot;好的，帮你查看优惠信息\&quot;\n&#125;\n\n请严格按照 JSON 格式返回，不要输出其他内容。&quot;</span><br><span class="line">             &#125;,</span><br><span class="line">             &#123;</span><br><span class="line">               &quot;role&quot;: &quot;user&quot;,</span><br><span class="line">               &quot;content&quot;: &quot;能便宜吗？&quot;</span><br><span class="line">             &#125;</span><br><span class="line">           ],</span><br><span class="line">           &quot;temperature&quot;: 0,</span><br><span class="line">           &quot;stream&quot;: false,</span><br><span class="line">           &quot;max_tokens&quot;: 100</span><br><span class="line">         &#125;&apos;</span><br></pre></td></tr></table></figure></p><p>效果：</p><p>结果不稳定</p><h3 id="3-列出模型"><a href="#3-列出模型" class="headerlink" title="3.列出模型"></a>3.列出模型</h3><p><a href="http://10.33.158.12:11434/api/tags" target="_blank" rel="noopener">http://10.33.158.12:11434/api/tags</a><br>效果：</p><h4 id="DeepSeek官网API支持的接口："><a href="#DeepSeek官网API支持的接口：" class="headerlink" title="DeepSeek官网API支持的接口："></a>DeepSeek官网API支持的接口：</h4><p><a href="https://api-docs.deepseek.com/zh-cn/api/create-chat-completion" target="_blank" rel="noopener">https://api-docs.deepseek.com/zh-cn/api/create-chat-completion</a><br><a href="https://api-docs.deepseek.com/zh-cn/api/create-completion" target="_blank" rel="noopener">https://api-docs.deepseek.com/zh-cn/api/create-completion</a><br><a href="https://api-docs.deepseek.com/zh-cn/api/list-models" target="_blank" rel="noopener">https://api-docs.deepseek.com/zh-cn/api/list-models</a> </p><h3 id="带上下文的意图"><a href="#带上下文的意图" class="headerlink" title="带上下文的意图"></a>带上下文的意图</h3><p>多轮对话放在messages里<br><figure class="highlight plain"><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">messages: [</span><br><span class="line">    &#123;&quot;role&quot;: &quot;system&quot;, &quot;content&quot;: &quot;你是一位乐于助人的助手&quot;&#125;,</span><br><span class="line">    &#123;&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;中国的首都是哪里？&quot;&#125;,</span><br><span class="line">    &#123;&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: &quot;中国的首都是北京。&quot;&#125;,</span><br><span class="line">    &#123;&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;美国的首都是哪里？&quot;&#125;</span><br><span class="line">]</span><br></pre></td></tr></table></figure></p><p><a href="https://api-docs.deepseek.com/zh-cn/guides/multi_round_chat" target="_blank" rel="noopener">https://api-docs.deepseek.com/zh-cn/guides/multi_round_chat</a> </p><h3 id="响应速度"><a href="#响应速度" class="headerlink" title="响应速度"></a>响应速度</h3><p>通常在几秒内返回</p><h3 id="Function-Calling"><a href="#Function-Calling" class="headerlink" title="Function Calling"></a>Function Calling</h3><p>Function Calling 让模型能够调用外部工具，来增强自身能力。<br>示例流程：<br>●用户：询问现在的天气<br>●模型：返回 function get_weather({location: ‘Hangzhou’})<br>●用户：调用 function get_weather({location: ‘Hangzhou’})，并传给模型。<br>●模型：返回自然语言，”The current temperature in Hangzhou is 24°C.”</p><p>注：上述代码中 get_weather 函数功能需由用户提供，模型本身不执行具体函数。<br><a href="https://api-docs.deepseek.com/zh-cn/guides/function_calling" target="_blank" rel="noopener">https://api-docs.deepseek.com/zh-cn/guides/function_calling</a> </p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;本地部署&quot;&gt;&lt;a href=&quot;#本地部署&quot; class=&quot;headerlink&quot; title=&quot;本地部署&quot;&gt;&lt;/a&gt;本地部署&lt;/h2&gt;&lt;h3 id=&quot;1-下载安装Ollama&quot;&gt;&lt;a href=&quot;#1-下载安装Ollama&quot; class=&quot;headerlink&quot; 
      
    
    </summary>
    
      <category term="大模型" scheme="http://chanceli.com/categories/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    
    
      <category term="大模型" scheme="http://chanceli.com/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B/"/>
    
  </entry>
  
  <entry>
    <title>【前端】开发Tips</title>
    <link href="http://chanceli.com/%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91Tips/"/>
    <id>http://chanceli.com/前端开发Tips/</id>
    <published>2024-04-03T08:21:00.000Z</published>
    <updated>2024-04-03T12:19:38.767Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-添加-vConsole"><a href="#1-添加-vConsole" class="headerlink" title="1.添加 vConsole"></a>1.添加 vConsole</h2><figure class="highlight html"><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="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"https://cdn.bootcss.com/vConsole/3.2.2/vconsole.min.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="undefined">    var vConsole = new VConsole();</span></span><br><span class="line"><span class="undefined">    console.log('hello world');</span></span><br><span class="line"><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><p><a href="https://www.npmjs.com/package/vconsole" target="_blank" rel="noopener">https://www.npmjs.com/package/vconsole</a></p><h2 id="2-iOS上使用display-webkit-box-子元素之间有间距"><a href="#2-iOS上使用display-webkit-box-子元素之间有间距" class="headerlink" title="2.iOS上使用display: -webkit-box;子元素之间有间距"></a>2.iOS上使用<code>display: -webkit-box;</code>子元素之间有间距</h2><figure class="highlight css"><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="selector-tag">display</span>: <span class="selector-tag">-webkit-box</span>;</span><br><span class="line"><span class="selector-tag">font-size</span>: 0; <span class="comment">/* 消除间隙 */</span></span><br></pre></td></tr></table></figure><p>父元素设置<code>font-size: 0;</code> 子视图重新设置<code>font-size</code> </p><h2 id="3-div-包含的文字不展示"><a href="#3-div-包含的文字不展示" class="headerlink" title="3.div 包含的文字不展示"></a>3.div 包含的文字不展示</h2><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;div&gt;test&lt;/div&gt;</span><br></pre></td></tr></table></figure><p>需要设置 font-size</p><h2 id="4-iOS-圆角不生效"><a href="#4-iOS-圆角不生效" class="headerlink" title="4.iOS 圆角不生效"></a>4.iOS 圆角不生效</h2><figure class="highlight css"><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="selector-tag">overflow</span>: <span class="selector-tag">hidden</span>;</span><br><span class="line"><span class="selector-tag">border-radius</span>: 12<span class="selector-tag">px</span>;</span><br><span class="line">// 兼容iOS圆角不生效</span><br><span class="line"><span class="selector-tag">transform</span>: <span class="selector-tag">rotate</span>(0<span class="selector-tag">deg</span>);</span><br><span class="line"><span class="selector-tag">-webkit-transform</span>: <span class="selector-tag">rotate</span>(0<span class="selector-tag">deg</span>);</span><br></pre></td></tr></table></figure><h2 id="5-小程序引入三方库，旧版编译器报错"><a href="#5-小程序引入三方库，旧版编译器报错" class="headerlink" title="5.小程序引入三方库，旧版编译器报错"></a>5.小程序引入三方库，旧版编译器报错</h2><p>更换引入方式，使用 import</p><figure class="highlight js"><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"><span class="comment">// 原</span></span><br><span class="line"><span class="keyword">const</span> SpyClient = <span class="built_in">require</span>(<span class="string">'spy-client/dist/spy-client-basic'</span>);</span><br><span class="line"><span class="comment">// 新</span></span><br><span class="line"><span class="keyword">import</span> SpyClient <span class="keyword">from</span> <span class="string">'spy-client/dist/spy-client-basic.esm.js'</span>;</span><br></pre></td></tr></table></figure><h1 id="6-去掉-iOS-自带的点击效果"><a href="#6-去掉-iOS-自带的点击效果" class="headerlink" title="6.去掉 iOS 自带的点击效果"></a>6.去掉 iOS 自带的点击效果</h1><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">-webkit-tap-highlight-color</span>: <span class="selector-tag">rgba</span>(0, 0, 0, 0);</span><br></pre></td></tr></table></figure><p><a href="https://blog.csdn.net/hua_ban_yu/article/details/84855662" target="_blank" rel="noopener">ios点击出现灰色背景_ios 点击元素出现 背景-CSDN博客</a></p><h2 id="7-Vant-组件缺少样式"><a href="#7-Vant-组件缺少样式" class="headerlink" title="7.Vant 组件缺少样式"></a>7.Vant 组件缺少样式</h2><p>vant 版本为2.x.x</p><p>按需手动导入样式文件，以 List 为例：</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">import 'vant/lib/list/style';</span><br></pre></td></tr></table></figure><p><a href="https://vant-ui.github.io/vant/v2/#/zh-CN/list" target="_blank" rel="noopener">https://vant-ui.github.io/vant/v2/#/zh-CN/list</a></p><h2 id="8-使用了-pxtorem，判断条件里的-px-也被转换成了-rem"><a href="#8-使用了-pxtorem，判断条件里的-px-也被转换成了-rem" class="headerlink" title="8.使用了 pxtorem，判断条件里的 px 也被转换成了 rem"></a>8.使用了 pxtorem，判断条件里的 px 也被转换成了 rem</h2><p>postcss-pxtorem</p><p>可以在不期望转换的px后紧跟<code>/* ignore */</code></p><figure class="highlight css"><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="keyword">media</span> screen and (min-width <span class="number">640px</span>/* ignore */) &#123;</span><br><span class="line">   <span class="selector-tag">width</span>: 90%;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="9-z-index-收敛"><a href="#9-z-index-收敛" class="headerlink" title="9.z-index 收敛"></a>9.z-index 收敛</h2><p>弹窗、提示等 z-index 收敛到一个文件里方便维护</p><h2 id="10-flex-子元素超出父容器"><a href="#10-flex-子元素超出父容器" class="headerlink" title="10.flex 子元素超出父容器"></a>10.flex 子元素超出父容器</h2><p>父视图不想设置宽度（写死宽度不太聪明的样子），可以设置 min-width: 0;或 width: 0;</p><p><a href="https://blog.csdn.net/weixin_46180366/article/details/130222932" target="_blank" rel="noopener">flex弹性布局 子元素内容超出盒子容器宽度问题_flex布局子元素超过页面-CSDN博客</a></p><h2 id="11-设置margin-top为负数，元素遮挡部分背景颜色不生效"><a href="#11-设置margin-top为负数，元素遮挡部分背景颜色不生效" class="headerlink" title="11.设置margin-top为负数，元素遮挡部分背景颜色不生效"></a>11.设置margin-top为负数，元素遮挡部分背景颜色不生效</h2><p>需要设置 position: relative</p><figure class="highlight css"><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="selector-tag">margin-top</span>: <span class="selector-tag">-40px</span>;</span><br><span class="line"><span class="selector-tag">position</span>: <span class="selector-tag">relative</span>;</span><br></pre></td></tr></table></figure><h2 id="12-使用小程序-API-注意最低版本"><a href="#12-使用小程序-API-注意最低版本" class="headerlink" title="12.使用小程序 API 注意最低版本"></a>12.使用小程序 API 注意最低版本</h2><p>使用小程序 API 时注意判断版本，例如隐藏页面左上角返回按钮 backButtonHide 最低版本 3.660.1</p><h2 id="13-小程序中调试H5"><a href="#13-小程序中调试H5" class="headerlink" title="13.小程序中调试H5"></a>13.小程序中调试H5</h2><p>控制台输入命令等<br><figure class="highlight plain"><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">location.reload()</span><br><span class="line">location.href</span><br><span class="line">window.pageData</span><br></pre></td></tr></table></figure></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;1-添加-vConsole&quot;&gt;&lt;a href=&quot;#1-添加-vConsole&quot; class=&quot;headerlink&quot; title=&quot;1.添加 vConsole&quot;&gt;&lt;/a&gt;1.添加 vConsole&lt;/h2&gt;&lt;figure class=&quot;highlight html
      
    
    </summary>
    
      <category term="前端" scheme="http://chanceli.com/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="http://chanceli.com/tags/%E5%89%8D%E7%AB%AF/"/>
    
  </entry>
  
  <entry>
    <title>【前端】nvm安装方法</title>
    <link href="http://chanceli.com/nvm%E5%AE%89%E8%A3%85%E6%96%B9%E6%B3%95/"/>
    <id>http://chanceli.com/nvm安装方法/</id>
    <published>2023-07-19T02:42:00.000Z</published>
    <updated>2024-04-01T06:21:51.960Z</updated>
    
    <content type="html"><![CDATA[<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>nvm可以管理node版本，使用homebrew安装nvm后，nvm -v查看版本提示</p><p>zsh: command not found: nvm</p><h1 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h1><h2 id="1-使用homebrew安装nvm"><a href="#1-使用homebrew安装nvm" class="headerlink" title="1.使用homebrew安装nvm"></a>1.使用homebrew安装nvm</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install nvm</span><br></pre></td></tr></table></figure><h2 id="2-如果-nvm不存在，创建文件夹"><a href="#2-如果-nvm不存在，创建文件夹" class="headerlink" title="2.如果~/.nvm不存在，创建文件夹"></a>2.如果~/.nvm不存在，创建文件夹</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkdir ~/.nvm</span><br></pre></td></tr></table></figure><h2 id="3-配置环境变量"><a href="#3-配置环境变量" class="headerlink" title="3.配置环境变量"></a>3.配置环境变量</h2><figure class="highlight bash"><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">vim ~/.zshrc</span><br><span class="line"><span class="built_in">export</span> NVM_DIR=<span class="string">"<span class="variable">$HOME</span>/.nvm"</span></span><br><span class="line">[ -s <span class="string">"/opt/homebrew/opt/nvm/nvm.sh"</span> ] &amp;&amp; \. <span class="string">"/opt/homebrew/opt/nvm/nvm.sh"</span><span class="comment"># This loads nvm</span></span><br><span class="line">[ -s <span class="string">"/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm"</span> ] &amp;&amp; \. <span class="string">"/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm"</span><span class="comment"># This loads nvm bash_completion</span></span><br></pre></td></tr></table></figure><p><strong>注意：通过homebrew安装的nvm，环境变量里配置的路径跟别的不太一样。</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> ~/.zshrc</span><br></pre></td></tr></table></figure><p>附：终端提示的操作方法</p><p><img src="/images/nvm/nvm.png" alt="img"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;背景&quot;&gt;&lt;a href=&quot;#背景&quot; class=&quot;headerlink&quot; title=&quot;背景&quot;&gt;&lt;/a&gt;背景&lt;/h2&gt;&lt;p&gt;nvm可以管理node版本，使用homebrew安装nvm后，nvm -v查看版本提示&lt;/p&gt;
&lt;p&gt;zsh: command not fo
      
    
    </summary>
    
      <category term="前端" scheme="http://chanceli.com/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="http://chanceli.com/tags/%E5%89%8D%E7%AB%AF/"/>
    
  </entry>
  
  <entry>
    <title>【前端】WOT参会分享-跨平台开发的最佳实践</title>
    <link href="http://chanceli.com/WOT%E5%8F%82%E4%BC%9A%E5%88%86%E4%BA%AB-%E8%B7%A8%E5%B9%B3%E5%8F%B0%E5%BC%80%E5%8F%91%E7%9A%84%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/"/>
    <id>http://chanceli.com/WOT参会分享-跨平台开发的最佳实践/</id>
    <published>2023-07-05T04:37:00.000Z</published>
    <updated>2024-04-01T06:52:05.532Z</updated>
    
    <content type="html"><![CDATA[<p>分享人：<code>uni-app</code> 产品负责人、<code>DCloud</code> CTO</p><h1 id="1-跨平台框架如何选型？"><a href="#1-跨平台框架如何选型？" class="headerlink" title="1.跨平台框架如何选型？"></a>1.跨平台框架如何选型？</h1><p><img src="/images/wot/image-1.png" alt="img"></p><p><img src="/images/wot/image-2.png" alt="img"></p><p>1.提升人效</p><p>2.抹平差异</p><p><img src="/images/wot/image-3.png" alt="img"></p><h2 id="跨端框架的技术方向"><a href="#跨端框架的技术方向" class="headerlink" title="跨端框架的技术方向"></a>跨端框架的技术方向</h2><h3 id="方向1：偏前端"><a href="#方向1：偏前端" class="headerlink" title="方向1：偏前端"></a>方向1：偏前端</h3><p>基于<code>WebView</code>的增强</p><p>1.<code>ionic</code></p><p>2.<code>CORDOVA Hybrid</code>框架</p><p>3.<code>SONIC</code> 客户端预加载<code>WebView</code>资源</p><p>4.小程序（<code>WebView</code>）</p><p>5.离线包方案<code>App</code>厂商对<code>Web</code>做的离线包方案</p><p>特点：</p><p>1.基于<code>WebView</code>渲染，补充了一些原生能力的增强</p><p>2.开发生态基于 <code>Web</code> 前端生态</p><p>3.想方设法增强<code>Web</code>的用户体验</p><h3 id="方向2：偏客户端"><a href="#方向2：偏客户端" class="headerlink" title="方向2：偏客户端"></a>方向2：偏客户端</h3><p>基于<code>DSL</code>的<code>Native</code>增强</p><p>(<code>DSL</code>:<code>Domain-specific language</code> 领域特定语言是专门针对特定应用程序领域的计算机语言)</p><p>1.<code>DinamicX</code> 手淘无障碍框架</p><p>2.<code>Tangram</code> 阿里七巧板</p><p>3.<code>Jasonette</code></p><p>4.<code>DreamBox</code></p><p>5.<code>MTFlexbox</code> 美团</p><p><code>MTFlexbox</code>首先定义一份跨平台统一的<code>DSL</code>布局描述文件，前端通过“所见即所得”的编辑器编辑产生布局，客户端下载布局文件后，根据布局中的描述绑定<code>JSON</code>数据，并最终完成视图的渲染。</p><p>特点：</p><p>1.开发生态基于<code>Native</code>，框架设计参考<code>WebView</code>的一些特性</p><p>2.自定义<code>DSL</code>来实现跨端与动态化</p><p>设计的像<code>React Native</code></p><h3 id="方向3：代码共享-Kotlin-Multiplatform"><a href="#方向3：代码共享-Kotlin-Multiplatform" class="headerlink" title="方向3：代码共享 Kotlin Multiplatform"></a>方向3：代码共享 <code>Kotlin Multiplatform</code></h3><p><code>KKM</code> <code>Kotlin Multiplatform Mobile</code> 代码共享</p><p><img src="/images/wot/image-4.png" alt="img"></p><p>跨端代码 <code>Shared module</code></p><p>壳工程 <code>Android/iOS App</code></p><h3 id="方向4：大终端"><a href="#方向4：大终端" class="headerlink" title="方向4：大终端"></a>方向4：大终端</h3><p>基于<code>GPL</code>的<code>Native</code>增强</p><p>(<code>GPL</code>: <code>General Public License</code>  通用公共许可证)</p><p>1.<code>Native Widget</code> 原生渲染组件</p><p><code>React Native</code> / <code>Hippy</code> 1.0 / <code>Weex</code> 1.0</p><p>2.自绘引擎 <code>Flutter</code></p><p>3.基于<code>Flutter</code>的自绘框架</p><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">webF`，`Kraken`, `Kun`, `Skyline</span><br></pre></td></tr></table></figure><p>4.基于系统图形库（<code>Skia</code>(Google出的图形处理引擎) / <code>OpenGL</code>(开放图形库) / <code>Vulkan</code> / <code>Metal</code>）的自绘框架</p><p><code>TDF</code>(Tencent Dynamic Framework), <code>Hippy</code> 3.0, <code>Weex</code> 2.0, <code>MagicBrash</code>, <code>Waft</code>(WebAssembly Framework for Things)</p><p><code>Waft</code>(WebAssembly Framework for Things)</p><p>AloT的尝试</p><p>1.Android App: 运存太低，性能受限</p><p>2.Waft</p><p><code>Waft</code>构成：</p><p>1.脚本引擎：<code>WebAssembly</code></p><p>2.渲染引擎：<code>Skia</code>封装</p><h2 id="跨端框架的技术要点"><a href="#跨端框架的技术要点" class="headerlink" title="跨端框架的技术要点"></a>跨端框架的技术要点</h2><h3 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h3><h4 id="1-动态化"><a href="#1-动态化" class="headerlink" title="1.动态化"></a>1.动态化</h4><ul><li>基于<code>WebView</code>的增强</li><li>基于<code>DSL</code>的<code>Native</code>增强</li><li>基于<code>GPL</code>的<code>Native</code>增强</li><li>插件化（<code>Android</code>）</li><li>利用<code>OC</code>运行时动态化特性（<code>iOS</code>）</li></ul><h4 id="2-loT"><a href="#2-loT" class="headerlink" title="2.loT"></a>2.loT</h4><h4 id="3-Desktop"><a href="#3-Desktop" class="headerlink" title="3.Desktop"></a>3.Desktop</h4><h4 id="4-车机"><a href="#4-车机" class="headerlink" title="4.车机"></a>4.车机</h4><p><img src="/images/wot/image-5.png" alt="img"></p><h3 id="技术要点"><a href="#技术要点" class="headerlink" title="技术要点"></a>技术要点</h3><h4 id="1-脚本引擎"><a href="#1-脚本引擎" class="headerlink" title="1.脚本引擎"></a>1.脚本引擎</h4><ul><li><code>JS</code>引擎：双引擎（<code>JSCore</code> + <code>V8</code>）、单引擎（<code>Hermes</code>）、单引擎（<code>QuickJS</code>）、单引擎（自研<code>JS</code>引擎）</li><li><code>DartVM</code>：利用<code>Flutter Engine</code></li><li><code>WARM</code>：需要设计<code>DSL</code>和实现渲染引擎</li></ul><h4 id="2-渲染引擎"><a href="#2-渲染引擎" class="headerlink" title="2.渲染引擎"></a>2.渲染引擎</h4><ul><li>基于 <code>Flutter Engine</code></li><li>基于系统图形库 <code>Skia</code> / <code>OpenGL</code> / <code>Vulkun</code> / <code>Metal</code></li></ul><h4 id="3-调试器"><a href="#3-调试器" class="headerlink" title="3. 调试器"></a>3. 调试器</h4><p><code>Debugger</code>：一种可以让 <code>JavaScript Runtime</code> 进行中断，并可以实时查看内部运行状态的应用</p><p>调试协议：</p><ul><li><code>CDP</code>: <code>Chrome DevTools Protocol</code></li><li><code>DAP</code>: <code>Debug Adapter Protocol</code></li><li>自建协议：微信小程序早期</li></ul><h4 id="4-工程化"><a href="#4-工程化" class="headerlink" title="4.工程化"></a>4.工程化</h4><ul><li>资源加载方案</li><li>降级处理</li><li>版本管理</li><li>研发模式</li></ul><p><img src="/images/wot/image-6.png" alt="img"></p><p><img src="/images/wot/image-7.png" alt="img"></p><h1 id="2-跨平台框架的共性问题"><a href="#2-跨平台框架的共性问题" class="headerlink" title="2.跨平台框架的共性问题"></a>2.跨平台框架的共性问题</h1><p><img src="/images/wot/image-8.png" alt="img"></p><p><img src="/images/wot/image-9.png" alt="img"></p><p><img src="/images/wot/image-10.png" alt="img"></p><h1 id="3-UTS-纯原生跨平台框架"><a href="#3-UTS-纯原生跨平台框架" class="headerlink" title="3.UTS: 纯原生跨平台框架"></a>3.UTS: 纯原生跨平台框架</h1><p>UTS官方文档：<a href="https://uniapp.dcloud.net.cn/tutorial/syntax-uts.html" target="_blank" rel="noopener">https://uniapp.dcloud.net.cn/tutorial/syntax-uts.html</a></p><p><img src="/images/wot/image-11.png" alt="img"></p><p><img src="/images/wot/image-12.png" alt="img"></p><h2 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h2><p><img src="/images/wot/image-13.png" alt="img"></p><h2 id="switch语句"><a href="#switch语句" class="headerlink" title="switch语句"></a>switch语句</h2><p><img src="/images/wot/image-14.png" alt="img"></p><h2 id="类、函数"><a href="#类、函数" class="headerlink" title="类、函数"></a>类、函数</h2><p><img src="/images/wot/image-15.png" alt="img"></p><p>框架设计就是追求取舍平衡的过程</p><p>uts是对ts的定制，有删有增有重定义</p><h2 id="运行时"><a href="#运行时" class="headerlink" title="运行时"></a>运行时</h2><p><img src="/images/wot/image-16.png" alt="img"></p><p><img src="/images/wot/image-17.png" alt="img"></p><p><img src="/images/wot/image-18.png" alt="img"></p><h1 id="4-云端一体，跨端之上的最佳实践"><a href="#4-云端一体，跨端之上的最佳实践" class="headerlink" title="4.云端一体，跨端之上的最佳实践"></a>4.云端一体，跨端之上的最佳实践</h1><p>改善前后端协同，赋能前端，加速业务</p><h2 id="协作模式"><a href="#协作模式" class="headerlink" title="协作模式"></a>协作模式</h2><p>协作模式改变，更少代码，更高效率</p><p><img src="/images/wot/image-19.png" alt="img"></p><p><img src="/images/wot/image-20.png" alt="img"></p><p><img src="/images/wot/image-21.png" alt="img"></p><p><img src="/images/wot/image-22.png" alt="img"></p><p><img src="/images/wot/image-23.png" alt="img"></p><h2 id="代码生成"><a href="#代码生成" class="headerlink" title="代码生成"></a>代码生成</h2><p>业务抽象schema，一键生成前后端代码</p><p><img src="/images/wot/image-24.png" alt="img"></p><h2 id="SSR"><a href="#SSR" class="headerlink" title="SSR"></a>SSR</h2><p>组件支持SSR，降低SSR门槛</p><h3 id="什么是SSR"><a href="#什么是SSR" class="headerlink" title="什么是SSR"></a>什么是SSR</h3><p>SSR：Server Side Render 是一种将服务端渲染和客户端渲染结合起来的技术，它可以在服务端生成HTML代码，并将其发送到浏览器端。</p><p>SPA：Single Page Application 单页面应用，一般也成为客户端渲染 CSR（Client Side Render）</p><p><img src="/images/wot/image-25.png" alt="img"></p><p><img src="/images/wot/image-26.png" alt="img"></p><p>SPA存在的问题：</p><ul><li>首屏加载满，加载大量的JavaScript和CSS文件</li><li>SEO不友好，搜索引擎无法直接抓取页面内容</li><li>内存占用高，会在内存中缓存大量数据</li></ul><p>SSR优点：</p><ul><li>性能好，首屏加载速度快</li><li>更好的SEO</li><li>更好的用户体验</li><li>更易于维护</li></ul><p><img src="/images/wot/image-27.png" alt="img"></p><p><img src="/images/wot/image-28.png" alt="img"></p><p><img src="/images/wot/image-29.png" alt="img"></p><p><img src="/images/wot/image-30.png" alt="img"></p><h2 id="轮子生态"><a href="#轮子生态" class="headerlink" title="轮子生态"></a>轮子生态</h2><p>云端一体轮子，聚焦业务</p><p><img src="/images/wot/image-31.png" alt="img"></p><p><img src="/images/wot/image-32.png" alt="img"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;分享人：&lt;code&gt;uni-app&lt;/code&gt; 产品负责人、&lt;code&gt;DCloud&lt;/code&gt; CTO&lt;/p&gt;
&lt;h1 id=&quot;1-跨平台框架如何选型？&quot;&gt;&lt;a href=&quot;#1-跨平台框架如何选型？&quot; class=&quot;headerlink&quot; title=&quot;1.跨平台框
      
    
    </summary>
    
      <category term="前端" scheme="http://chanceli.com/categories/%E5%89%8D%E7%AB%AF/"/>
    
    
      <category term="前端" scheme="http://chanceli.com/tags/%E5%89%8D%E7%AB%AF/"/>
    
  </entry>
  
  <entry>
    <title>【小程序】百度小程序web化遇到的一些问题</title>
    <link href="http://chanceli.com/%E5%B0%8F%E7%A8%8B%E5%BA%8Fweb%E5%8C%96%E9%81%87%E5%88%B0%E7%9A%84%E4%B8%80%E4%BA%9B%E9%97%AE%E9%A2%98/"/>
    <id>http://chanceli.com/小程序web化遇到的一些问题/</id>
    <published>2023-03-20T03:03:00.000Z</published>
    <updated>2024-04-02T13:08:37.629Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-开启直连"><a href="#1-开启直连" class="headerlink" title="1.开启直连"></a>1.开启直连</h2><p>发起请求增加参数</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">baiduCORS: true,</span><br></pre></td></tr></table></figure><p>原因：web化需要开启直连请求，目前只有线上域名开启了直连，测试环境也需要开启，否则可能会报跨域错误</p><h2 id="2-生成web化需要改成http"><a href="#2-生成web化需要改成http" class="headerlink" title="2.生成web化需要改成http"></a>2.生成web化需要改成http</h2><p>操作步骤：预览-Web态预览-在浏览器中预览，然后在浏览器中的链接把https改为http</p><p>原因：线下环境是http的，网址也需要是http，否则会报mix-content错误</p><h2 id="3-链接域名需要是-smartapps-baidu-com-而非-smartapps-cn"><a href="#3-链接域名需要是-smartapps-baidu-com-而非-smartapps-cn" class="headerlink" title="3.链接域名需要是*.smartapps.baidu.com  而非*.smartapps.cn"></a>3.链接域名需要是<code>*.smartapps.baidu.com</code>  而非<code>*.smartapps.cn</code></h2><p>示例：</p><p>正确：<a href="https://i0wpll.smartapps.baidu.com/sub-others/pages/course-agreement/course-agreement" target="_blank" rel="noopener">https://i0wpll.smartapps.baidu.com/sub-others/pages/course-agreement/course-agreement</a></p><p>错误：<a href="https://i0wpll.smartapps.cn/sub-others/pages/course-agreement/course-agreement" target="_blank" rel="noopener">https://i0wpll.smartapps.cn/sub-others/pages/course-agreement/course-agreement</a></p><p>原因：*.smartapps.cn   webmapp/api/v1/check_session 获取登录状态异常，会提示用户未登录</p><h2 id="4-链接里的-amp-swebfr-1需要去掉"><a href="#4-链接里的-amp-swebfr-1需要去掉" class="headerlink" title="4.链接里的&amp;_swebfr=1需要去掉"></a>4.链接里的&amp;_swebfr=1需要去掉</h2><p>链接里的&amp;_swebfr=1需要去掉或改成&amp;_swebfr=22，控制是否带</p><p>示例：</p><p>正确：<a href="https://i0wpll.smartapps.baidu.com/sub-others/pages/course-agreement/course-agreement" target="_blank" rel="noopener">https://i0wpll.smartapps.baidu.com/sub-others/pages/course-agreement/course-agreement</a></p><p>错误：<a href="https://i0wpll.smartapps.baidu.com/sub-others/pages/course-agreement/course-agreement?_swebfr=0" target="_blank" rel="noopener">https://i0wpll.smartapps.baidu.com/sub-others/pages/course-agreement/course-agreement?_swebfr=0</a></p><p>原因：_swebfr=1会带“在百度APP内打开”回流按钮，如果改为_swebfr=0，跳转链接会跳不过去，UI也可能异常</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;1-开启直连&quot;&gt;&lt;a href=&quot;#1-开启直连&quot; class=&quot;headerlink&quot; title=&quot;1.开启直连&quot;&gt;&lt;/a&gt;1.开启直连&lt;/h2&gt;&lt;p&gt;发起请求增加参数&lt;/p&gt;
&lt;figure class=&quot;highlight&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td 
      
    
    </summary>
    
      <category term="小程序" scheme="http://chanceli.com/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F/"/>
    
    
      <category term="小程序" scheme="http://chanceli.com/tags/%E5%B0%8F%E7%A8%8B%E5%BA%8F/"/>
    
  </entry>
  
  <entry>
    <title>【小程序】微信小程序开发遇到的问题</title>
    <link href="http://chanceli.com/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%BC%80%E5%8F%91%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98/"/>
    <id>http://chanceli.com/微信小程序开发遇到的问题/</id>
    <published>2023-03-08T04:42:00.000Z</published>
    <updated>2024-04-02T13:24:53.191Z</updated>
    
    <content type="html"><![CDATA[<h1 id="问题1-体验版怎么访问域名不支持https-带端口号的线下环境"><a href="#问题1-体验版怎么访问域名不支持https-带端口号的线下环境" class="headerlink" title="问题1 体验版怎么访问域名不支持https 带端口号的线下环境"></a>问题1 体验版怎么访问域名不支持https 带端口号的线下环境</h1><p>点击右上角三个点-点击“开发调试”，即可访问域名不支持https 带端口号的线下环境</p><p><img src="/images/miniprogram/image-1.png" alt="img"></p><h1 id="问题2-体验版怎么查看日志，清除缓存"><a href="#问题2-体验版怎么查看日志，清除缓存" class="headerlink" title="问题2 体验版怎么查看日志，清除缓存"></a>问题2 体验版怎么查看日志，清除缓存</h1><p>打开“开发调试”后，点击vConsole，可以查看log，清除storage等</p><p><img src="/images/miniprogram/image-2.png" alt="img"></p><h1 id="问题3-怎么找页面路径"><a href="#问题3-怎么找页面路径" class="headerlink" title="问题3 怎么找页面路径"></a>问题3 怎么找页面路径</h1><p><img src="/images/miniprogram/image-3.jpeg" alt="img"></p><p><img src="/images/miniprogram/image-4.png" alt="img"></p><p><img src="/images/miniprogram/image-5.png" alt="img"></p><h1 id="问题4-子视图的margin-top不生效，变成了父视图的margin-top"><a href="#问题4-子视图的margin-top不生效，变成了父视图的margin-top" class="headerlink" title="问题4 子视图的margin-top不生效，变成了父视图的margin-top"></a>问题4 子视图的margin-top不生效，变成了父视图的margin-top</h1><p>1.父视图需要设置overflow: hidden; 子视图margin-top才生效</p><p>2.text标签不生效，需要换成view</p><p>参考链接：<a href="https://developers.weixin.qq.com/community/develop/article/doc/0008860a8a4370fff76c5147656c13" target="_blank" rel="noopener">https://developers.weixin.qq.com/community/develop/article/doc/0008860a8a4370fff76c5147656c13</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;问题1-体验版怎么访问域名不支持https-带端口号的线下环境&quot;&gt;&lt;a href=&quot;#问题1-体验版怎么访问域名不支持https-带端口号的线下环境&quot; class=&quot;headerlink&quot; title=&quot;问题1 体验版怎么访问域名不支持https 带端口号的线下环
      
    
    </summary>
    
      <category term="小程序" scheme="http://chanceli.com/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F/"/>
    
    
      <category term="小程序" scheme="http://chanceli.com/tags/%E5%B0%8F%E7%A8%8B%E5%BA%8F/"/>
    
  </entry>
  
  <entry>
    <title>【小程序】小程序开发避坑指南</title>
    <link href="http://chanceli.com/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%BC%80%E5%8F%91%E9%81%BF%E5%9D%91%E6%8C%87%E5%8D%97/"/>
    <id>http://chanceli.com/小程序开发避坑指南/</id>
    <published>2023-02-17T04:43:00.000Z</published>
    <updated>2024-04-02T13:27:26.716Z</updated>
    
    <content type="html"><![CDATA[<h1 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h1><ul><li>include 导入的文件能否引用其他组件，生命周期等在哪儿</li><li>如何强制命中实验，命中不了实验是什么原因</li><li>扫预览码提示网络问题，请稍后再试</li><li>展现怎么打点？日志平台实时校验无数据</li><li>获取嵌套的组件元素rect获取不到</li><li>组件里设置的文本样式被父组件统一设置的文本样式覆盖</li><li>滚动到某一位置 滑动后不准确</li><li>如何实现计算属性</li><li>首次编译报错 Page is not found in path，刷新后不报错</li><li>一些规范</li></ul><h1 id="问题1：include-导入的文件能否引用其他组件，生命周期等在哪儿"><a href="#问题1：include-导入的文件能否引用其他组件，生命周期等在哪儿" class="headerlink" title="问题1：include 导入的文件能否引用其他组件，生命周期等在哪儿"></a>问题1：include 导入的文件能否引用其他组件，生命周期等在哪儿</h1><h1 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h1><p>可以引入Component，相关属性及方法在父组件上。</p><h1 id="问题2：如何强制命中实验，接口命中不了实验是什么原因"><a href="#问题2：如何强制命中实验，接口命中不了实验是什么原因" class="headerlink" title="问题2：如何强制命中实验，接口命中不了实验是什么原因"></a>问题2：如何强制命中实验，接口命中不了实验是什么原因</h1><p>可以在页面拼参数 eg：hitEid=11245</p><p>命中不了实验可能是获取实验的接口 cookie里的baiduid为空</p><h2 id="解决方案-1"><a href="#解决方案-1" class="headerlink" title="解决方案"></a>解决方案</h2><p>退出登录/重新登录，清缓存，重启IDE</p><h1 id="问题3：扫预览码提示网络问题，请稍后再试"><a href="#问题3：扫预览码提示网络问题，请稍后再试" class="headerlink" title="问题3：扫预览码提示网络问题，请稍后再试"></a>问题3：扫预览码提示网络问题，请稍后再试</h1><p><img src="/images/swan/image-1.png" alt="img"></p><h2 id="解决方案-2"><a href="#解决方案-2" class="headerlink" title="解决方案"></a>解决方案</h2><p>项目信息-本地配置-校验域名  取消勾选</p><h1 id="问题4：展现怎么打点？日志平台实时校验无数据"><a href="#问题4：展现怎么打点？日志平台实时校验无数据" class="headerlink" title="问题4：展现怎么打点？日志平台实时校验无数据"></a>问题4：展现怎么打点？日志平台实时校验无数据</h1><p>组件进入可视区域展现打点</p><figure class="highlight js"><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><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">val: &#123;</span><br><span class="line">    observerList: []</span><br><span class="line">&#125;,</span><br><span class="line"></span><br><span class="line">attached() &#123;</span><br><span class="line">    <span class="keyword">this</span>.startObserve();</span><br><span class="line">&#125;,</span><br><span class="line"></span><br><span class="line">detached() &#123;</span><br><span class="line">    <span class="keyword">this</span>.stopObserve();</span><br><span class="line">&#125;,</span><br><span class="line">methods: &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 开启曝光监听</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">     startObserve() &#123;</span><br><span class="line">        <span class="keyword">const</span> isIphoneX = util.getSystemInfoSync().isIphoneX;</span><br><span class="line">        <span class="keyword">const</span> observer = swan.createIntersectionObserver();</span><br><span class="line">        observer.relativeToViewport(&#123;</span><br><span class="line">            bottom: -(isIphoneX ? <span class="number">34</span> : <span class="number">0</span>) - <span class="number">58</span></span><br><span class="line">        &#125;).observe(<span class="string">`#question-tag-id`</span>, res =&gt; &#123;</span><br><span class="line">            <span class="keyword">if</span> (res.intersectionRatio &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                log.send(&#123;</span><br><span class="line">                    area: <span class="string">'emotion-label'</span>,</span><br><span class="line">                    action: <span class="string">'show'</span>,</span><br><span class="line">                    <span class="string">'log_version'</span>: <span class="string">'2.0'</span>,</span><br><span class="line">                    serverId: <span class="number">16268</span></span><br><span class="line">                &#125;);</span><br><span class="line">                observer.disconnect();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">this</span>.val.observerList.push(observer);</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 断开曝光监听</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    stopObserve() &#123;</span><br><span class="line">        <span class="keyword">this</span>.val.observerList.forEach(<span class="function"><span class="params">item</span> =&gt;</span> &#123;</span><br><span class="line">            item &amp;&amp; item.disconnect();</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">&#125;,</span><br></pre></td></tr></table></figure><h1 id="问题5：获取嵌套的组件元素rect获取不到"><a href="#问题5：获取嵌套的组件元素rect获取不到" class="headerlink" title="问题5：获取嵌套的组件元素rect获取不到"></a>问题5：获取嵌套的组件元素rect获取不到</h1><h2 id="解决方案-3"><a href="#解决方案-3" class="headerlink" title="解决方案"></a>解决方案</h2><p>加上<code>.in(this)</code></p><figure class="highlight js"><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">// 获取标签rect</span></span><br><span class="line">getRect() &#123;</span><br><span class="line">    <span class="keyword">let</span> that = <span class="keyword">this</span>;</span><br><span class="line">    swan.createSelectorQuery().in(<span class="keyword">this</span>)</span><br><span class="line">    .select(<span class="string">".tag-container"</span>)</span><br><span class="line">    .boundingClientRect(<span class="function"><span class="keyword">function</span>(<span class="params">rect</span>) </span>&#123;</span><br><span class="line">        that.triggerEvent(<span class="string">'tagRectChanged'</span>, rect);</span><br><span class="line">    &#125;)</span><br><span class="line">    .exec();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="问题6：组件里设置的文本样式被父组件统一设置的文本样式覆盖"><a href="#问题6：组件里设置的文本样式被父组件统一设置的文本样式覆盖" class="headerlink" title="问题6：组件里设置的文本样式被父组件统一设置的文本样式覆盖"></a>问题6：组件里设置的文本样式被父组件统一设置的文本样式覆盖</h1><h2 id="解决方案-4"><a href="#解决方案-4" class="headerlink" title="解决方案"></a>解决方案</h2><p>提高权重</p><figure class="highlight js"><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="comment">/* 提高权重 */</span></span><br><span class="line">#question-tag-id .tag-text &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="问题7：滚动到某一位置-滑动后落点不准确"><a href="#问题7：滚动到某一位置-滑动后落点不准确" class="headerlink" title="问题7：滚动到某一位置 滑动后落点不准确"></a>问题7：滚动到某一位置 滑动后落点不准确</h1><h2 id="解决方案-5"><a href="#解决方案-5" class="headerlink" title="解决方案"></a>解决方案</h2><p>需要监听<code>onPageScroll</code>方法，计算上已滑动的距离 <code>currentScrollTop</code></p><figure class="highlight js"><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">scrollToRelatedContent()&#123;</span><br><span class="line">    <span class="keyword">const</span> &#123;statusBarHeight, titleBarHeight = <span class="number">44</span>&#125; = util.getSystemInfoSync();</span><br><span class="line">    <span class="keyword">const</span> navHeight = statusBarHeight + titleBarHeight;</span><br><span class="line">    <span class="keyword">let</span> currentScrollTop = <span class="keyword">this</span>.data.currentScrollTop;</span><br><span class="line">    swan.createSelectorQuery()</span><br><span class="line">    .select(<span class="string">".swan-security-padding-bottom"</span>)</span><br><span class="line">    .boundingClientRect(<span class="function"><span class="keyword">function</span>(<span class="params">rect</span>) </span>&#123;</span><br><span class="line">        swan.pageScrollTo(&#123;</span><br><span class="line">            scrollTop: rect.top - navHeight + currentScrollTop,</span><br><span class="line">            duration: <span class="number">300</span>,</span><br><span class="line">            success: <span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">            &#125;,</span><br><span class="line">            fail: <span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;)</span><br><span class="line">    .exec();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="问题8：计算属性"><a href="#问题8：计算属性" class="headerlink" title="问题8：计算属性"></a>问题8：计算属性</h1><h2 id="解决方案-6"><a href="#解决方案-6" class="headerlink" title="解决方案"></a>解决方案</h2><p>给属性增加observer，发生改变时调用方法更新其他属性</p><h1 id="问题9：首次编译报错-Page-is-not-found-in-path，刷新后不报错"><a href="#问题9：首次编译报错-Page-is-not-found-in-path，刷新后不报错" class="headerlink" title="问题9：首次编译报错 Page is not found in path，刷新后不报错"></a>问题9：首次编译报错 Page is not found in path，刷新后不报错</h1><p>忽略就行</p><h1 id="问题10：一些规范"><a href="#问题10：一些规范" class="headerlink" title="问题10：一些规范"></a>问题10：一些规范</h1><p>色值使用小写</p><p>建议：color: #3b6fff;</p><p>不建议：color: #3B6FFF;</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;include 导入的文件能否引用其他组件，生命周期等在哪儿&lt;/li&gt;
&lt;li&gt;如何强制命中实验，命中不了实验是什么原因&lt;/li
      
    
    </summary>
    
      <category term="小程序" scheme="http://chanceli.com/categories/%E5%B0%8F%E7%A8%8B%E5%BA%8F/"/>
    
    
      <category term="小程序" scheme="http://chanceli.com/tags/%E5%B0%8F%E7%A8%8B%E5%BA%8F/"/>
    
  </entry>
  
  <entry>
    <title>【iOS 开发】3D Banner的实现</title>
    <link href="http://chanceli.com/3D%20Banner%E7%9A%84%E5%AE%9E%E7%8E%B0/"/>
    <id>http://chanceli.com/3D Banner的实现/</id>
    <published>2022-05-13T06:24:00.000Z</published>
    <updated>2024-04-02T13:26:41.982Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-效果"><a href="#1-效果" class="headerlink" title="1.效果"></a>1.效果</h1><p>裸眼3D</p><h1 id="2-CMMotionManager-概述"><a href="#2-CMMotionManager-概述" class="headerlink" title="2.CMMotionManager 概述"></a>2.CMMotionManager 概述</h1><p>用于启动和管理运动服务的对象。</p><figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="built_in">CMMotionManager</span> : <span class="built_in">NSObject</span></span><br></pre></td></tr></table></figure><p>使用<code>CMMotionManager</code>对象启动报告设备板载传感器检测到的运动的服务。使用此对象接收四种类型的运动数据：</p><ul><li><strong>加速度计数据</strong>，表示设备在三维空间的瞬时加速度。</li><li><strong>陀螺仪数据</strong>，表示围绕设备三个主轴的瞬时旋转。</li><li><strong>磁力计数据</strong>，指示设备相对于地球磁场的方向。</li><li><strong>设备运动数据</strong>，指示与运动相关的关键属性，例如设备的用户启动加速度、其姿态、旋转速率、相对于校准磁场的方向以及相对于重力的方向。该数据由 Core Motion 的传感器融合算法提供。</li></ul><p>处理后的设备运动数据给出了设备的姿态、旋转速率、校准磁场、重力方向以及用户赋予设备的加速度。</p><p>只为您的应用创建一个 CMMotionManager 对象。此类的多个实例会影响从加速度计和陀螺仪接收数据的速率。</p><p>您可以按指定的更新间隔接收实时传感器数据，也可以让传感器收集数据并将其存储以供以后检索。使用这两种方法，当您不再需要数据时调用适当的停止方法 (<code>stopAccelerometerUpdates()</code>, <code>stopGyroUpdates()</code>, <code>stopMagnetometerUpdates()</code>,和 <code>stopDeviceMotionUpdates()</code>) 。</p><h3 id="以指定的间隔处理运动更新"><a href="#以指定的间隔处理运动更新" class="headerlink" title="以指定的间隔处理运动更新"></a>以指定的间隔处理运动更新</h3><p>为了在特定的时间间隔接收运动数据，app 调用一个“start”方法，该方法采用一个操作队列（OperationQueue的实例）和一个特定类型的block handler 来处理这些更新。运动数据被传递到block handler 中。更新频率由“interval”属性的值决定。</p><ul><li><strong>加速度计。</strong>设置<code>accelerometerUpdateInterval</code>属性以指定更新间隔。调用<code>startAccelerometerUpdates(to:withHandler:)</code> 该方法，传入一个<code>CMAccelerometerHandler</code>类型的block。加速度计数据作为<code>CMAccelerometerData</code>对象传递到block中。</li><li><strong>陀螺仪。</strong>设置<code>gyroUpdateInterval</code>属性以指定更新间隔。调用<code>startGyroUpdates(to:withHandler:)</code> 该方法，传入一个<code>CMGyroHandler</code>类型的块。旋转速率数据作为<code>CMGyroData</code>对象传递到block中。</li><li><strong>磁力计。</strong>设置<code>magnetometerUpdateInterval</code>属性以指定更新间隔。调用 <code>startMagnetometerUpdates(to:withHandler:)</code> 该方法，传递一个<code>CMMagnetometerHandler</code>类型的block。磁场数据作为<code>CMMagnetometerData</code>对象传递到block中。</li><li><strong>设备运动。</strong>设置属性<code>deviceMotionUpdateInterval</code>以指定更新间隔。调用<code>startDeviceMotionUpdates(using:)</code> 或<code>startDeviceMotionUpdates(using:to:withHandler:)</code> 或  <code>startDeviceMotionUpdates(to:withHandler:)</code> 方法，传入一个<code>CMDeviceMotionHandler</code>类型的块。旋转速率数据作为<code>CMDeviceMotion</code>对象传递到block中。</li></ul><figure class="highlight objc"><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"><span class="built_in">CMMotionManager</span> *motionManager = [[<span class="built_in">CMMotionManager</span> alloc] init];</span><br><span class="line">[motionManager startDeviceMotionUpdatesToQueue:[<span class="built_in">NSOperationQueue</span> currentQueue] withHandler:^(<span class="built_in">CMDeviceMotion</span> * _Nullable motion, <span class="built_in">NSError</span> * _Nullable error) &#123;</span><br><span class="line">    </span><br><span class="line">&#125;];</span><br></pre></td></tr></table></figure><h3 id="运动数据的定期采样"><a href="#运动数据的定期采样" class="headerlink" title="运动数据的定期采样"></a>运动数据的定期采样</h3><p>为了通过周期性采样来处理运动数据，应用程序调用一个不带参数的“start”方法，并周期性地访问给定类型运动数据的属性所保存的运动数据。这种方法是游戏等应用程序的推荐方法。在一个block中处理加速度计数据会带来额外的开销，并且大多数游戏应用程序只对渲染帧时的最新运动数据样本感兴趣。</p><ul><li><strong>加速度计。</strong>调用<code>startAccelerometerUpdates()</code> 以开始更新并通过读取<code>accelerometerData</code> 属性定期访问<code>CMAccelerometerData</code> 对象。</li><li><strong>陀螺仪。</strong> 调用<code>startGyroUpdates()</code> 以开始更新并通过读取<code>gyroData</code> 属性定期访问<code>CMGyroData</code> 对象。</li><li><strong>磁力计。</strong>调用<code>startMagnetometerUpdates()</code> 以开始更新并通过读取<code>magnetometerData</code> 属性定期访问<code>CMMagnetometerData</code>对象。</li><li><strong>设备运动。</strong> 调用<code>startDeviceMotionUpdates(using:)</code>或<code>startDeviceMotionUpdates()</code>方法开始更新并通过读取<code>deviceMotion</code>属性定期访问<code>CMDeviceMotion</code>对象。该方法（iOS 5.0 中的新方法）允许您指定用于姿态估计的参考框架。</li></ul><figure class="highlight objc"><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"><span class="built_in">CMMotionManager</span> *motionManager = [[<span class="built_in">CMMotionManager</span> alloc] init];</span><br><span class="line">motionManager.deviceMotionUpdateInterval = <span class="number">1</span>/<span class="number">15.0</span>;</span><br><span class="line"><span class="keyword">if</span> (motionManager.deviceMotionAvailable) &#123;</span><br><span class="line">    [motionManager startDeviceMotionUpdates];</span><br><span class="line">    <span class="keyword">double</span> x = motionManager.deviceMotion.gravity.x;</span><br><span class="line">    <span class="keyword">double</span> y = motionManager.deviceMotion.gravity.y;</span><br><span class="line">    <span class="keyword">double</span> z = motionManager.deviceMotion.gravity.z;</span><br><span class="line">    <span class="built_in">NSLog</span>(<span class="string">@"x:%f, y:%f, z:%f"</span>, x, y, z);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="CMDeviceMotion"><a href="#CMDeviceMotion" class="headerlink" title="CMDeviceMotion"></a><code>CMDeviceMotion</code></h3><figure class="highlight java"><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="meta">@interface</span> CMDeviceMotion : CMLogItem</span><br><span class="line"><span class="comment">// attitude 用于标识空间位置的欧拉角（roll、yaw、pitch）和四元数（quaternion）</span></span><br><span class="line"><span class="meta">@property</span>(readonly, nonatomic) CMAttitude *attitude;</span><br><span class="line"><span class="comment">// rotationRate 标识设备旋转速率</span></span><br><span class="line"><span class="meta">@property</span>(readonly, nonatomic) CMRotationRate rotationRate;</span><br><span class="line"><span class="comment">// gravity 用于标识重力在设备各个方向的分量，具体值的变化遵循如下规律：重力方向始终指向地球，而在设备的三个方向上有不同分量，最大可达 1.0，最小是 0.0</span></span><br><span class="line"><span class="meta">@property</span>(readonly, nonatomic) CMAcceleration gravity;</span><br><span class="line"><span class="comment">// userAcceleration 用于标识设备各个方向上的加速度，可以标识当前设备正在当前方向上减速 or 加速</span></span><br><span class="line"><span class="meta">@property</span>(readonly, nonatomic) CMAcceleration userAcceleration;</span><br><span class="line"><span class="comment">// magneticField 用于标识设备周围的磁场范围和精度</span></span><br><span class="line"><span class="meta">@property</span>(readonly, nonatomic) <span class="function">CMCalibratedMagneticField magneticField COREMOTION_EXPORT <span class="title">API_AVAILABLE</span><span class="params">(ios(<span class="number">5.0</span>)</span>)</span>;</span><br><span class="line"><span class="comment">// heading 用于标识北极方向</span></span><br><span class="line"><span class="meta">@property</span>(readonly, nonatomic) <span class="function"><span class="keyword">double</span> heading COREMOTION_EXPORT <span class="title">API_AVAILABLE</span><span class="params">(ios(<span class="number">11.0</span>)</span>)</span>;</span><br><span class="line"><span class="comment">// 传感器位置</span></span><br><span class="line"><span class="meta">@property</span>(readonly, nonatomic) <span class="function">CMDeviceMotionSensorLocation sensorLocation COREMOTION_EXPORT <span class="title">API_AVAILABLE</span><span class="params">(ios(<span class="number">14.0</span>)</span>)</span>;</span><br><span class="line"><span class="meta">@end</span></span><br></pre></td></tr></table></figure><p><img src="/images/3dbanner/image-1.png" alt="img"></p><h3 id="硬件可用性和状态"><a href="#硬件可用性和状态" class="headerlink" title="硬件可用性和状态"></a>硬件可用性和状态</h3><p>如果某个硬件功能（例如陀螺仪）在设备上不可用，则调用与该功能相关的启动方法无效。您可以通过检查相应的属性来了解硬件功能是否可用或处于活动状态；例如，对于陀螺仪数据，您可以检查<code>isGyroAvailable</code>或<code>isGyroActive</code>属性的值。</p><h1 id="3-实现方式"><a href="#3-实现方式" class="headerlink" title="3.实现方式"></a>3.实现方式</h1><ul><li>图片分层</li><li>通过传感器获取偏移角度</li><li>计算偏移量，更新图片位置</li></ul><h1 id="4-核心代码"><a href="#4-核心代码" class="headerlink" title="4.核心代码"></a>4.核心代码</h1><h3 id="iOS-实现"><a href="#iOS-实现" class="headerlink" title="iOS 实现"></a>iOS 实现</h3><figure class="highlight java"><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><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br></pre></td><td class="code"><pre><span class="line">#import &lt;CoreMotion/CoreMotion.h&gt;</span><br><span class="line"><span class="meta">@property</span> (nonatomic, strong) CMMotionManager *motionManager;</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> CGFloat CRMotionDeviceMotionMinimumTreshold = <span class="number">0</span>.f;</span><br><span class="line"><span class="comment">// 采样频率</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> CGFloat CRMotionDeviceMotionUpdateInterval = <span class="number">0.02</span>;</span><br><span class="line"><span class="comment">// 往左偏移角度 45°</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> CGFloat CRMotionDeviceMotionXLFactor = <span class="number">1.0</span>/(M_PI_4/M_PI_2);</span><br><span class="line"><span class="comment">// 往右偏移角度 45°</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> CGFloat CRMotionDeviceMotionXRFactor = <span class="number">1.0</span>/(M_PI_4/M_PI_2);</span><br><span class="line"><span class="comment">// 往下偏移角度 60°</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> CGFloat CRMotionDeviceMotionYBFactor = <span class="number">1.0</span>/(M_PI/<span class="number">3</span>/M_PI_2);</span><br><span class="line"><span class="comment">// 往上偏移角度 45°</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> CGFloat CRMotionDeviceMotionYTFactor = <span class="number">1.0</span>/(M_PI_4/M_PI_2);</span><br><span class="line"><span class="comment">// 左、右偏移位移</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> CGFloat CRMotionDeviceMotionXLimit = <span class="number">20.0f</span>;</span><br><span class="line"><span class="comment">// 上、下偏移位移</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> CGFloat CRMotionDeviceMotionYLimit = <span class="number">12.0f</span>;</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)setData &#123;</span><br><span class="line">    <span class="keyword">if</span> (self.imgURLs.count &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        [self startMonitor];</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 停止更新</span></span><br><span class="line">        [self.motionManager stopDeviceMotionUpdates];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)startMonitor &#123;</span><br><span class="line">    <span class="keyword">if</span> (!_motionManager) &#123;</span><br><span class="line">        _motionManager = [[CMMotionManager alloc] init];</span><br><span class="line">        <span class="comment">// 采样频率</span></span><br><span class="line">        _motionManager.deviceMotionUpdateInterval = CRMotionDeviceMotionUpdateInterval;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 往左偏移角度 45°</span></span><br><span class="line">    CGFloat xl = CRMotionDeviceMotionXLFactor;</span><br><span class="line">    <span class="comment">// 往右偏移角度 45°</span></span><br><span class="line">    CGFloat xr = CRMotionDeviceMotionXRFactor;</span><br><span class="line">    <span class="comment">// 往上偏移角度 45°</span></span><br><span class="line">    CGFloat yt = CRMotionDeviceMotionYTFactor;</span><br><span class="line">    <span class="comment">// 往下偏移角度 60°</span></span><br><span class="line">    CGFloat yb = CRMotionDeviceMotionYBFactor;</span><br><span class="line">    CGFloat oy = (M_PI_2/<span class="number">3</span>)/M_PI_2*<span class="number">1.0</span>;</span><br><span class="line">    CGFloat ox = <span class="number">0</span>*<span class="number">1.0</span>;</span><br><span class="line">    <span class="comment">// deviceMotionAvailable:检测硬件是否正常，deviceMotionActive:检测当前 CMMotionManager 是否正在提供数据更新</span></span><br><span class="line">    <span class="keyword">if</span> (![_motionManager isDeviceMotionActive] &amp;&amp; [_motionManager isDeviceMotionAvailable]) &#123;</span><br><span class="line">        <span class="meta">@weakify</span>(self);</span><br><span class="line">        [_motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]</span><br><span class="line">                                                        withHandler:^(CMDeviceMotion * _Nullable motion,</span><br><span class="line">                                                                      NSError * _Nullable error) &#123;</span><br><span class="line">            <span class="meta">@strongify</span>(self);</span><br><span class="line">            <span class="keyword">double</span> gravityX = motion.gravity.x+ox;</span><br><span class="line">            <span class="keyword">double</span> gravityY = motion.gravity.y+oy;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 上、下偏移位移</span></span><br><span class="line">            CGFloat maximumYOffset = CRMotionDeviceMotionYLimit;</span><br><span class="line">            CGFloat minimumYOffset = -CRMotionDeviceMotionYLimit;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 左、右偏移位移</span></span><br><span class="line">            CGFloat maximumXOffset = CRMotionDeviceMotionXLimit;</span><br><span class="line">            CGFloat minimumXOffset = -CRMotionDeviceMotionXLimit;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (fabs(gravityX) &gt;= CRMotionDeviceMotionMinimumTreshold) &#123;</span><br><span class="line">                <span class="comment">// 计算偏移量</span></span><br><span class="line">                CGFloat fOffsetX = CRMotionDeviceMotionXLimit*gravityX*xr;</span><br><span class="line">                <span class="keyword">if</span> (gravityX&lt;=<span class="number">0</span>) &#123;</span><br><span class="line">                    fOffsetX = CRMotionDeviceMotionXLimit*gravityX*xl;</span><br><span class="line">                &#125;</span><br><span class="line">                CGFloat fOffsetY = -CRMotionDeviceMotionYLimit*gravityY*yt;</span><br><span class="line">                <span class="keyword">if</span> (gravityY&lt;=<span class="number">0</span>) &#123;</span><br><span class="line">                    fOffsetY = -CRMotionDeviceMotionYLimit*gravityY*yb;</span><br><span class="line">                &#125;</span><br><span class="line">                </span><br><span class="line">                <span class="keyword">if</span> (fOffsetX &gt; maximumXOffset) &#123;</span><br><span class="line">                    fOffsetX = maximumXOffset;</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (fOffsetX &lt; minimumXOffset) &#123;</span><br><span class="line">                    fOffsetX = minimumXOffset;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (fOffsetY &gt; maximumYOffset) &#123;</span><br><span class="line">                    fOffsetY = maximumYOffset;</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span> (fOffsetY &lt; minimumYOffset) &#123;</span><br><span class="line">                    fOffsetY = minimumYOffset;</span><br><span class="line">                &#125;</span><br><span class="line">                </span><br><span class="line">                CGFloat bOffsetX = -fOffsetX;</span><br><span class="line">                CGFloat bOffsetY = -fOffsetY;</span><br><span class="line">                </span><br><span class="line">                <span class="keyword">if</span> (self.bannerView.scrollView.transform.tx != fOffsetX || self.bannerView.scrollView.transform.ty != fOffsetY) &#123;</span><br><span class="line">                    self.bannerView.scrollView.transform = CGAffineTransformMakeTranslation(fOffsetX, fOffsetY);</span><br><span class="line">                    self.backImageView.transform = CGAffineTransformMakeTranslation(bOffsetX, bOffsetY);</span><br><span class="line">                    self.frontImageView.transform = CGAffineTransformMakeTranslation(bOffsetX,bOffsetY);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;];</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        self.bannerView.scrollView.transform = CGAffineTransformMakeTranslation(<span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">        self.backImageView.transform = CGAffineTransformMakeTranslation(<span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">        self.frontImageView.transform = CGAffineTransformMakeTranslation(<span class="number">0</span>,<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Android-实现"><a href="#Android-实现" class="headerlink" title="Android 实现"></a>Android 实现</h3><h4 id="1-注册对应的传感器"><a href="#1-注册对应的传感器" class="headerlink" title="1.注册对应的传感器"></a>1.注册对应的传感器</h4><figure class="highlight java"><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">mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);</span><br><span class="line"><span class="comment">// 重力传感器</span></span><br><span class="line">mAcceleSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);</span><br><span class="line"><span class="comment">// 地磁场传感器</span></span><br><span class="line">mMagneticSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);</span><br><span class="line"></span><br><span class="line">mSensorManager.registerListener(<span class="keyword">this</span>, mAcceleSensor, SensorManager.SENSOR_DELAY_GAME);</span><br><span class="line">mSensorManager.registerListener(<span class="keyword">this</span>, mMagneticSensor, SensorManager.SENSOR_DELAY_GAME);</span><br></pre></td></tr></table></figure><h4 id="2-通过重力传感器和地磁场传感器，获取设备的偏转角度"><a href="#2-通过重力传感器和地磁场传感器，获取设备的偏转角度" class="headerlink" title="2.通过重力传感器和地磁场传感器，获取设备的偏转角度"></a>2.通过重力传感器和地磁场传感器，获取设备的偏转角度</h4><figure class="highlight java"><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"><span class="keyword">if</span> (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) &#123;</span><br><span class="line">    mAcceleValues = event.values;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) &#123;</span><br><span class="line">    mMageneticValues = event.values;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">float</span>[] values = <span class="keyword">new</span> <span class="keyword">float</span>[<span class="number">3</span>];</span><br><span class="line"><span class="keyword">float</span>[] R = <span class="keyword">new</span> <span class="keyword">float</span>[<span class="number">9</span>];</span><br><span class="line">SensorManager.getRotationMatrix(R, <span class="keyword">null</span>, mAcceleValues, mMageneticValues);</span><br><span class="line">SensorManager.getOrientation(R, values);</span><br><span class="line"><span class="comment">// x轴的偏转角度</span></span><br><span class="line">values[<span class="number">1</span>] = (<span class="keyword">float</span>) Math.toDegrees(values[<span class="number">1</span>]);</span><br><span class="line"><span class="comment">// y轴的偏转角度</span></span><br><span class="line">values[<span class="number">2</span>] = (<span class="keyword">float</span>) Math.toDegrees(values[<span class="number">2</span>]);</span><br></pre></td></tr></table></figure><h4 id="3-根据偏转角度执行滑动"><a href="#3-根据偏转角度执行滑动" class="headerlink" title="3.根据偏转角度执行滑动"></a>3.根据偏转角度执行滑动</h4><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (mDegreeY &lt;= <span class="number">0</span> &amp;&amp; mDegreeY &gt; mDegreeYMin) &#123;</span><br><span class="line">    hasChangeX = <span class="keyword">true</span>;</span><br><span class="line">    scrollX = (<span class="keyword">int</span>) (mDegreeY / Math.abs(mDegreeYMin) * mXMoveDistance*mDirection);</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (mDegreeY &gt; <span class="number">0</span> &amp;&amp; mDegreeY &lt; mDegreeYMax) &#123;</span><br><span class="line">    hasChangeX = <span class="keyword">true</span>;</span><br><span class="line">    scrollX = (<span class="keyword">int</span>) (mDegreeY / Math.abs(mDegreeYMax) * mXMoveDistance*mDirection);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// mDegreeX:偏转角度 mDegreeXMin和mDegreeXMax为X轴可发生偏转位移的角度最大值和最小值</span></span><br><span class="line"><span class="keyword">if</span> (mDegreeX &lt;= <span class="number">0</span> &amp;&amp; mDegreeX &gt; mDegreeXMin) &#123;</span><br><span class="line">    hasChangeY = <span class="keyword">true</span>;</span><br><span class="line">    <span class="comment">// mYMoveDistance: Y轴上的最大偏移距离</span></span><br><span class="line">    scrollY = (<span class="keyword">int</span>) (mDegreeX / Math.abs(mDegreeXMin) * mYMoveDistance*mDirection);</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (mDegreeX &gt; <span class="number">0</span> &amp;&amp; mDegreeX &lt; mDegreeXMax) &#123;</span><br><span class="line">    hasChangeY = <span class="keyword">true</span>;</span><br><span class="line">    scrollY = (<span class="keyword">int</span>) (mDegreeX / Math.abs(mDegreeXMax) * mYMoveDistance*mDirection);</span><br><span class="line">&#125;</span><br><span class="line">smoothScrollTo(hasChangeX ? scrollX : mScroller.getFinalX(), hasChangeY ? scrollY : mScroller.getFinalY());</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;1-效果&quot;&gt;&lt;a href=&quot;#1-效果&quot; class=&quot;headerlink&quot; title=&quot;1.效果&quot;&gt;&lt;/a&gt;1.效果&lt;/h1&gt;&lt;p&gt;裸眼3D&lt;/p&gt;
&lt;h1 id=&quot;2-CMMotionManager-概述&quot;&gt;&lt;a href=&quot;#2-CMMotionMan
      
    
    </summary>
    
      <category term="iOS开发" scheme="http://chanceli.com/categories/iOS%E5%BC%80%E5%8F%91/"/>
    
    
      <category term="iOS开发" scheme="http://chanceli.com/tags/iOS%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>【Flutter 开发】Flutter笔记之环境配置</title>
    <link href="http://chanceli.com/Flutter%E7%AC%94%E8%AE%B0%E4%B9%8B%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/"/>
    <id>http://chanceli.com/Flutter笔记之环境配置/</id>
    <published>2020-04-06T15:34:00.000Z</published>
    <updated>2024-04-02T13:19:45.892Z</updated>
    
    <content type="html"><![CDATA[<p>Flutter笔记之环境配置</p><p>Android Studio 下载地址：<a href="https://developer.android.google.cn/studio" target="_blank" rel="noopener">https://developer.android.google.cn/studio</a><br>Flutter SDK 下载地址：<a href="https://flutter.dev/docs/get-started/install/macos" target="_blank" rel="noopener">https://flutter.dev/docs/get-started/install/macos</a></p><h1 id="配置Flutter-SDK"><a href="#配置Flutter-SDK" class="headerlink" title="配置Flutter SDK"></a>配置Flutter SDK</h1><p>添加用户环境变量<br>打开终端，输入<code>vim ~/.bash_profile</code>,回车，编辑/.bash_profile文件。<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">vim ~/.bash_profile</span><br></pre></td></tr></table></figure></p><p>点击i进入编辑模式。<br>将如下环境变量加入到用户环境变量中：</p><figure class="highlight plain"><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">export PATH=`pwd`/flutter/bin:$PATH</span><br><span class="line">export PUB_HOSTED_URL=https://pub.flutter-io.cn</span><br><span class="line">export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn</span><br></pre></td></tr></table></figure><p>编辑完成，点击“Esc键，退出insert模式”, 然后输入“:wq”,回车，保存成功。<br>输入<code>source ~/.bash_profile</code>，让环境变量生效。</p><p>输入<code>echo $PATH</code>,查看环境变量，发现添加成功。 </p><p>输入<code>flutter-h</code>查看是否配置成功。</p><p>运行<code>flutter doctor</code>检测环境。</p><h2 id="Android-Studio-3-6-1-Android-sdkmanager-tool-not-found-的问题"><a href="#Android-Studio-3-6-1-Android-sdkmanager-tool-not-found-的问题" class="headerlink" title="Android Studio 3.6.1 Android sdkmanager tool not found 的问题"></a>Android Studio 3.6.1 Android sdkmanager tool not found 的问题</h2><h3 id="问题描述："><a href="#问题描述：" class="headerlink" title="问题描述："></a>问题描述：</h3><p>执行<code>flutter doctor</code>后提示 Android license status unknown.<br>执行 <code>flutter doctor --android-licenses</code>后提示<br>Android sdkmanager tool not found<br><img src="/images/flutter/1.jpg" alt="图1 Android sdkmanager tool not found"></p><h3 id="问题原因："><a href="#问题原因：" class="headerlink" title="问题原因："></a>问题原因：</h3><p>Android Studio 3.6.1版本没有tools文件夹。sdkmanager是在Android/sdk/cmdline-tools/latest/bin/sdkmanager路径下。<br><img src="/images/flutter/2.jpg" alt="图2 新版本没有tools文件夹"></p><p><img src="/images/flutter/3.jpg" alt="图3 新版本sdkmanager的路径"></p><h3 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h3><p>找到偏好设置里Android SDK下的隐藏废弃包取消掉，下载废弃的Android SDK Tools（Obsolete）。<br>Preferences-Appearance &amp; Behavior-System Settings-Android SDK-SDK Tools<br>把 Hide Obsolete Packages 前边的对勾取消掉。这时候会显示出隐藏的废弃包Android SDK Tools（Obsolete）。勾选上后，点击Apply下载Android SDK Tools。下载完成后问题得以解决。<br><img src="/images/flutter/4.jpg" alt="图4 下载 Android SDK Tools（Obsolete）"></p><p>参考链接：<a href="https://flutterchina.club/setup-macos/" target="_blank" rel="noopener">入门: 在macOS上搭建Flutter开发环境 - Flutter中文网</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Flutter笔记之环境配置&lt;/p&gt;
&lt;p&gt;Android Studio 下载地址：&lt;a href=&quot;https://developer.android.google.cn/studio&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dev
      
    
    </summary>
    
      <category term="Flutter" scheme="http://chanceli.com/categories/Flutter/"/>
    
    
      <category term="Flutter" scheme="http://chanceli.com/tags/Flutter/"/>
    
  </entry>
  
  <entry>
    <title>【Flutter 开发】Dart笔记</title>
    <link href="http://chanceli.com/Dart%E7%AC%94%E8%AE%B0/"/>
    <id>http://chanceli.com/Dart笔记/</id>
    <published>2020-03-02T10:06:00.000Z</published>
    <updated>2021-03-14T13:46:19.000Z</updated>
    
    <content type="html"><![CDATA[<p>Flutter之Dart笔记</p><h1 id="1-入口方法、变量、常量"><a href="#1-入口方法、变量、常量" class="headerlink" title="1. 入口方法、变量、常量"></a>1. 入口方法、变量、常量</h1><h2 id="1-1-入口方法main方法"><a href="#1-1-入口方法main方法" class="headerlink" title="1.1 入口方法main方法"></a>1.1 入口方法main方法</h2><figure class="highlight dart"><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="keyword">void</span> main() &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="1-2-常量变量"><a href="#1-2-常量变量" class="headerlink" title="1.2 常量变量"></a>1.2 常量变量</h2><ul><li>var 变量</li><li>const 常量</li><li>final 常量</li></ul><h3 id="const-与-final区别"><a href="#const-与-final区别" class="headerlink" title="const 与 final区别"></a>const 与 final区别</h3><p>final 可以开始不赋值，只能赋值一次；而final不仅有const的编译时常量的特性，最重要的它是运行时常量，并且final是惰性初始化，即在运行时第一次使用前才初始化。</p><h1 id="2-数据类型"><a href="#2-数据类型" class="headerlink" title="2. 数据类型"></a>2. 数据类型</h1><h2 id="2-1-字符串类型-String"><a href="#2-1-字符串类型-String" class="headerlink" title="2.1 字符串类型 String"></a>2.1 字符串类型 String</h2><p>字符串定义可以用单引号，“” 也可以用‘双引号’，成对出现。</p><p>String str1 = ‘this is str1’;</p><p>三个单引号（双引号）  可以定义多行字符串</p><p>字符串的拼接<br><figure class="highlight dart"><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="built_in">print</span>($str1 $str2);</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(str1 + str2);</span><br></pre></td></tr></table></figure></p><h2 id="2-2-数值类型-int-double"><a href="#2-2-数值类型-int-double" class="headerlink" title="2.2 数值类型 int double"></a>2.2 数值类型 int double</h2><p>int<br><figure class="highlight plain"><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">int a = 123</span><br><span class="line"></span><br><span class="line">double b = 23.5</span><br><span class="line"></span><br><span class="line">print(a);</span><br></pre></td></tr></table></figure></p><p>double既可以是整型 也可以是浮点型</p><h2 id="2-3-布尔类型-bool"><a href="#2-3-布尔类型-bool" class="headerlink" title="2.3 布尔类型 bool"></a>2.3 布尔类型 bool</h2><figure class="highlight dart"><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="built_in">bool</span> flag1 = <span class="keyword">true</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> l1 = [<span class="string">'aaa'</span>,<span class="string">'bbb'</span>,<span class="string">'ccc'</span>];</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(l1.length);</span><br></pre></td></tr></table></figure><h2 id="2-4-集合类型-List"><a href="#2-4-集合类型-List" class="headerlink" title="2.4 集合类型 List"></a>2.4 集合类型 List</h2><figure class="highlight dart"><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><br><span class="line"><span class="keyword">var</span> l2 = <span class="keyword">new</span> <span class="built_in">List</span>();</span><br><span class="line"></span><br><span class="line">l2.add(<span class="string">'张三'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(l2[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> l3 = <span class="keyword">new</span> <span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt;();</span><br><span class="line"></span><br><span class="line">l3.add(<span class="string">'张三'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(l3);</span><br></pre></td></tr></table></figure><h2 id="2-5-Map类型-Map"><a href="#2-5-Map类型-Map" class="headerlink" title="2.5 Map类型 Map"></a>2.5 Map类型 Map</h2><figure class="highlight dart"><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"><span class="keyword">var</span> person = &#123;</span><br><span class="line"></span><br><span class="line"><span class="string">"name"</span>:<span class="string">"张三"</span>,</span><br><span class="line"></span><br><span class="line"><span class="string">"age"</span>:<span class="number">20</span>,</span><br><span class="line"></span><br><span class="line"><span class="string">"work"</span>:[<span class="string">"程序员"</span>,<span class="string">"快递员"</span>]</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(person[<span class="string">"name"</span>]);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> p = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line"></span><br><span class="line">p[<span class="string">"name"</span>] = <span class="string">"李四"</span>;</span><br></pre></td></tr></table></figure><h2 id="2-6-类型判断"><a href="#2-6-类型判断" class="headerlink" title="2.6 类型判断"></a>2.6 类型判断</h2><p>is 关键字判断类型</p><h1 id="3-算数运算符"><a href="#3-算数运算符" class="headerlink" title="3. 算数运算符"></a>3. 算数运算符</h1><h2 id="3-1-算数运算符"><a href="#3-1-算数运算符" class="headerlink" title="3.1 算数运算符"></a>3.1 算数运算符</h2><figure class="highlight plain"><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><br><span class="line">~/ 取整</span><br></pre></td></tr></table></figure><h2 id="3-2-关系运算符"><a href="#3-2-关系运算符" class="headerlink" title="3.2 关系运算符"></a>3.2 关系运算符</h2><p>关系运算符 == != &gt; &lt; &gt;= &lt;=</p><h2 id="3-3-逻辑运算符"><a href="#3-3-逻辑运算符" class="headerlink" title="3.3 逻辑运算符"></a>3.3 逻辑运算符</h2><p>逻辑运算符 ! &amp;&amp; ||</p><h2 id="3-4-赋值运算符"><a href="#3-4-赋值运算符" class="headerlink" title="3.4 赋值运算符"></a>3.4 赋值运算符</h2><p>赋值运算符 == ??=  从右向左<br><figure class="highlight dart"><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="built_in">int</span> b;</span><br><span class="line"></span><br><span class="line">b ??= <span class="number">23</span>; <span class="comment">//如果b为空 把23赋值给b</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//+= -= *=  /=  ~/=</span></span><br><span class="line">a += <span class="number">3</span>;  a = a+<span class="number">3</span>;</span><br></pre></td></tr></table></figure></p><h2 id="3-5-条件表达式"><a href="#3-5-条件表达式" class="headerlink" title="3.5 条件表达式"></a>3.5 条件表达式</h2><p>条件表达式  if else switch case<br><figure class="highlight dart"><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">if</span>() &#123;</span><br><span class="line"></span><br><span class="line">&#125;<span class="keyword">else</span> &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>三目运算符  ? :</p><p>?? 运算符</p><h2 id="3-6-类型转换"><a href="#3-6-类型转换" class="headerlink" title="3.6 类型转换"></a>3.6 类型转换</h2><p>数值类型转换成String  toString()  </p><p>String转换成int  int.parse()</p><p>try catch</p><p>其他类型转换布尔类型</p><p>isEmpty：判断字符串是否为空</p><p>NaN not a number isNaN 是否是数值</p><p>自增自减 ++ – 在赋值运算中，如果++在前边，先运算后赋值。如果++在后边，先赋值后运算。</p><figure class="highlight dart"><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></pre></td><td class="code"><pre><span class="line">a++;  <span class="comment">//a=a+1;</span></span><br><span class="line"></span><br><span class="line">a--; <span class="comment">//a = a-1;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b = a++;</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(a); <span class="comment">//11</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(b):<span class="comment">//10</span></span><br></pre></td></tr></table></figure><h1 id="4-循环语句"><a href="#4-循环语句" class="headerlink" title="4. 循环语句"></a>4. 循环语句</h1><h2 id="4-1-for循环"><a href="#4-1-for循环" class="headerlink" title="4.1 for循环"></a>4.1 for循环</h2><figure class="highlight dart"><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="keyword">for</span>(<span class="built_in">int</span> i =<span class="number">1</span>;i&lt;=<span class="number">10</span>;i++) &#123;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">print</span>(i);</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//先执行print(i) 再执行i++</span></span><br></pre></td></tr></table></figure><h2 id="4-2-while-do…while"><a href="#4-2-while-do…while" class="headerlink" title="4.2 while do…while"></a>4.2 while do…while</h2><p>while(){</p><p>}</p><p>do{</p><p>}while()</p><h2 id="4-3-break-continue-用法区别"><a href="#4-3-break-continue-用法区别" class="headerlink" title="4.3 break continue 用法区别"></a>4.3 break continue 用法区别</h2><p>break 跳出当前循环，只能跳出一层循环</p><p>continue 跳过当次循环，循环还会继续执行</p><h1 id="5-List-Set-Map"><a href="#5-List-Set-Map" class="headerlink" title="5. List Set Map"></a>5. List Set Map</h1><h2 id="5-1-List"><a href="#5-1-List" class="headerlink" title="5.1 List"></a>5.1 List</h2><p>属性</p><ul><li>length</li><li>isEmpty</li><li>isNotEmpty</li><li>reversed  对列表倒序排序</li></ul><p>方法</p><ul><li><p>add(‘桃子’) 增加元素</p></li><li><p>addAll() 增加数组里的元素</p></li><li><p>indexOf(‘苹果’) 查找数据  查找不到返回-1 找到返回index</p></li><li><p>remove(‘西瓜’)</p></li><li><p>fillRange 修改</p></li><li><p>insert 插入</p></li><li><p>insertAll 插入多个</p></li><li><p>myList.join(‘’) //list转换成字符串</p></li><li><p>str.split(‘-‘) 字符串转换成list</p></li></ul><h2 id="5-2-Set"><a href="#5-2-Set" class="headerlink" title="5.2 Set"></a>5.2 Set</h2><p>Set 集合 去重</p><h2 id="5-3-Map"><a href="#5-3-Map" class="headerlink" title="5.3 Map"></a>5.3 Map</h2><p>常用属性</p><ul><li><p>keys</p></li><li><p>values</p></li><li><p>isEmpty</p></li><li><p>isNotEmpty</p></li></ul><p>常用方法</p><ul><li><p>addAll</p></li><li><p>remove</p></li><li><p>containsValue</p></li></ul><h2 id="5-4-forEach-map-where-any-every"><a href="#5-4-forEach-map-where-any-every" class="headerlink" title="5.4 forEach map where any every"></a>5.4 forEach map where any every</h2><figure class="highlight dart"><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">myList.forEach((value) &#123;</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"%value"</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>map</p><figure class="highlight dart"><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="built_in">List</span> myList = [<span class="number">1</span>,<span class="number">3</span>,<span class="number">4</span>];</span><br><span class="line"><span class="keyword">var</span> newList = myList.map((value)&#123;</span><br><span class="line">  <span class="keyword">return</span> value*<span class="number">2</span>;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">print</span>(newList.toList());<span class="comment">//[2,6,8]</span></span><br></pre></td></tr></table></figure><p>where</p><figure class="highlight dart"><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="built_in">List</span> myList = [<span class="number">1</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span>];</span><br><span class="line"><span class="keyword">var</span> newList = myList.where((value)&#123;</span><br><span class="line">  <span class="keyword">return</span> value&gt;<span class="number">5</span>;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">print</span>(newList.toList());<span class="comment">//[7,8,9]</span></span><br></pre></td></tr></table></figure><p>any </p><figure class="highlight dart"><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="built_in">List</span> myList = [<span class="number">1</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span>];</span><br><span class="line"><span class="keyword">var</span> f = myList.any((value)&#123; <span class="comment">//只要集合里面有满足条件的就返回true</span></span><br><span class="line">  <span class="keyword">return</span> value&gt;<span class="number">5</span>;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">print</span>(f);<span class="comment">//true</span></span><br></pre></td></tr></table></figure><p>every</p><figure class="highlight dart"><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="built_in">List</span> myList = [<span class="number">1</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span>];</span><br><span class="line"><span class="keyword">var</span> f = myList.every((value)&#123; <span class="comment">//每一个都满足条件返回true，否则返回false</span></span><br><span class="line">  <span class="keyword">return</span> value&gt;<span class="number">5</span>;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="built_in">print</span>(f);<span class="comment">//false</span></span><br></pre></td></tr></table></figure><h1 id="6-函数"><a href="#6-函数" class="headerlink" title="6. 函数"></a>6. 函数</h1><h2 id="方法定义"><a href="#方法定义" class="headerlink" title="方法定义"></a>方法定义</h2><p>返回类型 方法名称（参数1，参数2，…） {<br>方法体<br>return 返回值；<br>}</p><figure class="highlight dart"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> printInfo()&#123;</span><br><span class="line"><span class="built_in">print</span>(<span class="string">'我是一个自定义方法'</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">int</span> getNum() &#123;</span><br><span class="line"><span class="keyword">var</span> myNum = <span class="number">123</span>;</span><br><span class="line"><span class="keyword">return</span> myNum;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> main() &#123;</span><br><span class="line"><span class="built_in">print</span>(<span class="string">''</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> n = getNum();</span><br><span class="line"><span class="built_in">print</span>(n);</span><br><span class="line"></span><br><span class="line"><span class="built_in">List</span> getList() &#123;</span><br><span class="line">  <span class="keyword">return</span> [<span class="string">'111'</span>,<span class="string">'222'</span>,<span class="string">'333'</span>];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="可选参数，默认参数"><a href="#可选参数，默认参数" class="headerlink" title="可选参数，默认参数"></a>可选参数，默认参数</h2><figure class="highlight dart"><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="comment">//可选参数</span></span><br><span class="line"><span class="built_in">String</span> printUserInfo(<span class="built_in">String</span> username,[<span class="built_in">String</span> sex = <span class="string">'男'</span>,<span class="built_in">int</span> age]) &#123;<span class="comment">//[可选参数]</span></span><br><span class="line">  <span class="keyword">if</span> (age != <span class="keyword">null</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">"姓名：<span class="subst">$username</span>---年龄：<span class="subst">$age<span class="string">";</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">  &#125;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">  return "</span></span>姓名：<span class="subst">$username</span>---年龄保密"</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">print</span>(printUserInfo(<span class="string">"张三"</span>,<span class="number">20</span>));</span><br></pre></td></tr></table></figure><h2 id="命名参数"><a href="#命名参数" class="headerlink" title="命名参数"></a>命名参数</h2><figure class="highlight dart"><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="built_in">String</span> printUserInfo(<span class="built_in">String</span> username,&#123;<span class="built_in">String</span> sex = <span class="string">'男'</span>,<span class="built_in">int</span> age&#125;) &#123;</span><br><span class="line">  <span class="keyword">if</span> (age != <span class="keyword">null</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">"姓名：<span class="subst">$username</span>---年龄：<span class="subst">$age<span class="string">";</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">  &#125;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">  return "</span></span>姓名：<span class="subst">$username</span>---年龄保密"</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">print</span>(printUserInfo(<span class="string">"张三"</span>,age:<span class="number">20</span>));</span><br></pre></td></tr></table></figure><h2 id="方法当做参数"><a href="#方法当做参数" class="headerlink" title="方法当做参数"></a>方法当做参数</h2><figure class="highlight dart"><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="keyword">var</span> fn = ()&#123;</span><br><span class="line">  <span class="built_in">print</span>(<span class="string">'我是一个匿名方法'</span>);</span><br><span class="line">&#125;</span><br><span class="line">fn();</span><br><span class="line"></span><br><span class="line">fn1() &#123;</span><br><span class="line">  <span class="built_in">print</span>(<span class="string">'fn1'</span>);</span><br><span class="line">&#125;</span><br><span class="line">fn2(fn) &#123;</span><br><span class="line">  fn();</span><br><span class="line">&#125;</span><br><span class="line">fn2(fn1);</span><br></pre></td></tr></table></figure><h2 id="箭头函数-只能写一行"><a href="#箭头函数-只能写一行" class="headerlink" title="箭头函数(只能写一行)"></a>箭头函数(只能写一行)</h2><figure class="highlight dart"><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="built_in">List</span> list = [<span class="string">'苹果'</span>,<span class="string">'香蕉'</span>,<span class="string">'西瓜'</span>];</span><br><span class="line"><span class="comment">//常规写法</span></span><br><span class="line">list.forEach((value)&#123;</span><br><span class="line"><span class="built_in">print</span>(value);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//箭头函数</span></span><br><span class="line">list.<span class="keyword">for</span>((value)=&gt;<span class="built_in">print</span>(value));</span><br></pre></td></tr></table></figure><figure class="highlight dart"><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">List</span> list = [<span class="number">4</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>];</span><br><span class="line"><span class="comment">//常规写法</span></span><br><span class="line"><span class="keyword">var</span> newList = list.map((value)&#123;</span><br><span class="line">  <span class="keyword">if</span> (value &gt; <span class="number">2</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> value*<span class="number">2</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> value;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//箭头函数</span></span><br><span class="line"><span class="keyword">var</span> newList = list.map((value)=&gt;value&gt;<span class="number">2</span>?value*<span class="number">2</span>:value);</span><br><span class="line"><span class="built_in">print</span>(newList.toList());</span><br></pre></td></tr></table></figure><h2 id="匿名方法"><a href="#匿名方法" class="headerlink" title="匿名方法"></a>匿名方法</h2><figure class="highlight dart"><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"><span class="keyword">var</span> printNum = ()&#123;</span><br><span class="line">  <span class="built_in">print</span>(<span class="number">123</span>);</span><br><span class="line">&#125;;</span><br><span class="line">printNum();</span><br></pre></td></tr></table></figure><h2 id="自执行方法"><a href="#自执行方法" class="headerlink" title="自执行方法"></a>自执行方法</h2><figure class="highlight dart"><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">((<span class="built_in">int</span> n)&#123;</span><br><span class="line"><span class="built_in">print</span>(n);</span><br><span class="line"><span class="built_in">print</span>(<span class="string">'我是自执行方法'</span>);</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><h2 id="方法的递归"><a href="#方法的递归" class="headerlink" title="方法的递归"></a>方法的递归</h2><figure class="highlight dart"><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="keyword">var</span> sum = <span class="number">1</span>;</span><br><span class="line">fn(n) &#123;</span><br><span class="line">  sum *= n;</span><br><span class="line">  <span class="keyword">if</span> (n==<span class="number">1</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  fn(n<span class="number">-1</span>);</span><br><span class="line">&#125;</span><br><span class="line">fn(<span class="number">5</span>);</span><br><span class="line"><span class="built_in">print</span>(sum);</span><br></pre></td></tr></table></figure><h2 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h2><p>常驻内存，不污染全局</p><p>1.全局变量特点：全局变量常驻内存、全局变量污染全局</p><p>2.局部变量特点：不常住内存会被垃圾机制回收、不会污染全局</p><figure class="highlight dart"><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></pre></td><td class="code"><pre><span class="line">fn() &#123;</span><br><span class="line">  <span class="keyword">var</span> a = <span class="number">123</span>; <span class="comment">/*不会污染全局 常驻内存*/</span></span><br><span class="line">  <span class="keyword">return</span>() &#123;</span><br><span class="line">    a++;</span><br><span class="line">    <span class="built_in">print</span>(a);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> b = fn();</span><br><span class="line">b(); <span class="comment">//124</span></span><br><span class="line">b(); <span class="comment">//125</span></span><br><span class="line">b(); <span class="comment">//126</span></span><br></pre></td></tr></table></figure><h1 id="7-类"><a href="#7-类" class="headerlink" title="7. 类"></a>7. 类</h1><h2 id="默认构造函数-命名构造函数"><a href="#默认构造函数-命名构造函数" class="headerlink" title="默认构造函数 命名构造函数"></a>默认构造函数 命名构造函数</h2><p>默认构造函数只能定义一个<br>命名构造函数可以定义多个</p><figure class="highlight dart"><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><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span></span>&#123;</span><br><span class="line"><span class="built_in">String</span> name;</span><br><span class="line"><span class="built_in">int</span> age;</span><br><span class="line">  <span class="comment">//默认构造函数</span></span><br><span class="line">  Person(<span class="built_in">String</span> name,<span class="built_in">int</span> age) &#123;</span><br><span class="line">    <span class="keyword">this</span>.name = name;</span><br><span class="line">    <span class="keyword">this</span>.age = age;</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">'这是构造函数里面的内容，这个方法在实例化的时候触发'</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">//默认构造函数简写 Person(this.name,this.age);</span></span><br><span class="line">  Person.now()&#123;</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">'我是命名构造函数'</span>);</span><br><span class="line">  &#125;</span><br><span class="line"><span class="keyword">void</span> getInfo() &#123;</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"<span class="subst">$name</span>---<span class="subst">$age<span class="string">");</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="string">print("</span></span><span class="subst">$&#123;<span class="keyword">this</span>.name&#125;</span>---<span class="subst">$&#123;<span class="keyword">this</span>.age&#125;</span>"</span>);</span><br><span class="line">&#125;</span><br><span class="line">  <span class="keyword">void</span> setInfo(<span class="built_in">int</span> age) &#123;</span><br><span class="line">    <span class="keyword">this</span>.age = age;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">void</span> main() &#123;</span><br><span class="line">  <span class="keyword">var</span> p1 = <span class="keyword">new</span> person(<span class="string">'张三'</span>,<span class="number">20</span>);</span><br><span class="line">  <span class="built_in">print</span>(p1.name);</span><br><span class="line">  p1.setInfo(<span class="number">28</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="私有属性、私有方法"><a href="#私有属性、私有方法" class="headerlink" title="私有属性、私有方法"></a>私有属性、私有方法</h2><p>使用<code>_</code>把一个属性或者方法定义成私有。需要把类抽离成一个文件</p><h2 id="getter-setter"><a href="#getter-setter" class="headerlink" title="getter setter"></a>getter setter</h2><figure class="highlight dart"><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="class"><span class="keyword">class</span> <span class="title">Rect</span> </span>&#123;</span><br><span class="line">  <span class="built_in">num</span> height;</span><br><span class="line">  <span class="built_in">num</span> width;</span><br><span class="line">  Rect(<span class="keyword">this</span>.height,<span class="keyword">this</span>.width);</span><br><span class="line">  <span class="keyword">get</span> area&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.height*<span class="keyword">this</span>.width;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">set</span> areaHeight(value) &#123;</span><br><span class="line">    <span class="keyword">this</span>.height = value;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">void</span> main()&#123;</span><br><span class="line">  Rect r = <span class="keyword">new</span> Rect(<span class="number">10</span>,<span class="number">4</span>);</span><br><span class="line">  r.areaHeight = <span class="number">6</span>;</span><br><span class="line">  <span class="built_in">print</span>(<span class="string">"面积:<span class="subst">$&#123;r.area&#125;</span>"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="类的初始化列表"><a href="#类的初始化列表" class="headerlink" title="类的初始化列表"></a>类的初始化列表</h2><figure class="highlight dart"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Rect</span> </span>&#123;</span><br><span class="line">  <span class="built_in">int</span> height;</span><br><span class="line">  <span class="built_in">int</span> width;</span><br><span class="line">  Rect():height=<span class="number">2</span>,width=<span class="number">10</span> &#123;</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">"<span class="subst">$&#123;<span class="keyword">this</span>.height&#125;</span>---<span class="subst">$&#123;<span class="keyword">this</span>.width&#125;</span>"</span>);</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">get</span> area&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.height*<span class="keyword">this</span>.width;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">set</span> areaHeight(value) &#123;</span><br><span class="line">    <span class="keyword">this</span>.height = value;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">void</span> main()&#123;</span><br><span class="line">  Rect r = <span class="keyword">new</span> Rect();</span><br><span class="line">  <span class="built_in">print</span>(<span class="string">"面积:<span class="subst">$&#123;r.area&#125;</span>"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="类中的静态成员"><a href="#类中的静态成员" class="headerlink" title="类中的静态成员"></a>类中的静态成员</h2><p>1.使用<code>static</code>关键字来实现类级别的变量和函数</p><p>2.静态方法不能访问非静态成员，非静态方法可以访问静态成员</p><figure class="highlight dart"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">  <span class="keyword">static</span> <span class="built_in">String</span> name = <span class="string">'张三'</span>;</span><br><span class="line">  <span class="built_in">int</span> age = <span class="number">20</span>;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">void</span> show() &#123;</span><br><span class="line">    <span class="built_in">print</span>(name);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">void</span> printInfo() &#123; <span class="comment">/*非静态方法可以访问静态成员以及非静态成员*/</span></span><br><span class="line">    <span class="built_in">print</span>(name); <span class="comment">//访问静态属性</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="keyword">this</span>.age); <span class="comment">//访问非静态属性</span></span><br><span class="line">    show();<span class="comment">//调用静态方法</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">void</span> printUserInfo() &#123;<span class="comment">//静态方法</span></span><br><span class="line">    <span class="built_in">print</span>(name);<span class="comment">//静态属性</span></span><br><span class="line">    show();<span class="comment">//静态方法</span></span><br><span class="line">    </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">main() &#123;</span><br><span class="line">  <span class="built_in">print</span>(Person.name);</span><br><span class="line">  Person.show();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="对象操作符"><a href="#对象操作符" class="headerlink" title="对象操作符"></a>对象操作符</h2><p>? 条件运算符</p><p>as 类型转换</p><p>is 类型判断</p><p>.. 级联操作（连缀）</p><h2 id="条件运算符"><a href="#条件运算符" class="headerlink" title="? 条件运算符"></a>? 条件运算符</h2><figure class="highlight dart"><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="class"><span class="keyword">class</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">  <span class="built_in">String</span> name;</span><br><span class="line">  <span class="built_in">num</span> age;</span><br><span class="line">  Person(<span class="keyword">this</span>.name,<span class="keyword">this</span>.age);</span><br><span class="line">  <span class="keyword">void</span> printInfo() &#123;</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">"<span class="subst">$&#123;<span class="keyword">this</span>.name&#125;</span>---<span class="subst">$&#123;<span class="keyword">this</span>.age&#125;</span>"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">main() &#123;</span><br><span class="line">  Person p;</span><br><span class="line">  p?.printInfo();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="as-类型转换"><a href="#as-类型转换" class="headerlink" title="as 类型转换"></a>as 类型转换</h3><figure class="highlight dart"><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"><span class="keyword">var</span> p1;</span><br><span class="line">p1 = <span class="string">''</span>;</span><br><span class="line">p1 = <span class="keyword">new</span> Person(<span class="string">'张三'</span>,<span class="number">20</span>);</span><br><span class="line">(p1 <span class="keyword">as</span> Person).printInfo();</span><br></pre></td></tr></table></figure><h3 id="级联操作"><a href="#级联操作" class="headerlink" title=".. 级联操作"></a>.. 级联操作</h3><figure class="highlight dart"><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">Person p1 = <span class="keyword">new</span> pERSON(<span class="string">'张三'</span>,<span class="number">20</span>);</span><br><span class="line">p1.printInfo();</span><br><span class="line">p1..name = <span class="string">"李四"</span></span><br><span class="line">  ..age = <span class="number">30</span></span><br><span class="line">  ..printInfo();</span><br></pre></td></tr></table></figure><h3 id="继承-extends"><a href="#继承-extends" class="headerlink" title="继承 extends"></a>继承 extends</h3><figure class="highlight dart"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Web</span> <span class="keyword">extends</span> <span class="title">Person</span> </span>&#123;</span><br><span class="line">  <span class="built_in">String</span> sex;</span><br><span class="line">Web(<span class="built_in">String</span> name,<span class="built_in">num</span> age,<span class="built_in">String</span> sex) : <span class="keyword">super</span>(name, age) &#123;</span><br><span class="line">    <span class="keyword">this</span>.sex = sex;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">//覆写父类的方法</span></span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="keyword">void</span> printInfo() &#123;</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">"姓名:<span class="subst">$&#123;<span class="keyword">this</span>.name&#125;</span>---年龄:<span class="subst">$&#123;<span class="keyword">this</span>.age&#125;</span>"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="抽象类"><a href="#抽象类" class="headerlink" title="抽象类"></a>抽象类</h2><p>抽象类：主要用于定义标准，子类可以继承抽象类，也可以实现抽象类接口。</p><p>抽象方法  没有方法体的方法</p><ul><li>子类必须实现</li><li>抽象类不能被实例化</li></ul><figure class="highlight dart"><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">//抽象类</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Animal</span> </span>&#123;</span><br><span class="line">  eat(); <span class="comment">//抽象方法</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Dog</span> <span class="keyword">extends</span> <span class="title">Animal</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  eat() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="多态"><a href="#多态" class="headerlink" title="多态"></a>多态</h2><p>允许将子类类型的指针赋值给父类类型的指针，同一个函数调用会有不同的执行结果。</p><p>子类的实例赋值给父类的引用。</p><p>多态就是父类定义一个方法不去实现，让继承他的子类去实现，每个子类有不同的表现。</p><figure class="highlight dart"><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">Animal d = <span class="keyword">new</span> Dog();</span><br><span class="line">d.eat();</span><br><span class="line">Animal c = <span class="keyword">new</span> Cat();</span><br><span class="line">c.eat();</span><br></pre></td></tr></table></figure><h2 id="接口"><a href="#接口" class="headerlink" title="接口"></a>接口</h2><p>使用抽象类定义接口</p><figure class="highlight dart"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Db</span> </span>&#123; <span class="comment">//当做接口 接口：就是约定、规范</span></span><br><span class="line">  <span class="built_in">String</span> uri;</span><br><span class="line">  add();</span><br><span class="line">  save();</span><br><span class="line">  delete();</span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Mysql</span> <span class="keyword">implements</span> <span class="title">Db</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">String</span> uri;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  add() &#123;</span><br><span class="line">    </span><br><span class="line">  &#125;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  delete() &#123;</span><br><span class="line">    </span><br><span class="line">  &#125;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  save() &#123;</span><br><span class="line">    </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="extends抽象类和implements的区别："><a href="#extends抽象类和implements的区别：" class="headerlink" title="extends抽象类和implements的区别："></a>extends抽象类和implements的区别：</h3><p>1.如果要复用抽象类里面的方法，并且要用抽象方法约束子类的话就用extends继承抽象类。</p><p>2.如果只是把抽象类当做标准的话就用implements实现抽象类。</p><h2 id="一个类实现多个接口"><a href="#一个类实现多个接口" class="headerlink" title="一个类实现多个接口"></a>一个类实现多个接口</h2><figure class="highlight dart"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">A</span> </span>&#123;</span><br><span class="line">  <span class="built_in">String</span> name;</span><br><span class="line">  printA();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">B</span> </span>&#123;</span><br><span class="line">  printB();</span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">C</span> <span class="keyword">implements</span> <span class="title">A</span>,<span class="title">B</span> </span>&#123;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  <span class="built_in">String</span> name;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  printA() &#123;</span><br><span class="line">    </span><br><span class="line">  &#125;</span><br><span class="line">  <span class="meta">@override</span></span><br><span class="line">  printB() &#123;</span><br><span class="line">    </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="mixins实现类似多继承的功能"><a href="#mixins实现类似多继承的功能" class="headerlink" title="mixins实现类似多继承的功能"></a>mixins实现类似多继承的功能</h2><figure class="highlight dart"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span> </span>&#123;</span><br><span class="line"><span class="keyword">void</span> printA() &#123;</span><br><span class="line">    <span class="built_in">String</span> info = <span class="string">"this is A"</span>;</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"A"</span>);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">B</span> </span>&#123;</span><br><span class="line"><span class="keyword">void</span> printB() &#123;</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"B"</span>);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">C</span> <span class="keyword">extends</span> <span class="title">Person</span> <span class="title">with</span> <span class="title">A</span>,<span class="title">B</span> </span>&#123;</span><br><span class="line">  </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> main() &#123;</span><br><span class="line">  <span class="keyword">var</span> c = <span class="keyword">new</span> C();</span><br><span class="line">  c.printA();</span><br><span class="line">  C.printB();</span><br><span class="line">  <span class="built_in">print</span>(c.info);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="8-泛型"><a href="#8-泛型" class="headerlink" title="8. 泛型"></a>8. 泛型</h1><p>解决类 接口 方法的复用性、以及对不特定数据类型的支持（类型校验）</p><figure class="highlight dart"><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">//泛型方法</span></span><br><span class="line">T getData&lt;T&gt;(T value) &#123;</span><br><span class="line">  <span class="keyword">return</span> value;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">void</span> main() &#123;</span><br><span class="line">  getData&lt;<span class="built_in">String</span>&gt;(<span class="string">'你好'</span>);</span><br><span class="line">  <span class="built_in">print</span>(getData&lt;<span class="built_in">int</span>&gt;(<span class="number">12</span>));</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//泛型类</span></span><br><span class="line"><span class="built_in">List</span> list = <span class="keyword">new</span> <span class="built_in">List</span>&lt;<span class="built_in">String</span>&gt;();</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Flutter之Dart笔记&lt;/p&gt;
&lt;h1 id=&quot;1-入口方法、变量、常量&quot;&gt;&lt;a href=&quot;#1-入口方法、变量、常量&quot; class=&quot;headerlink&quot; title=&quot;1. 入口方法、变量、常量&quot;&gt;&lt;/a&gt;1. 入口方法、变量、常量&lt;/h1&gt;&lt;h2 id=&quot;1
      
    
    </summary>
    
      <category term="Flutter" scheme="http://chanceli.com/categories/Flutter/"/>
    
    
      <category term="Flutter" scheme="http://chanceli.com/tags/Flutter/"/>
    
      <category term="Dart" scheme="http://chanceli.com/tags/Dart/"/>
    
  </entry>
  
  <entry>
    <title>《大话数据结构》五</title>
    <link href="http://chanceli.com/DataStructurePart5/"/>
    <id>http://chanceli.com/DataStructurePart5/</id>
    <published>2019-09-15T15:55:00.000Z</published>
    <updated>2021-03-14T13:40:02.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="第6章-树"><a href="#第6章-树" class="headerlink" title="第6章 树"></a>第6章 树</h1><p>树（Tree）是 n（n≥0）个结点的有限集。n=0 时称为空树。在任意一棵非空树中：（1）有且仅有一个特定的称为根（Root）的结点；（2）当 n&gt;1 时，其余结点可分为 m（m&gt;0）个互不相交的有限集 T<sub>1</sub>、T<sub>2</sub>、……、T<sub>m</sub>，其中每一个集合本身又是一棵树，并且称为根的子树（SubTree）。</p><h2 id="树的定义"><a href="#树的定义" class="headerlink" title="树的定义"></a>树的定义</h2><blockquote><p>树（Tree）是 n（n≥0）个结点的有限集。n=0 时称为空树。在任意一棵非空树中：（1）有且仅有一个特定的称为根（Root）的结点；（2）当 n&gt;1 时，其余结点可分为 m（m&gt;0）个互不相交的有限集 T<sub>1</sub>、T<sub>2</sub>、……、T<sub>m</sub>，其中每一个集合本身又是一棵树，并且称为根的子树（SubTree）。</p></blockquote><h3 id="结点分类"><a href="#结点分类" class="headerlink" title="结点分类"></a>结点分类</h3><p>树的结点包含一个数据元素及若干指向其子树的分支。</p><p>结点拥有的子树数称为结点的度（Degree）。度为 0 的结点称为叶结点（Leaf）或终端结点；度不为 0 的结点称为非终端结点或分支结点。除根结点之外，分支结点也称为内部结点。树的度是树内各结点的度的最大值。</p><h3 id="结点间关系"><a href="#结点间关系" class="headerlink" title="结点间关系"></a>结点间关系</h3><p>结点的子树的根称为该结点的孩子（Child），相应的，该结点称为孩子的双亲（Parent）。同一个双亲的孩子之前互称兄弟（Sibling）。结点的祖先是从根到该结点所经分支上的所有结点。反之，以某结点为根的子树中的任一结点都称为该结点的子孙。</p><h3 id="树的其他相关概念"><a href="#树的其他相关概念" class="headerlink" title="树的其他相关概念"></a>树的其他相关概念</h3><p>结点的层次（Level）从根开始定义起，根为第一层，根的孩子为第二层。若某结点在第 1 层，则其子树的根就在第 i+1 层。其双亲在同一层的结点互为堂兄弟。树中结点的最大层次称为树的深度（Depth）或高度。</p><p>如果将树中结点的各子树看成从左至右是有次序的，不能互换的，则称该树为有序树，否则称为无序树。</p><p>森林（Forest）是 m（m≥0）棵互不相交的树的集合。</p><p>线性表与树结构</p><table><thead><tr><th style="text-align:center">线性结构</th><th style="text-align:center">树结构</th></tr></thead><tbody><tr><td style="text-align:center">第一个数据元素：无前驱</td><td style="text-align:center">根结点：无双亲，唯一</td></tr><tr><td style="text-align:center">最后一个数据元素：无后继</td><td style="text-align:center">叶结点：无孩子，可以多个</td></tr><tr><td style="text-align:center">中间元素：一个前驱一个后继</td><td style="text-align:center">中间结点：一个双亲多个孩子</td></tr></tbody></table><h2 id="树的抽象数据类型"><a href="#树的抽象数据类型" class="headerlink" title="树的抽象数据类型"></a>树的抽象数据类型</h2><p>ADT 树（tree）</p><p>Data</p><p>  树是由一个根结点和若干子树构成。树中结点具有相同数据类型及层次关系。</p><p>Operation</p><p>  InitTree(*T):构造空树 T。</p><p>  DestroyTree(*T):销毁树 T。</p><p>  CreateTree(*T, definition):按 definition 中给出树的定义来构造树。</p><p>  ClearTree(*T):若树 T 存在，则将树 T 清为空树。</p><p>  TreeEmpty(*T):若 T 为空树，返回 true，否则返回 false。</p><p>  TreeDepth(T):返回 T 的深度。</p><p>  Root(T):返回树的根结点。</p><p>  Value(T, cur_e):cur_e 是树 T 中一个结点，返回此结点的值。</p><p>  Assign(T, cur_e,value):给树 T 的结点 cur_e 赋值为 value。</p><p>  Parent(T, cur_e):若 cur_e 是树 T 的非根结点，则返回它的双亲，否则返回空。</p><p>  LeftChild(T, cur_e):若 cur_e 是树 T 的非叶结点，则返回它的最左孩子，否则返回空。</p><p>  RightSibling(T, cur_e):若 cur_e 有右兄弟，则返回它的右兄弟，否则返回空。</p><p>  InsertChild(<em>T, </em>p, i, c):其中 p 指向树 T 的某个结点，i 为所指结点 p 的度加上1，非空树 c 与 T 不相交，操作结果为插入 c 为树 T 中 p 指结点的第 i 棵子树。</p><p>  DeleteChild(<em>T, </em>p, i):其中 p 指向树 T 的某个结点，i 为所指结点 p 的度，操作结果为删除 T 中 p 所指结点的第 i 棵子树。</p><p>endADT</p><h2 id="树的存储结构"><a href="#树的存储结构" class="headerlink" title="树的存储结构"></a>树的存储结构</h2><h3 id="双亲表示法"><a href="#双亲表示法" class="headerlink" title="双亲表示法"></a>双亲表示法</h3><p>我们假设以一组连续空间存储树的结点，同时在每个结点中，附设一个指示器表示其双亲结点在数组中的位置。也就是说，每个结点除了知道自己是谁以外，还知道它的双亲在哪里。</p><p>以下是我们的双亲表示法的结点结构定义代码。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 树的双亲表示法结点结构定义 */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_TREE_SIZE 100</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">int</span> TElemType; <span class="comment">/* 树结点的数据类型，目前暂定为整型 */</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">PTNode</span> /* 结点结构 */</span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  TElemType data; <span class="comment">/* 结点数据 */</span></span><br><span class="line">  <span class="keyword">int</span> parent; <span class="comment">/* 双亲位置 */</span></span><br><span class="line">&#125; PTNode;</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span>  /* 树结构 */</span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  PTNode nodes[MAX_TREE_SIZE]; <span class="comment">/* 结点数组 */</span></span><br><span class="line">  <span class="keyword">int</span> r,n;  <span class="comment">/* 根的位置和结点数 */</span></span><br><span class="line">&#125; PTree</span><br></pre></td></tr></table></figure><p>存储结构的设计是一个非常灵活的过程。一个存储结构设计的是否合理，取决于基于该存储结构的运算是否合适、是否方便，时间复杂度好不好等。</p><h3 id="孩子表示法"><a href="#孩子表示法" class="headerlink" title="孩子表示法"></a>孩子表示法</h3><p>由于树中每个结点可能有多棵子树，可以考虑用多重链表，即每个结点有多个指针域，其中每个指针指向一棵子树的根结点，我们把这种方法叫做多重链表表示法。</p><p>孩子表示法。把每个结点的孩子结点排列起来，以单链表作存储结构，则 n 个结点有 n 个孩子链表，如果是叶子结点则此单链表为空。然后 n 个头指针又组成一个线性表，采用顺序存储结构，放进一个一维数组中。</p><p>以下是我们的孩子表示法的结构定义代码。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 树的孩子表示法结构定义 */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_TREE_SIZE 100</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">CTNode</span> /* 孩子结点 */</span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  <span class="keyword">int</span> child;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">CTNode</span> *<span class="title">next</span>;</span></span><br><span class="line">&#125; *ChildPtr;</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> /* 表头结构 */</span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  TElemType data;</span><br><span class="line">  ChildPtr firstchild;</span><br><span class="line">&#125; CTBox;</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> /* 树结构 */</span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  CTBox nodes[MAX_TREE_SIZE]; <span class="comment">/* 结点数组 */</span></span><br><span class="line">  <span class="keyword">int</span> r,n; <span class="comment">/* 根的位置和结点数 */</span></span><br><span class="line">&#125; CTree;</span><br></pre></td></tr></table></figure><h3 id="孩子兄弟表示法"><a href="#孩子兄弟表示法" class="headerlink" title="孩子兄弟表示法"></a>孩子兄弟表示法</h3><p>任意一棵树，它的结点的第一个孩子如果存在就是唯一的，它的右兄弟如果存在也是唯一的。因此，我们设置两个指针，分别指向该结点的第一个孩子和此结点的右兄弟。</p><p>结构定义代码如下。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 树的孩子兄弟表示法结构定义 */</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">CSNode</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  TElemType data;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">CSNode</span> *<span class="title">fistchild</span>,*<span class="title">rightsib</span>;</span></span><br><span class="line">&#125; CSNode, *CSTree;</span><br></pre></td></tr></table></figure><h2 id="二叉树的定义"><a href="#二叉树的定义" class="headerlink" title="二叉树的定义"></a>二叉树的定义</h2><p>二叉树（Binary Tree）是 n（n≥0）个结点的有限集合，该集合或者为空集（称为空二叉树），或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。</p><h3 id="二叉树特点"><a href="#二叉树特点" class="headerlink" title="二叉树特点"></a>二叉树特点</h3><p>二叉树的特点有：</p><ul><li>每个结点最多有两棵子树，所以二叉树中不存在度大于 2 的结点。注意不是只有两棵子树，而是最多有。没有子树或者一棵子树都是可以的。</li><li>左子树和右子树是有顺序的，次序不能任意颠倒。</li><li>即使树中某个结点只有一棵子树，也要区分它是左子树还是右子树。</li></ul><p>二叉树具有五种基本形态：</p><ol><li>空二叉树。</li><li>只有一个根结点。</li><li>根结点只有左子树。</li><li>根结点只有右子树。</li><li>根结点既有左子树又有右子树。</li></ol><h3 id="特殊二叉树"><a href="#特殊二叉树" class="headerlink" title="特殊二叉树"></a>特殊二叉树</h3><h4 id="1-斜树"><a href="#1-斜树" class="headerlink" title="1. 斜树"></a>1. 斜树</h4><p>所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。</p><h4 id="2-满二叉树"><a href="#2-满二叉树" class="headerlink" title="2. 满二叉树"></a>2. 满二叉树</h4><p>在一棵二叉树中，如果所有分支结点都存在左子树和右子树，并且所有叶子都在同一层，这样的二叉树称为满二叉树。</p><p>满二叉树的特点有：</p><p>（1）叶子只能出现在最下一层。</p><p>（2）非叶子结点的度一定是 2。</p><p>（3）在同样深度的二叉树中，满二叉树的结点个数最多，叶子树最多。</p><h4 id="3-完全二叉树"><a href="#3-完全二叉树" class="headerlink" title="3. 完全二叉树"></a>3. 完全二叉树</h4><p>对一棵具有 n 个结点的二叉树按层序编号，如果编号为 i（1≤i≤n）的结点与同样深度的满二叉树中编号为 i 的结点在二叉树中位置完全相同，则这棵二叉树称为完全二叉树。</p><p>完全二叉树的特点：</p><p>（1）叶子结点只能出现在最下两层。</p><p>（2）最下层的叶子一定集中在左部连续位置。</p><p>（3）倒数二层，若有叶子结点，一定都在右部连续位置。</p><p>（4）如果结点度为1，则该结点只有左孩子，即不存在只有右子树的情况。</p><p>（5）同样结点的二叉树，完全二叉树的深度最小。</p><p>判断某个二叉树是否是完全二叉树的办法，就是看着树的示意图，心中默默给每个结点按照满二叉树的结构逐层顺序编号，如果编号出现空挡，就说明不是完全二叉树，否则就是。</p><h2 id="二叉树的性质"><a href="#二叉树的性质" class="headerlink" title="二叉树的性质"></a>二叉树的性质</h2><h3 id="二叉树的性质1"><a href="#二叉树的性质1" class="headerlink" title="二叉树的性质1"></a>二叉树的性质1</h3><p>性质1：在二叉树的第 i 层上至多有 2<sup>i-1</sup> 个结点（i≥1）。</p><h3 id="二叉树的性质2"><a href="#二叉树的性质2" class="headerlink" title="二叉树的性质2"></a>二叉树的性质2</h3><p>性质2：深度为 k 的二叉树至多有 2<sup>k</sup>-1 个结点（k≥1）。</p><h3 id="二叉树的性质3"><a href="#二叉树的性质3" class="headerlink" title="二叉树的性质3"></a>二叉树的性质3</h3><p>性质3：对任何一棵二叉树 T，如果其终端结点数为 n<sub>0</sub>，度为 2 的结点数为 n<sub>2</sub>，则 n<sub>0</sub>=n<sub>2</sub>+1。</p><h3 id="二叉树的性质4"><a href="#二叉树的性质4" class="headerlink" title="二叉树的性质4"></a>二叉树的性质4</h3><p>性质4：具有 n 个结点的完全二叉树的深度为 ⎣log<sub>2</sub>n⎦+1 ( ⎣x⎦表示不大于 x 的最大整数)。</p><p>注：⎣⎦ 向下取整运算。</p><h3 id="二叉树的性质5"><a href="#二叉树的性质5" class="headerlink" title="二叉树的性质5"></a>二叉树的性质5</h3><p>性质5：如果对于一棵有 n 个结点的完全二叉树（其深度为 ⎣log<sub>2</sub>n⎦+1）的结点按层序编号（从第 1 层到第 ⎣log<sub>2</sub>n⎦+1 层），每层从左到右，对任一结点 i （1≤i≤n）有：</p><ol><li>如果 i = 1，则结点 i 是二叉树的根，无双亲；如果 i&gt;1，则其双亲是结点 ⎣i/2⎦。</li><li>如果 2i&gt;n，则结点 i 无左孩子（结点 i 为叶子结点）；否则其左孩子是结点 2i。</li><li>如果 2i+1&gt;n，则结点 i 无右孩子；否则其右孩子是结点 2i+1。</li></ol><h2 id="二叉树的存储结构"><a href="#二叉树的存储结构" class="headerlink" title="二叉树的存储结构"></a>二叉树的存储结构</h2><h3 id="二叉树顺序存储结构"><a href="#二叉树顺序存储结构" class="headerlink" title="二叉树顺序存储结构"></a>二叉树顺序存储结构</h3><p>顺序存储结构一般只用于完全二叉树。</p><h3 id="二叉链表"><a href="#二叉链表" class="headerlink" title="二叉链表"></a>二叉链表</h3><p>二叉树每个结点最多有两个孩子，所以为它设计一个数据域和两个指针域是比较自然的想法，我们称这样的链表叫做二叉链表。</p><p>以下是我们的二叉链表的结点结构定义代码。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 二叉树的二叉链表结点结构定义 */</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">BitNode</span> /* 结点结构 */</span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  TElemType data; <span class="comment">/* 结点数据 */</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">BiTNode</span> *<span class="title">lchild</span>, *<span class="title">rchild</span>;</span> <span class="comment">/* 左右孩子指针 */</span></span><br><span class="line">&#125; BiTNode, *BiTree;</span><br></pre></td></tr></table></figure><h2 id="遍历二叉树"><a href="#遍历二叉树" class="headerlink" title="遍历二叉树"></a>遍历二叉树</h2><h3 id="二叉树遍历原理"><a href="#二叉树遍历原理" class="headerlink" title="二叉树遍历原理"></a>二叉树遍历原理</h3><p>二叉树的遍历（traversing biary tree）是指从根结点出发，按照某种次序依次访问二叉树中所有结点，使得每个结点被访问一次且仅被访问一次。</p><h3 id="二叉树遍历方法"><a href="#二叉树遍历方法" class="headerlink" title="二叉树遍历方法"></a>二叉树遍历方法</h3><h4 id="1-前序遍历"><a href="#1-前序遍历" class="headerlink" title="1. 前序遍历"></a>1. 前序遍历</h4><p>规则是若二叉树为空，则空操作返回，否则先访问根结点，然后前序遍历左子树，再前序遍历右子树。</p><h4 id="2-中序遍历"><a href="#2-中序遍历" class="headerlink" title="2. 中序遍历"></a>2. 中序遍历</h4><p>规则是若树为空，则空操作返回，否则从根结点开始（注意不是先访问根结点），中序遍历根结点的左子树，然后是访问根结点，最后中序遍历右子树。</p><h4 id="3-后序遍历"><a href="#3-后序遍历" class="headerlink" title="3. 后序遍历"></a>3. 后序遍历</h4><p>规则是若树为空，则空操作返回，否则从左到右先叶子后结点的方式遍历访问左右子树，最后是访问根结点。</p><h4 id="4-层序遍历"><a href="#4-层序遍历" class="headerlink" title="4. 层序遍历"></a>4. 层序遍历</h4><p>规则是若树为空，则空操作返回，否则从树的第一层，也就是根结点开始访问，从上而下逐层遍历，在同一层中，按从左到右的顺序对结点逐个访问。</p><h3 id="前序遍历算法"><a href="#前序遍历算法" class="headerlink" title="前序遍历算法"></a>前序遍历算法</h3><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 二叉树的前序遍历递归算法 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">PreOrderTraverse</span> <span class="params">(BiTree T)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (T==<span class="literal">NULL</span>)</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">"%c"</span>,T-&gt;data); <span class="comment">/* 显示结点数据，可以更改为其他对结点操作 */</span></span><br><span class="line">  PreOrderTraverse (T-&gt;lchild); <span class="comment">/* 再先序遍历左子树 */</span></span><br><span class="line">  PreOrderTraverse (T-&gt;rchild); <span class="comment">/* 最后先序遍历右子树 */</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="中序遍历算法"><a href="#中序遍历算法" class="headerlink" title="中序遍历算法"></a>中序遍历算法</h3><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 二叉树的中序遍历递归算法 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">InOrderTraverse</span> <span class="params">(BiTree T)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (T==<span class="literal">NULL</span>)</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  InOrderTraverse (T-&gt;lchild); <span class="comment">/* 中序遍历左子树 */</span></span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">"%c"</span>,T-&gt;data); <span class="comment">/* 显示结点数据，可以更改为其他对结点操作 */</span></span><br><span class="line">  InOrderTraverse (T-&gt;rchild); <span class="comment">/* 最后中序遍历右子树 */</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="后序遍历算法"><a href="#后序遍历算法" class="headerlink" title="后序遍历算法"></a>后序遍历算法</h3><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 二叉树的后序遍历递归算法 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">PostOrderTraverse</span> <span class="params">(BiTree T)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (T==<span class="literal">NULL</span>)</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  PostOrderTraverse (T-&gt;lchild); <span class="comment">/* 先后序遍历左子树 */</span></span><br><span class="line">  PostOrderTraverse (T-&gt;rchild); <span class="comment">/* 再后序遍历右子树 */</span></span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">"%c"</span>,T-&gt;data); <span class="comment">/* 显示结点数据，可以更改为其他对结点操作 */</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="推导遍历结果"><a href="#推导遍历结果" class="headerlink" title="推导遍历结果"></a>推导遍历结果</h3><ul><li>已知前序遍历序列和中序遍历序列，可以唯一确定一棵二叉树。</li><li>已知后序遍历序列和中序遍历序列，可以唯一确定一棵二叉树。</li><li>已知前序和后序遍历，是不能唯一确定一棵二叉树的。</li></ul><h2 id="二叉树的建立"><a href="#二叉树的建立" class="headerlink" title="二叉树的建立"></a>二叉树的建立</h2><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="comment">/* 按前序输入二叉树中结点的值（一个字符） */</span></span><br><span class="line"><span class="comment">/* #表示空树，构造二叉链表表示二叉树 T。 */</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">CreateBiTree</span> <span class="params">(BiTree *T)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  TElemType ch;</span><br><span class="line">  <span class="built_in">scanf</span>(<span class="string">"%c"</span>,&amp;ch);</span><br><span class="line">  <span class="keyword">if</span> (ch==<span class="string">'#'</span>)</span><br><span class="line">    *T=<span class="literal">NULL</span>;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">    *T=(BiTree)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(BiTNode));</span><br><span class="line">    <span class="keyword">if</span> (!*T)</span><br><span class="line">      <span class="built_in">exit</span> (OVERFLOW);</span><br><span class="line">    (*T)-&gt;data=ch; <span class="comment">/* 生成根结点 */</span></span><br><span class="line">    CreateBiTree(&amp;(*T)-&gt;lchild); <span class="comment">/* 构造左子树 */</span></span><br><span class="line">    CreateBiTree(&amp;(*T)-&gt;rchild); <span class="comment">/* 构造右子树 */</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="线索二叉树"><a href="#线索二叉树" class="headerlink" title="线索二叉树"></a>线索二叉树</h2><h3 id="线索二叉树原理"><a href="#线索二叉树原理" class="headerlink" title="线索二叉树原理"></a>线索二叉树原理</h3><p>指向前驱和后继的指针称为线索，加上线索的二叉链表称为线索链表，相应的二叉树就称为线索二叉树（Threaded Binary Tree）。</p><p>对二叉树以某种次序遍历使其变为线索二叉树的过程叫做是线索化。</p><h3 id="线索二叉树结构实现"><a href="#线索二叉树结构实现" class="headerlink" title="线索二叉树结构实现"></a>线索二叉树结构实现</h3><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 二叉树的二叉线索存储结构定义 */</span></span><br><span class="line"><span class="function"><span class="keyword">typedef</span> <span class="title">enum</span> <span class="params">(Link, Thread)</span> PointerTag</span>; <span class="comment">/* Link==0 表示指向左右孩子指针 Thread==1 表示指向前驱或后继的线索 */</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">BiThrNode</span> /* 二叉线索存储结点结构 */</span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  TElemType data; <span class="comment">/* 结点数据 */</span></span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">BiThrNode</span> *<span class="title">lchild</span>, *<span class="title">rchild</span>;</span> <span class="comment">/* 左右孩子指针 */</span></span><br><span class="line">  PointerTag LTag;</span><br><span class="line">  PointerTag RTag; <span class="comment">/* 左右标志 */</span></span><br><span class="line">&#125; BiThrNode, *BiThrTree;</span><br></pre></td></tr></table></figure><p>线索化的过程就是在遍历的过程中修改空指针的过程。</p><p>如果所用的二叉树需经常遍历或查找结点时需要某种遍历序列中的前驱和后继，那么采用线索二叉链表的存储结构就是非常不错的选择。</p><h2 id="树、森林与二叉树的转换"><a href="#树、森林与二叉树的转换" class="headerlink" title="树、森林与二叉树的转换"></a>树、森林与二叉树的转换</h2><h3 id="树转换为二叉树"><a href="#树转换为二叉树" class="headerlink" title="树转换为二叉树"></a>树转换为二叉树</h3><p>将树转换为二叉树的步骤如下</p><ol><li>加线。在所有兄弟结点之间加一条连线。</li><li>去线。对树中每个结点，只保留它与第一个孩子结点的连线，删除它与其他孩子结点之间的连线。</li><li>层次调整。以树的根结点为轴心，将整棵树顺时针旋转一定的角度，使之结构层次分明。注意第一个孩子是二叉树结点的左孩子，兄弟转换过来的孩子是结点的右孩子。</li></ol><h3 id="森林转换为二叉树"><a href="#森林转换为二叉树" class="headerlink" title="森林转换为二叉树"></a>森林转换为二叉树</h3><p>步骤如下：</p><ol><li>将每个树转换为二叉树。</li><li>第一棵二叉树不动，从第二棵二叉树开始，依次把后一棵二叉树的根结点作为前一棵二叉树的根结点的右孩子，用线连接起来。当所有的二叉树连接起来后就得到了由森林转换来的二叉树。</li></ol><h3 id="二叉树转换为树"><a href="#二叉树转换为树" class="headerlink" title="二叉树转换为树"></a>二叉树转换为树</h3><ol><li>加线。</li><li>去线。</li><li>层次调整。</li></ol><h3 id="二叉树转换为森林"><a href="#二叉树转换为森林" class="headerlink" title="二叉树转换为森林"></a>二叉树转换为森林</h3><ol><li>从根结点开始，若右孩子存在，则把右孩子结点的连线删除，再查看分离后的二叉树，若右孩子存在，则连线删除……，直到所有右孩子连线都删除为止，得到分离的二叉树。</li><li>再将每棵分离的二叉树转换为树即可。</li></ol><h3 id="树与森林的遍历"><a href="#树与森林的遍历" class="headerlink" title="树与森林的遍历"></a>树与森林的遍历</h3><p>树的遍历分为两种方式。</p><ol><li>一种是先根遍历树，即先访问树的根结点，然后依次先根遍历根的每棵子树。</li><li>另一种是后根遍历，即先依次后根遍历每棵子树，然后再访问根结点。</li></ol><p>森林的遍历也分为两种方式：</p><ol><li>前序遍历</li><li>后序遍历</li></ol><h2 id="赫夫曼树及其应用"><a href="#赫夫曼树及其应用" class="headerlink" title="赫夫曼树及其应用"></a>赫夫曼树及其应用</h2><h3 id="赫夫曼树定义与原理"><a href="#赫夫曼树定义与原理" class="headerlink" title="赫夫曼树定义与原理"></a>赫夫曼树定义与原理</h3><p>从树中一个结点到另一个结点之间的分支构成两个结点之间的路径，路径上的分支数目称做路径长度。</p><p>树的路径长度就是从树根到每一个结点的路径长度之和。</p><p>带权路径长度 WPL 最小的二叉树称做赫夫曼树。</p><h3 id="赫夫曼编码"><a href="#赫夫曼编码" class="headerlink" title="赫夫曼编码"></a>赫夫曼编码</h3><p>一般地，设需要编码的字符集为{d<sub>1</sub>,d<sub>2</sub>,…,d<sub>n</sub>}，各个字符在电文中出现的次数或频率集合为 {w<sub>1</sub>,w<sub>2</sub>,…,w<sub>n</sub>}，以 d<sub>1</sub>,d<sub>2</sub>,…,d<sub>n</sub> 作为叶子结点，以 w<sub>1</sub>,w<sub>2</sub>,…,w<sub>n</sub> 作为相应叶子结点的权值来构造一棵赫夫曼树。规定赫夫曼树的左分支代表 0，右分支代表 1，则从根结点到叶子结点所经过的路径分支组成的 0 和 1 的序列便为该结点对应字符的编码，这就是赫夫曼编码。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;第6章-树&quot;&gt;&lt;a href=&quot;#第6章-树&quot; class=&quot;headerlink&quot; title=&quot;第6章 树&quot;&gt;&lt;/a&gt;第6章 树&lt;/h1&gt;&lt;p&gt;树（Tree）是 n（n≥0）个结点的有限集。n=0 时称为空树。在任意一棵非空树中：（1）有且仅有一个特定的称为根
      
    
    </summary>
    
      <category term="《大话数据结构》" scheme="http://chanceli.com/categories/%E3%80%8A%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E3%80%8B/"/>
    
    
      <category term="学习笔记" scheme="http://chanceli.com/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title>《大话数据结构》四</title>
    <link href="http://chanceli.com/DataStructurePart4/"/>
    <id>http://chanceli.com/DataStructurePart4/</id>
    <published>2019-09-10T14:24:00.000Z</published>
    <updated>2021-03-14T13:39:52.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="第5章-串"><a href="#第5章-串" class="headerlink" title="第5章 串"></a>第5章 串</h1><p>串（string）是由零个或多个字符组成的有限序列，又名叫字符串。</p><h2 id="串的定义"><a href="#串的定义" class="headerlink" title="串的定义"></a>串的定义</h2><blockquote><p>串（string）是由零个或多个字符组成的有限序列，又名叫字符串。</p></blockquote><h2 id="串的比较"><a href="#串的比较" class="headerlink" title="串的比较"></a>串的比较</h2><p>给定两个串：s= “a<sub>1</sub>a<sub>2</sub>……a<sub>n</sub>“,  t= “b<sub>1</sub>b<sub>2</sub>……b<sub>m</sub>“, 当满足以下条件之一时，s&lt;t。</p><ol><li>n&lt;m，且a<sub>i</sub>=b<sub>i</sub>（i=1, 2, ……, n）。</li><li>存在某个k&lt;min(m, n), 使得a<sub>i</sub>=b<sub>i</sub>（i=1，2，……，k-1）a<sub>k</sub>&lt;b<sub>k</sub></li></ol><h2 id="串的抽象数据类型"><a href="#串的抽象数据类型" class="headerlink" title="串的抽象数据类型"></a>串的抽象数据类型</h2><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">ADT 串（string）</span><br><span class="line">Data</span><br><span class="line">  串中元素仅由一个字符组成，相邻元素具有前驱和后继关系。</span><br><span class="line">Operation</span><br><span class="line">  StrAssign(T,*chars):生成一个其值等于字符串常量chars的串T。</span><br><span class="line">  StrCopy(T,S):串S存在，由串复制得串T。</span><br><span class="line">  ClearString(S):串S存在，将串清空。</span><br><span class="line">  StringEmpty(S):若串为空，返回true，否则返回false。</span><br><span class="line">  StrLength(S):返回串S的元素个数，即串的长度。</span><br><span class="line">  StrCompare(S,T):若S&gt;T，返回值&gt;0,若S=T，返回0，若S&lt;T，返回值&lt;0.</span><br><span class="line">  Concat(T,S1,S2):用T返回由S1和S2联接而成的新串。</span><br><span class="line">  SubString(Sub,S,pos,len):串S存在，1&lt;=pos&lt;=StrLength(S)，且0&lt;=len&lt;=StrLength(S)-pos+1,用Sub返回串S的第pos个字符长度为len的子串。</span><br><span class="line">  Index(S,T,pos):串S和T存在，T是非空串，1&lt;=pos&lt;=StrLength(S)。若主串S中存在和串T值相同的子串，则返回它在主串S中第pos个字符之后第一次出现的位置，否则返回0。</span><br><span class="line">  Replace(S,T,V):串S、T和V存在，T是非空串。用V替换主串S中出现的所有与T相等的不重叠的子串。</span><br><span class="line">  StrInsert(S,pos,T):串S和T存在，1&lt;=pos&lt;=StrLength(S)+1。在串S的第pos个字符之前插入串T。</span><br><span class="line">  StrDelete(S,pos,len):串S存在，1&lt;=pos&lt;=StrLength(S)-len+1。从串S中删除第pos个字符起长度为len的子串。</span><br><span class="line">endADT</span><br></pre></td></tr></table></figure><p>Index 的实现算法<br><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><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* T为非空串。若主串S中第pos个字符之后存在与T相等的子串，则返回第一个这样的子串在S中的位置，否则返回0 */</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">Index</span><span class="params">(String S, String T, <span class="keyword">int</span> pos)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> n,m,i;</span><br><span class="line">  String sub;</span><br><span class="line">  <span class="keyword">if</span> (pos &gt; <span class="number">0</span>)</span><br><span class="line">  &#123;</span><br><span class="line">    n = StrLength(S);</span><br><span class="line">    m = StrLength(T);</span><br><span class="line">    i = pos;</span><br><span class="line">    <span class="keyword">while</span> ( i &lt;= n-m+<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">      SubString(sub,S,i,m); <span class="comment">/* 取主串第i个位置 长度与T相等子串给sub */</span></span><br><span class="line">      <span class="keyword">if</span> (StrCompare(sub,T) != <span class="number">0</span>)  <span class="comment">/* 如果两串不相等 */</span></span><br><span class="line">        ++i;</span><br><span class="line">      <span class="keyword">else</span>  <span class="comment">/* 如果两串相等 */</span></span><br><span class="line">        <span class="keyword">return</span> i;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>; <span class="comment">/* 若无子串与T相等，返回0 */</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>当中用到了 StrLength、SubString、StrCompare 等基本操作来实现。</p><h2 id="串的存储结构"><a href="#串的存储结构" class="headerlink" title="串的存储结构"></a>串的存储结构</h2><h3 id="串的顺序存储结构"><a href="#串的顺序存储结构" class="headerlink" title="串的顺序存储结构"></a>串的顺序存储结构</h3><p>串的顺序存储结构是用一组地址连续的存储单元来存储串中的字符序列的。按照预定义的大小，为每个定义的串变量分配一个固定长度的存储区。一般使用定长数组来定义。</p><h3 id="串的链式存储结构"><a href="#串的链式存储结构" class="headerlink" title="串的链式存储结构"></a>串的链式存储结构</h3><p>对于串的链式存储结构，与线性表是相似的，但由于串结构的特殊性，结构中的每个元素数据是一个字符，如果也简单的应用链表存储串值，一个结点对应一个字符，就会存在很大的空间浪费。因此，一个结点可以存放一个字符，也可以考虑存放多个字符，最后一个结点若是未被占满时，可以用“#”或其他非串值字符补全。</p><p>串的链式存储结构除了在链接串与串操作时有一定方便外，总的来说不如顺序存储灵活，性能也不如顺序存储结构好。</p><h2 id="朴素的模式匹配算法"><a href="#朴素的模式匹配算法" class="headerlink" title="朴素的模式匹配算法"></a>朴素的模式匹配算法</h2><p>子串的定位操作通常称做串的模式匹配。<br>假设我们要从下面的主串S=“goodgoogle”中，找到T=“google这个子串的位置”。<br>简单的来说，就是对主串的每一个字符作为子串开头，与要匹配的字符串进行匹配。对主串做大循环，每个字符开头做T的长度的小循环，直到匹配成功或全部遍历完成为止。<br>最好的情况，时间复杂度为O(1)。<br>稍差一些的情况，时间复杂度为O(n+m)，其中n为主串长度，m为要匹配的子串长度。<br>根据等概率原则，平均是(n+m)/2次查找，时间复杂度为O(n+m)。</p><h2 id="KMP模式匹配算法"><a href="#KMP模式匹配算法" class="headerlink" title="KMP模式匹配算法"></a>KMP模式匹配算法</h2><p>D.E.Knuth、J.H.Morris 和 V.R.Pratt 发表一个模式匹配算法，可以大大避免重复遍历的情况，我们把它称之为克努特-莫里斯-普拉特算法，简称 KMP 算法。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;第5章-串&quot;&gt;&lt;a href=&quot;#第5章-串&quot; class=&quot;headerlink&quot; title=&quot;第5章 串&quot;&gt;&lt;/a&gt;第5章 串&lt;/h1&gt;&lt;p&gt;串（string）是由零个或多个字符组成的有限序列，又名叫字符串。&lt;/p&gt;
&lt;h2 id=&quot;串的定义&quot;&gt;&lt;a hre
      
    
    </summary>
    
      <category term="《大话数据结构》" scheme="http://chanceli.com/categories/%E3%80%8A%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E3%80%8B/"/>
    
    
      <category term="学习笔记" scheme="http://chanceli.com/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title>【iOS 开发】优化 iOS 安装包大小</title>
    <link href="http://chanceli.com/%E4%BC%98%E5%8C%96iOS%E5%AE%89%E8%A3%85%E5%8C%85%E5%A4%A7%E5%B0%8F/"/>
    <id>http://chanceli.com/优化iOS安装包大小/</id>
    <published>2019-09-09T16:00:00.000Z</published>
    <updated>2021-03-14T13:37:35.000Z</updated>
    
    <content type="html"><![CDATA[<p>最近优化了一下我负责的两个App ipa 包大小，记录一下优化方法。</p><h3 id="App1"><a href="#App1" class="headerlink" title="App1:"></a>App1:</h3><p>优化前：37.8MB 优化后：15.7MB</p><ol><li>移除重复图片，多余图片，压缩大的图片，去掉用不到的类。包大小变为35.1MB，减少了2.7MB。</li><li>把环信含音视频的 SDK 换成不含音视频的 SDK。包大小变为28MB，减少了7.1MB。</li><li>移除 armv7，不再支持 iPhone 4、iPhone 4s。包大小变为20.4MB，减少了7.6MB。</li><li>移除百度地图 SDK (项目里只用到了百度定位 SDK )。包大小变为16.6MB，减少了3.8MB。</li><li>移除环信 UI 中的兔斯基 GIF 图。包大小变为16MB，减少了0.6MB。</li><li>启动图替换为压缩后的启动图。包大小变为15.7MB，减少了0.3MB。</li></ol><h3 id="App2："><a href="#App2：" class="headerlink" title="App2："></a>App2：</h3><p>优化前：27.7MB 优化后：9.2MB</p><ol><li>移除环信音视频 SDK，包大小变为20.3MB，减少了7.4MB。</li><li>移除百度地图 SDK，包大小变为14.8MB，减少了5.5MB。</li><li>移除 armv7，包大小变为9.8MB，减少了5MB。</li><li>移除环信 UI 中的兔斯基 GIF 图。包大小变为9.2MB，减少了0.6MB。</li></ol><h3 id="小提示"><a href="#小提示" class="headerlink" title="小提示"></a>小提示</h3><ul><li>AppStore 上 App 信息中的展示的大小指的是安装大小，并非下载大小。所以要比打出来的 ipa 包大很多。</li><li>在各个机型上面的大小也不尽相同。</li></ul><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>通过这次实践，我发现影响包大小的主要因素是某些较大的第三方库及库里的图片，以及支持iPhone 4、4s 的 armv7。对于用不到的类、图片等资源文件要及时清理，使用被蓝湖等工具压缩过的图片等方式来改善安装包大小。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;最近优化了一下我负责的两个App ipa 包大小，记录一下优化方法。&lt;/p&gt;
&lt;h3 id=&quot;App1&quot;&gt;&lt;a href=&quot;#App1&quot; class=&quot;headerlink&quot; title=&quot;App1:&quot;&gt;&lt;/a&gt;App1:&lt;/h3&gt;&lt;p&gt;优化前：37.8MB 优化后：15.
      
    
    </summary>
    
      <category term="iOS开发" scheme="http://chanceli.com/categories/iOS%E5%BC%80%E5%8F%91/"/>
    
    
      <category term="iOS开发" scheme="http://chanceli.com/tags/iOS%E5%BC%80%E5%8F%91/"/>
    
  </entry>
  
  <entry>
    <title>《大话数据结构》三</title>
    <link href="http://chanceli.com/DataStructurePart3/"/>
    <id>http://chanceli.com/DataStructurePart3/</id>
    <published>2019-09-08T14:58:00.000Z</published>
    <updated>2021-03-14T13:39:41.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="第4章-栈与队列"><a href="#第4章-栈与队列" class="headerlink" title="第4章 栈与队列"></a>第4章 栈与队列</h1><p>栈是限定仅在表尾进行插入和删除操作的线性表。<br>队列是只允许在一端进行插入操作、而在另一端进行删除操作的线性表。</p><h2 id="栈的定义"><a href="#栈的定义" class="headerlink" title="栈的定义"></a>栈的定义</h2><blockquote><p>栈（stack）是限定仅在表尾进行插入和删除操作的线性表。</p></blockquote><p>我们把允许插入和删除的一端称为栈顶（top），另一端称为栈底（bottom），不含任何数据元素的栈称为空栈。栈又称为后进先出（Last In First Out）的线性表，简称 LIFO 结构。</p><p>栈的插入操作，叫做进栈，也称压栈、入栈。<br>栈的删除操作，叫做出栈，也有的叫弹栈。</p><h2 id="进栈出栈的变化形式"><a href="#进栈出栈的变化形式" class="headerlink" title="进栈出栈的变化形式"></a>进栈出栈的变化形式</h2><p>元素数量多个，出栈次序会有很多种可能。</p><h2 id="栈的抽象数据类型"><a href="#栈的抽象数据类型" class="headerlink" title="栈的抽象数据类型"></a>栈的抽象数据类型</h2><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></pre></td><td class="code"><pre><span class="line">ADT 栈</span><br><span class="line">Data</span><br><span class="line">  同线性表。元素具有相同的类型，相邻元素具有前驱和后继关系。</span><br><span class="line">Operation</span><br><span class="line">  InitStack(*S):初始化操作，建立一个空栈S。</span><br><span class="line">  DestroyStack(*S):若栈存在，则销毁它。</span><br><span class="line">  ClearStack(*S):将栈清空。</span><br><span class="line">  StackEmpty(S):若栈为空，返回 <span class="literal">true</span> ，否则返回 <span class="literal">false</span>。</span><br><span class="line">  GetTop(S,*e):若栈存在且非空，用e返回S的栈顶元素。</span><br><span class="line">  Push(*S,e):若栈S存在，插入新元素e到栈S中并成为栈顶元素。</span><br><span class="line">  Pop(*S,e):删除栈S中的栈顶元素，并用e返回其值。</span><br><span class="line">  StackLength(S):返回栈S的元素个数。</span><br><span class="line">endADT</span><br></pre></td></tr></table></figure><h2 id="栈的顺序存储结构及实现"><a href="#栈的顺序存储结构及实现" class="headerlink" title="栈的顺序存储结构及实现"></a>栈的顺序存储结构及实现</h2><h3 id="栈的顺序存储结构"><a href="#栈的顺序存储结构" class="headerlink" title="栈的顺序存储结构"></a>栈的顺序存储结构</h3><p>栈的结构定义<br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> init SElemType;</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  SElemType data[MAXSIZE]</span><br><span class="line">  <span class="keyword">int</span> top;     <span class="comment">/* 用于栈顶指针 */</span></span><br><span class="line">&#125;SqStack;</span><br></pre></td></tr></table></figure></p><h3 id="栈的顺序存储结构——进栈操作"><a href="#栈的顺序存储结构——进栈操作" class="headerlink" title="栈的顺序存储结构——进栈操作"></a>栈的顺序存储结构——进栈操作</h3><p>进栈操作 push，其代码如下：<br><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></pre></td><td class="code"><pre><span class="line"><span class="function">Status <span class="title">Push</span> <span class="params">(SqStack *S, SElemType e)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (S-&gt;top == MAXSIZE - <span class="number">1</span>) <span class="comment">/* 栈满 */</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">return</span> ERROR;</span><br><span class="line">  &#125;</span><br><span class="line">  S-&gt;top++;   <span class="comment">/* 栈顶指针增加一 */</span></span><br><span class="line">  S-&gt;data[S-&gt;top]=e;   <span class="comment">/* 将新插入元素赋值给栈顶空间 */</span></span><br><span class="line">  <span class="keyword">return</span> OK;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="栈的顺序存储结构——出栈操作"><a href="#栈的顺序存储结构——出栈操作" class="headerlink" title="栈的顺序存储结构——出栈操作"></a>栈的顺序存储结构——出栈操作</h3><p>出栈操作 pop，其代码如下：<br><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></pre></td><td class="code"><pre><span class="line"><span class="function">Status <span class="title">Pop</span> <span class="params">(SqStack *S, SElemType *e)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(S-&gt;top==<span class="number">-1</span>)</span><br><span class="line">    <span class="keyword">return</span> ERROR;</span><br><span class="line">  *e=S-&gt;data[S-&gt;top]; <span class="comment">/* 将要删除的栈顶元素赋值给e */</span></span><br><span class="line">  S-&gt;top--;   <span class="comment">/* 栈顶指针减一 */</span></span><br><span class="line">  <span class="keyword">return</span> OK;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>进栈和出栈没有涉及到任何循环语句，因此时间复杂度均是 O(1)。</p><h2 id="两栈共享空间"><a href="#两栈共享空间" class="headerlink" title="两栈共享空间"></a>两栈共享空间</h2><p>使用这样的数据结构，通常都是当两个栈的空间需求有相反关系时，也就是一个栈增长时另一个栈在缩短的情况。</p><h2 id="栈的链式存储结构及实现"><a href="#栈的链式存储结构及实现" class="headerlink" title="栈的链式存储结构及实现"></a>栈的链式存储结构及实现</h2><h3 id="栈的链式存储结构"><a href="#栈的链式存储结构" class="headerlink" title="栈的链式存储结构"></a>栈的链式存储结构</h3><p>栈的链式存储结构，简称为链栈。<br>链栈的结构代码如下：<br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">StackNode</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  SElemType data;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">StackNode</span> *<span class="title">next</span>;</span></span><br><span class="line">&#125;StackNode, *LinkStackPtr;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">LinkStack</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  LinkStackPtr top;</span><br><span class="line">  <span class="keyword">int</span> count;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="栈的链式存储结构——进栈操作"><a href="#栈的链式存储结构——进栈操作" class="headerlink" title="栈的链式存储结构——进栈操作"></a>栈的链式存储结构——进栈操作</h3><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 插入元素 e 为新的栈顶元素 */</span></span><br><span class="line"><span class="function">Status <span class="title">Push</span> <span class="params">(LinkStack *S, SElemType e)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  LinkStackPtr s = (LinkStackPtr)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(StackNode));</span><br><span class="line">  s-&gt;data = e;</span><br><span class="line">  s-&gt;next = s-&gt;top;  <span class="comment">/* 把当前的栈顶元素赋值给新结点的直接后继 */</span></span><br><span class="line">  S-&gt;top = s;  <span class="comment">/* 将新的结点s赋值给栈顶指针 */</span></span><br><span class="line">  S-&gt;count++;</span><br><span class="line">  <span class="keyword">return</span> OK;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="栈的链式存储结构——出栈操作"><a href="#栈的链式存储结构——出栈操作" class="headerlink" title="栈的链式存储结构——出栈操作"></a>栈的链式存储结构——出栈操作</h3><p>假设变量 p 用来存储要删除的栈顶结点，将栈顶指针下移一位，最后释放 p 即可。<br><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></pre></td><td class="code"><pre><span class="line"><span class="function">Status <span class="title">Pop</span> <span class="params">(LinkStack *S, SElemType *e)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  LinkStackPtr p;</span><br><span class="line">  <span class="keyword">if</span> (StackEmpty(*S))</span><br><span class="line">    <span class="keyword">return</span> ERROR;</span><br><span class="line">  *e = S-&gt;top-&gt;data;</span><br><span class="line">  p=S-&gt;top;  <span class="comment">/* 将栈顶结点赋值给p */</span></span><br><span class="line">  S-&gt;top=S-&gt;top-&gt;next;  <span class="comment">/* 使得栈顶指针下移一位，指向后一结点 */</span></span><br><span class="line">  <span class="built_in">free</span>(p);  <span class="comment">/* 释放结点 p */</span></span><br><span class="line">  S-&gt;count--;</span><br><span class="line">  <span class="keyword">return</span> OK;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>链栈的进栈 push 和出栈 pop 操作没有任何循环操作，时间复杂度均为O(1)。<br>如果栈的使用过程中元素变化不可预料，有时很小，有时非常大，那么最好是用链栈，反之，如果它的变化在可控范围内，建议使用顺序栈会更好一些。</p><h2 id="栈的作用"><a href="#栈的作用" class="headerlink" title="栈的作用"></a>栈的作用</h2><p>栈的引入简化了程序设计的问题，划分了不同关注层次，使得思考范围缩小，更加聚焦于我们要解决的问题核心。</p><h2 id="栈的应用——递归"><a href="#栈的应用——递归" class="headerlink" title="栈的应用——递归"></a>栈的应用——递归</h2><h3 id="斐波那切数列实现"><a href="#斐波那切数列实现" class="headerlink" title="斐波那切数列实现"></a>斐波那切数列实现</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">Fbi</span> <span class="params">(<span class="keyword">int</span> i)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (i &lt; <span class="number">2</span>)</span><br><span class="line">    <span class="keyword">return</span> i == <span class="number">0</span> ? <span class="number">0</span> : <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">return</span> Fbi(i<span class="number">-1</span>) + Fbi(i<span class="number">-2</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">int</span> i;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i &lt; <span class="number">40</span>; i++)</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"%d "</span>, Fbi(i));</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="递归定义"><a href="#递归定义" class="headerlink" title="递归定义"></a>递归定义</h3><p>在高级语言中，调用自己和其他函数并没有本质的不同。我们把一个直接调用自己或通过一系列的调用语句间接地调用自己的函数，称做递归函数。<br>每个递归定义必须至少有一个条件，满足递归不再进行，即不再引用自身而是返回值退出。</p><h2 id="栈的应用——四则运算表达式求值"><a href="#栈的应用——四则运算表达式求值" class="headerlink" title="栈的应用——四则运算表达式求值"></a>栈的应用——四则运算表达式求值</h2><ol><li><p>将中缀表达式转化为后缀表达式（栈用来进出运算的符号）。</p></li><li><p>将后缀表达式进行运算得出结果（栈用来进出运算的数字）。</p></li></ol><h2 id="队列的定义"><a href="#队列的定义" class="headerlink" title="队列的定义"></a>队列的定义</h2><blockquote><p>队列（queue）是只允许在一端进行插入操作，而在另一端进行删除操作的线性表。</p></blockquote><p>队列是一种先进先出（First In First Out）的线性表，简称FIFO。允许插入的一端称为队尾，允许删除的一端称为队头。</p><h2 id="队列的抽象数据类型"><a href="#队列的抽象数据类型" class="headerlink" title="队列的抽象数据类型"></a>队列的抽象数据类型</h2><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></pre></td><td class="code"><pre><span class="line">ADT 队列（Queue）</span><br><span class="line"></span><br><span class="line">Data</span><br><span class="line">  同线性表。元素具有相同的类型，相邻元素具有前缀和后继关系。</span><br><span class="line">Operation</span><br><span class="line">  InitQueue(*Q):初始化操作，建立一个空队列Q。</span><br><span class="line">  DestroyQueue(*Q):若队列Q存在，则销毁它。</span><br><span class="line">  ClearQueue(*Q):将队列Q清空。</span><br><span class="line">  QueueEmpty(*Q):若队列Q为空，返回 <span class="literal">true</span>，否则返回 <span class="literal">false</span>。</span><br><span class="line">  GetHead(Q,*e):若队列Q存在且非空，用e返回队列Q的队头元素。</span><br><span class="line">  EnQueue(*Q,e):若队列Q存在，插入新元素e到队列Q中并成为队尾元素。</span><br><span class="line">  DeQueue(*Q,*e):删除队列Q中队头元素，并用e返回其值。</span><br><span class="line">  QueueLength(Q):返回队列Q的元素个数。</span><br><span class="line">endADT</span><br></pre></td></tr></table></figure><h2 id="循环队列"><a href="#循环队列" class="headerlink" title="循环队列"></a>循环队列</h2><h3 id="循环队列定义"><a href="#循环队列定义" class="headerlink" title="循环队列定义"></a>循环队列定义</h3><p>我们把队列的这种头尾相接的顺序存储结构称为循环队列。</p><h2 id="队列的链式存储结构及实现"><a href="#队列的链式存储结构及实现" class="headerlink" title="队列的链式存储结构及实现"></a>队列的链式存储结构及实现</h2><p>队列的链式存储结构，其实就是线性表的单链表，只不过它只能尾进头出而已，我们把它简称为链队列。<br>链队列的结构为：<br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="keyword">int</span> QElemType;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">QNode</span>  /* 节点结构 */</span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  QElemType data;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">QNode</span> *<span class="title">next</span>;</span></span><br><span class="line">&#125;QNode,*QueuePtr;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span>  /* 队列的链表结构 */</span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  QueuePtr front,rear; <span class="comment">/* 队头、队尾指针 */</span></span><br><span class="line">&#125;LinkQueue;</span><br></pre></td></tr></table></figure></p><h3 id="队列的链式存储结构——入队操作"><a href="#队列的链式存储结构——入队操作" class="headerlink" title="队列的链式存储结构——入队操作"></a>队列的链式存储结构——入队操作</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function">Status <span class="title">EnQueue</span><span class="params">(LinkQueue *Q, QElemType e)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  QueuePtr s = (QueuePtr)<span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(QNode));</span><br><span class="line">  <span class="keyword">if</span>(!s)  <span class="comment">/* 存储分配失败 */</span></span><br><span class="line">    <span class="built_in">exit</span>(OVERFLOW);</span><br><span class="line">  s-&gt;data = e;</span><br><span class="line">  s-&gt;next = <span class="literal">NULL</span>;</span><br><span class="line">  Q-&gt;rear-&gt;next = s;  <span class="comment">/* 把拥有元素e新结点s赋值给原队尾节点的后继 */</span></span><br><span class="line">  Q-&gt;rear = s;  <span class="comment">/* 把当前的s设置为队尾结点，rear指向s */</span></span><br><span class="line">  <span class="keyword">return</span> OK;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="队列的链式存储结构——出队操作"><a href="#队列的链式存储结构——出队操作" class="headerlink" title="队列的链式存储结构——出队操作"></a>队列的链式存储结构——出队操作</h3><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></pre></td><td class="code"><pre><span class="line"><span class="function">Status <span class="title">DeQueue</span><span class="params">(LinkQueue *Q, QElemType *e)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  QueuePtr p;</span><br><span class="line">  <span class="keyword">if</span>(Q-&gt;front==Q-&gt;rear)</span><br><span class="line">    <span class="keyword">return</span> ERROR;</span><br><span class="line">  p=Q-&gt;front-&gt;next;  <span class="comment">/* 将欲删除的队头结点暂存给p */</span></span><br><span class="line">  *e=p-&gt;data;  <span class="comment">/* 将欲删除的队头结点的值赋值给e */</span></span><br><span class="line">  Q-&gt;front-&gt;next=p-&gt;next;  <span class="comment">/* 将原队头结点后继p-&gt;next赋值给头结点后缀 */</span></span><br><span class="line">  <span class="keyword">if</span>(Q-&gt;rear==p)  <span class="comment">/* 若队头是队尾，则删除后将rear指向头结点 */</span></span><br><span class="line">    Q-&gt;rear=Q-&gt;front;</span><br><span class="line">  <span class="built_in">free</span>(p);</span><br><span class="line">  <span class="keyword">return</span> OK;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在可以确定队列长度最大值的情况下，建议用循环队列，如果你无法预估队列的长度时，则用链队列。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;第4章-栈与队列&quot;&gt;&lt;a href=&quot;#第4章-栈与队列&quot; class=&quot;headerlink&quot; title=&quot;第4章 栈与队列&quot;&gt;&lt;/a&gt;第4章 栈与队列&lt;/h1&gt;&lt;p&gt;栈是限定仅在表尾进行插入和删除操作的线性表。&lt;br&gt;队列是只允许在一端进行插入操作、而在另一
      
    
    </summary>
    
      <category term="《大话数据结构》" scheme="http://chanceli.com/categories/%E3%80%8A%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E3%80%8B/"/>
    
    
      <category term="学习笔记" scheme="http://chanceli.com/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title>【iOS 开发】使用环信实现聊天遇到的一些坑</title>
    <link href="http://chanceli.com/imeasemob/"/>
    <id>http://chanceli.com/imeasemob/</id>
    <published>2019-08-26T07:47:00.000Z</published>
    <updated>2021-03-14T13:49:40.000Z</updated>
    
    <content type="html"><![CDATA[<p>使用环信实现聊天的过程遇到了一些坑，记录一下避免自己和其他人踩坑或者尽快出坑。</p><h2 id="问题1-聊天页面环信工具栏向上偏移"><a href="#问题1-聊天页面环信工具栏向上偏移" class="headerlink" title="问题1 聊天页面环信工具栏向上偏移"></a>问题1 聊天页面环信工具栏向上偏移</h2><h3 id="问题描述："><a href="#问题描述：" class="headerlink" title="问题描述："></a>问题描述：</h3><p>在聊天页面，点击输入框弹起键盘，点击<code>IQKeyboardManager</code>带的完成按钮或者点击空白页面收起键盘，反复操作多次后，会出现页面向上偏移，环信<code>UI</code>的工具栏移动到了页面最上边。</p><h3 id="解决方案："><a href="#解决方案：" class="headerlink" title="解决方案："></a>解决方案：</h3><p>在聊天页面禁用<code>IQKeyboardManager</code>。<br><figure class="highlight objc"><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">- (<span class="keyword">void</span>)viewDidAppear:(<span class="built_in">BOOL</span>)animated</span><br><span class="line">&#123;</span><br><span class="line">    [<span class="keyword">super</span> viewDidAppear:animated];</span><br><span class="line">    IQKeyboardManager *keyboardManager =  [IQKeyboardManager sharedManager];</span><br><span class="line">    keyboardManager.enable = <span class="literal">NO</span>;</span><br><span class="line">    keyboardManager.enableAutoToolbar = <span class="literal">NO</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>)viewWillDisappear:(<span class="built_in">BOOL</span>)animated</span><br><span class="line">&#123;</span><br><span class="line">    [<span class="keyword">super</span> viewWillDisappear:animated];</span><br><span class="line">    IQKeyboardManager *keyboardManager =  [IQKeyboardManager sharedManager];</span><br><span class="line">    keyboardManager.enable = <span class="literal">YES</span>;</span><br><span class="line">    keyboardManager.enableAutoToolbar = <span class="literal">YES</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="问题2-保存环信昵称头像到数据库失败"><a href="#问题2-保存环信昵称头像到数据库失败" class="headerlink" title="问题2 保存环信昵称头像到数据库失败"></a>问题2 保存环信昵称头像到数据库失败</h2><h3 id="问题描述：-1"><a href="#问题描述：-1" class="headerlink" title="问题描述："></a>问题描述：</h3><p>环信服务器不存储用户的昵称头像等用户数据。需要客户端自己来存储并展示在界面上。有几个时机是需要插入或更新一条包含昵称、头像的用户数据到数据库里，比如查看用户详情，在某个页面收到环信消息。我们服务器提供的查询用户信息接口返回的昵称和头像，之前是没有问题的，有一次出现了从用户信息界面进入聊天页面不显示昵称的问题。</p><h3 id="问题原因："><a href="#问题原因：" class="headerlink" title="问题原因："></a>问题原因：</h3><p>经排查，接口返回的用户头像字段的值是<code>null</code>，由于客户端之前没有做空处理，导致插入或更新一条数据失败。<br><figure class="highlight plain"><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">DB Query: INSERT or REPLACE INTO t_huanxin (hxId,nickname,avatar) VALUES (?,?,?)</span><br><span class="line">Unknown error finalizing or resetting statement (19: NOT NULL constraint failed: t_huanxin.avatar)</span><br></pre></td></tr></table></figure></p><h3 id="解决方案：-1"><a href="#解决方案：-1" class="headerlink" title="解决方案："></a>解决方案：</h3><p>存储用户信息到本地数据库时增加安全判断。如果为空，就存入空字符串。避免插入或更新字段为<code>null</code>导致操作失败。<br><figure class="highlight objc"><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="built_in">NSString</span> *nickname = dic[<span class="string">@"nickname"</span>]?:<span class="string">@""</span>;</span><br><span class="line"><span class="built_in">NSString</span> *avatar = dic[<span class="string">@"avatar"</span>]?:<span class="string">@""</span>;</span><br></pre></td></tr></table></figure></p><h2 id="问题3-聊天页面发送首条消息后下拉刷新出现两条重复消息"><a href="#问题3-聊天页面发送首条消息后下拉刷新出现两条重复消息" class="headerlink" title="问题3 聊天页面发送首条消息后下拉刷新出现两条重复消息"></a>问题3 聊天页面发送首条消息后下拉刷新出现两条重复消息</h2><h3 id="问题描述：-2"><a href="#问题描述：-2" class="headerlink" title="问题描述："></a>问题描述：</h3><p>使用环信<code>Demo</code>发送消息，首条消息发送后，下拉刷新，单聊页面会有两条重复的消息。<br>操作步骤：1.点击单聊页面右上角的清空按钮，清空某个用户的消息。2.返回会话列表。3.首次进入该用户的单聊页面。4.发送首条消息。5.页面下拉刷新。6.出现了两条一摸一样的消息。</p><h3 id="解决方案：-2"><a href="#解决方案：-2" class="headerlink" title="解决方案："></a>解决方案：</h3><p>在<code>EMChatViewController</code>的<code>tableViewDidTriggerHeaderRefresh</code>方法中增加如下判断。<br><figure class="highlight objc"><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="keyword">void</span>)tableViewDidTriggerHeaderRefresh</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">//解决首条消息发送后下拉刷新出现两条重复消息的BUG 开始</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">self</span>.dataArray.count &amp;&amp; <span class="keyword">self</span>.moreMsgId == <span class="literal">nil</span>) &#123;</span><br><span class="line">        [<span class="keyword">self</span> tableViewDidFinishTriggerHeader:<span class="literal">YES</span> reload:<span class="literal">NO</span>];</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//解决首条消息发送后下拉刷新出现两条重复消息的BUG 结束</span></span><br><span class="line">    other code</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="问题4-聊天页面发送语音，点击“按住录音”，发送了0秒的聊天记录"><a href="#问题4-聊天页面发送语音，点击“按住录音”，发送了0秒的聊天记录" class="headerlink" title="问题4 聊天页面发送语音，点击“按住录音”，发送了0秒的聊天记录"></a>问题4 聊天页面发送语音，点击“按住录音”，发送了0秒的聊天记录</h2><h3 id="问题描述：-3"><a href="#问题描述：-3" class="headerlink" title="问题描述："></a>问题描述：</h3><p><code>iOS</code>环信<code>Demo</code>聊天页面，发送语音，点击“按住录音”，发送了0秒的聊天记录。</p><h3 id="解决方案：-3"><a href="#解决方案：-3" class="headerlink" title="解决方案："></a>解决方案：</h3><p>在<code>EMChatViewController</code>的<code>chatBarRecordAudioViewStopRecord:timeLength:</code>方法中增加对录音时长的判断。<br><figure class="highlight objc"><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="keyword">void</span>)chatBarRecordAudioViewStopRecord:(<span class="built_in">NSString</span> *)aPath</span><br><span class="line">                              timeLength:(<span class="built_in">NSInteger</span>)aTimeLength</span><br><span class="line">&#123;</span><br><span class="line">    EMVoiceMessageBody *body = [[EMVoiceMessageBody alloc] initWithLocalPath:aPath displayName:<span class="string">@"audio"</span>];</span><br><span class="line">    body.duration = (<span class="keyword">int</span>)aTimeLength;</span><br><span class="line">    <span class="keyword">if</span> (body.duration == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="built_in">NSLog</span>(<span class="string">@"录音时长为0"</span>);</span><br><span class="line">        <span class="built_in">NSLog</span>(<span class="string">@"录制时间过短，不能发送"</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    [<span class="keyword">self</span> _sendMessageWithBody:body ext:<span class="literal">nil</span> isUpload:<span class="literal">YES</span>];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h2 id="问题5-聊天页面点击查看图片，图片右上角按钮显示Done"><a href="#问题5-聊天页面点击查看图片，图片右上角按钮显示Done" class="headerlink" title="问题5 聊天页面点击查看图片，图片右上角按钮显示Done"></a>问题5 聊天页面点击查看图片，图片右上角按钮显示<code>Done</code></h2><h3 id="问题描述：-4"><a href="#问题描述：-4" class="headerlink" title="问题描述："></a>问题描述：</h3><p>聊天页面，点击查看图片，图片右上角的按钮显示<code>Done</code>，而不是”完成“。</p><h3 id="解决方案：-4"><a href="#解决方案：-4" class="headerlink" title="解决方案："></a>解决方案：</h3><p>直接全局搜索<code>Done</code>,把环信<code>UI</code>里的<code>Done</code>,替换成”完成“即可。</p><h2 id="问题6-如何实现推送消息内容自定义"><a href="#问题6-如何实现推送消息内容自定义" class="headerlink" title="问题6 如何实现推送消息内容自定义"></a>问题6 如何实现推送消息内容自定义</h2><h3 id="问题描述：-5"><a href="#问题描述：-5" class="headerlink" title="问题描述："></a>问题描述：</h3><p>不想使用环信默认的推送内容：”您有一条新消息”，或”xxx：消息内容”。想实现自定义的消息内容。</p><h3 id="解决方案：-5"><a href="#解决方案：-5" class="headerlink" title="解决方案："></a>解决方案：</h3><p>发送消息扩展里添加字段<code>em_apns_ext</code><br><figure class="highlight objc"><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">&#123;</span><br><span class="line">nickname:<span class="string">"王小二"</span>,</span><br><span class="line">avatar:<span class="string">"http://www.baidu.com"</span>,</span><br><span class="line">em_apns_ext:&#123;<span class="string">"em_push_content"</span>:<span class="string">"自定义推送内容"</span>&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>参考链接：<a href="http://docs-im.easemob.com/im/ios/apns/content" target="_blank" rel="noopener">环信 APNs 内容解析文档</a></p><h2 id="问题7-头像链接与路由短链冲突"><a href="#问题7-头像链接与路由短链冲突" class="headerlink" title="问题7 头像链接与路由短链冲突"></a>问题7 头像链接与路由短链冲突</h2><h3 id="问题描述：-6"><a href="#问题描述：-6" class="headerlink" title="问题描述："></a>问题描述：</h3><p>项目里点击推送通知到落地页采用了路由模式，有一个场景是需要我们自己服务器发推送点击通知栏跳转到聊天页面。路由短链类似：<code>page://chat?from=hxId_10086</code>,由于某些原因，需要带上昵称和头像，加上头像链接后，短链变成了<code>page://chat?from=hxId_10086&amp;avatar=http://www.baidu.com&amp;nickname=王小二</code>，<code>iOS</code> 解析短链的方法是先把短链字符串转成了<code>URL</code>，加了<code>avatar</code>参数之后，转换失败，导致无法跳转。</p><h3 id="解决方案：-6"><a href="#解决方案：-6" class="headerlink" title="解决方案："></a>解决方案：</h3><ul><li>方案1：<br>由于短链中含有<code>URL</code>，其实违反了路由链接设计的初衷，可以把<code>avatar</code>的值用<code>AES</code>加密一下传输，避免出现链接。</li><li>方案2：<br>不把短链字符串转换成<code>URL</code>，直接处理短链，去解析。</li></ul><h2 id="问题8-小程序发的语音消息iOS无法播放"><a href="#问题8-小程序发的语音消息iOS无法播放" class="headerlink" title="问题8 小程序发的语音消息iOS无法播放"></a>问题8 小程序发的语音消息<code>iOS</code>无法播放</h2><h3 id="问题描述：-7"><a href="#问题描述：-7" class="headerlink" title="问题描述："></a>问题描述：</h3><p>小程序发过来的语音消息<code>iOS</code>无法播放。</p><h3 id="问题原因：-1"><a href="#问题原因：-1" class="headerlink" title="问题原因："></a>问题原因：</h3><p><code>Android</code>和<code>iOS</code>发的语音格式都是<code>AMR</code>，小程序发的语音格式是<code>MP3</code>，<code>iOS</code>收到音频后，会先调用<code>_convertAudioFile:</code>方法转换格式，该方法内部实现实现里有一行代码是判断路径是否是<code>MP3</code>文件的，<br><figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[EMAudioPlayerHelper isMP3File:retPath]</span><br></pre></td></tr></table></figure></p><p>这个判断方法有问题，发过来的是<code>MP3</code>，却判断不是<code>MP3</code>，接着音频被当成<code>AMR</code>去转换<code>WAV</code> ，转换音频格式失败，播放失败。</p><h3 id="解决方案：-7"><a href="#解决方案：-7" class="headerlink" title="解决方案："></a>解决方案：</h3><p>在<code>EMAudioPlayerHelper</code>类中的<code>startPlayerWithPath:model:completion:</code>方法中增加判断，如果含有<code>.mp3</code>，就不转换，直接去播放。不含<code>.mp3</code>，就照旧走<code>_convertAudioFile:</code>转换格式方法。<br><figure class="highlight objc"><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="keyword">if</span> ([aPath containsString:<span class="string">@".mp3"</span>]) &#123;</span><br><span class="line">   <span class="comment">//不转换</span></span><br><span class="line">&#125;<span class="keyword">else</span> &#123;</span><br><span class="line">   aPath = [<span class="keyword">self</span> _convertAudioFile:aPath];</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// aPath = [self _convertAudioFile:aPath];</span></span><br></pre></td></tr></table></figure></p><h3 id="问题扩展："><a href="#问题扩展：" class="headerlink" title="问题扩展："></a>问题扩展：</h3><p><code>iOS</code>发的语音消息，<code>Web</code>/小程序收到后无法播放？<br><code>App</code>端发来的文件是<code>AMR</code>格式的，小程序需要下载的时候转成<code>MP3</code>格式去播放。转换方法如下：<br>参考链接：<a href="http://docs-im.easemob.com/im/web/basics/message#%E9%9F%B3%E9%A2%91%E6%B6%88%E6%81%AF" target="_blank" rel="noopener">环信 Web IM 音频消息文档</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;使用环信实现聊天的过程遇到了一些坑，记录一下避免自己和其他人踩坑或者尽快出坑。&lt;/p&gt;
&lt;h2 id=&quot;问题1-聊天页面环信工具栏向上偏移&quot;&gt;&lt;a href=&quot;#问题1-聊天页面环信工具栏向上偏移&quot; class=&quot;headerlink&quot; title=&quot;问题1 聊天页面环信工
      
    
    </summary>
    
      <category term="iOS 开发" scheme="http://chanceli.com/categories/iOS-%E5%BC%80%E5%8F%91/"/>
    
    
      <category term="iOS 开发" scheme="http://chanceli.com/tags/iOS-%E5%BC%80%E5%8F%91/"/>
    
      <category term="环信" scheme="http://chanceli.com/tags/%E7%8E%AF%E4%BF%A1/"/>
    
  </entry>
  
  <entry>
    <title>《大话数据结构》二</title>
    <link href="http://chanceli.com/DataStructurePart2/"/>
    <id>http://chanceli.com/DataStructurePart2/</id>
    <published>2019-05-29T09:46:00.000Z</published>
    <updated>2021-03-14T13:39:31.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="第3章-线性表"><a href="#第3章-线性表" class="headerlink" title="第3章 线性表"></a>第3章 线性表</h1><p>线性表：零个或多个数据元素的有限序列。</p><h2 id="线性表的定义"><a href="#线性表的定义" class="headerlink" title="线性表的定义"></a>线性表的定义</h2><blockquote><p>线性表(List):零个或多个数据元素的有限序列。</p></blockquote><p>线性表元素的个数 n (n&gt;=0) 定义为线性表的长度，当 n = 0时，称为空表。</p><h2 id="线性表的抽象数据类型"><a href="#线性表的抽象数据类型" class="headerlink" title="线性表的抽象数据类型"></a>线性表的抽象数据类型</h2><p>ADT 线性表(List)<br>Data<br>​    线性表的数据对象集合为{a<sub>1</sub>,a<sub>2</sub>,……,a<sub>n</sub>},每个元素的类型均为DataType。其中，除第一个元素a<sub>1</sub>外，每一个元素有且只有一个直接前驱元素，除了最后一个元素a<sub>n</sub>外，每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。</p><p>Operation</p><p>​    InitList (*L):初始化操作，建立一个空的线性表L。</p><p>​    ListEmpty(L)：若线性表为空，返回true，否则返回false。</p><p>​    ClearList(*L)：将线性表清空。</p><p>​    GetElem(L,i,*e)：在线性表L中的第i个位置元素值返回给e。</p><p>​    LocateElem(L,e)：在线性表L中查找与给定值e相等的元素，如果查找成功，返回该元素在表中的序号表示成功；否则，返回0表示失败。</p><p>​    ListInsert(*L,i,e)：在线性表L中的第i个位置插入新元素e。</p><p>​    ListDelete(<em>L,i, </em>e)：删除线性表L中的第i个位置元素，并用e返回其值。</p><p>​    ListLength(L)：返回线性表L的元素个数。</p><p>endADT</p><h2 id="线性表的顺序存储结构"><a href="#线性表的顺序存储结构" class="headerlink" title="线性表的顺序存储结构"></a>线性表的顺序存储结构</h2><h3 id="顺序存储定义"><a href="#顺序存储定义" class="headerlink" title="顺序存储定义"></a>顺序存储定义</h3><blockquote><p>线性表的顺序存储结构，指的是用一段地址连续的存储单元依次存储线性表的数据元素。</p></blockquote><h3 id="顺序存储方式"><a href="#顺序存储方式" class="headerlink" title="顺序存储方式"></a>顺序存储方式</h3><p>一维数组来实现顺序存储结构。</p><h3 id="数组长度与线性表长度区别"><a href="#数组长度与线性表长度区别" class="headerlink" title="数组长度与线性表长度区别"></a>数组长度与线性表长度区别</h3><p>数组的长度是存放线性表的存储空间的长度。线性表的长度是线性表中数据元素的个数。在任意时刻，线性表的长度应该小于等于数组的长度。</p><h3 id="地址计算方法"><a href="#地址计算方法" class="headerlink" title="地址计算方法"></a>地址计算方法</h3><p>存储器中每个存储单元都有自己的编号，这个编号称为地址。</p><p>LOC(a<sub>i</sub>) = LOC(a<sub>1</sub>) + (i-1)*c</p><p>存取的时间性能为O(1)。</p><h2 id="顺序存储结构的插入与删除"><a href="#顺序存储结构的插入与删除" class="headerlink" title="顺序存储结构的插入与删除"></a>顺序存储结构的插入与删除</h2><h3 id="获得元素操作"><a href="#获得元素操作" class="headerlink" title="获得元素操作"></a>获得元素操作</h3><p>只要i的数值在数组的下标范围内，就是把数组的第i-1下标的值返回即可。</p><h3 id="插入操作"><a href="#插入操作" class="headerlink" title="插入操作"></a>插入操作</h3><p>插入算法的思路：</p><ul><li>如果插入位置不合理，抛出异常；</li><li>如果线性表的长度大于等于数组长度，则抛出异常或动态增加容量；</li><li>从最后一个元素开始向前遍历到第i个位置，分别将它们都向后移动一个位置；</li><li>将要插入元素填入位置i处；</li><li>表长加1。</li></ul><h3 id="删除操作"><a href="#删除操作" class="headerlink" title="删除操作"></a>删除操作</h3><p>删除算法的思路：</p><ul><li>如果删除位置不合理，抛出异常；</li><li>取出删除元素；</li><li>从删除元素位置开始遍历到最后一个元素位置，分别将它们都向前移动一个位置；</li><li>表长减1。</li></ul><p>插入和删除的时间复杂度，最好情况为O(1)，最坏情况为O(n)，平均时间复杂度为O(n)。</p><h3 id="线性表顺序存储结构的优缺点"><a href="#线性表顺序存储结构的优缺点" class="headerlink" title="线性表顺序存储结构的优缺点"></a>线性表顺序存储结构的优缺点</h3><p>优点：</p><ul><li>无须为表示表中元素之间的逻辑关系而增加额外的存储空间。</li><li>可以快速地存取表中任一位置的元素。</li></ul><p>缺点：</p><ul><li>插入和删除操作需要移动大量元素。</li><li>当线性表长度变化较大时，难以确定存储空间的容量。</li><li>造成存储空间的”碎片”。</li></ul><h2 id="线性表的链式存储结构"><a href="#线性表的链式存储结构" class="headerlink" title="线性表的链式存储结构"></a>线性表的链式存储结构</h2><h3 id="线性表链式存储结构定义"><a href="#线性表链式存储结构定义" class="headerlink" title="线性表链式存储结构定义"></a>线性表链式存储结构定义</h3><p>​    为了表示每个数据元素a<sub>i</sub>与其直接后继数据元素a<sub>i+1</sub>之间的逻辑关系，对数据元素a<sub>i</sub>来说，除了存储其本身的信息之外，还需存储一个指示其直接后继的信息(即直接后继的存储位置)。我们把存储数据元素信息的域称为数据域，把存储直接后继位置的域称为指针域。指针域中存储的信息称作指针或链。这两部分信息组成数据元素a<sub>i</sub>的存储映像，称为结点(Node)。</p><p>​    n个结点(a<sub>i</sub>的存储映像)链结成一个链表，即为线性表(a<sub>1</sub>,a<sub>2</sub>,…,a<sub>n</sub>)的链式存储结构，因为此链表的每个结点中只包含一个指针域，所以叫做单链表。</p><p>​    链表中的第一个结点的存储位置叫做头指针。在单链表的第一个结点前附设一个结点，称为头结点。头结点的指针域存储指向第一个结点的指针。</p><h3 id="线性表链式存储结构代码描述"><a href="#线性表链式存储结构代码描述" class="headerlink" title="线性表链式存储结构代码描述"></a>线性表链式存储结构代码描述</h3><p>单链表中，我们在C语言中可用结构指针来描述。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*线性表的单链表存储结构*/</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">Node</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">  ElemType data;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">Node</span> *<span class="title">next</span>;</span></span><br><span class="line">&#125; Node;</span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">Node</span> *<span class="title">LinkList</span>;</span> <span class="comment">/*定义LinkList*/</span></span><br></pre></td></tr></table></figure><h3 id="单链表的读取"><a href="#单链表的读取" class="headerlink" title="单链表的读取"></a>单链表的读取</h3><p>获取链表第i个数据的算法思路：</p><ol><li>声明一个指针p指向链表的第一个结点，初始化j从1开始；</li><li>当j&lt;i时，就遍历链表，让p的指针向后移动，不断指向下一结点，j累加1；</li><li>若到链表末尾p为空，则说明第i个结点不存在；</li><li>否则查找成功，返回结点p的数据。</li></ol><h3 id="单链表的插入与删除"><a href="#单链表的插入与删除" class="headerlink" title="单链表的插入与删除"></a>单链表的插入与删除</h3><h3 id="单链表的插入"><a href="#单链表的插入" class="headerlink" title="单链表的插入"></a>单链表的插入</h3><p>单链表第i个数据插入结点的算法思路：</p><ol><li>声明一指针p指向链表头结点，初始化j从1开始；</li><li>当j&lt;i时，就遍历链表，让p的指针向后移动，不断指向下一结点，j累加1；</li><li>若到链表末尾p为空，则说明第i个结点不存在；</li><li>否则查找成功，在系统中生成一个空节点s；</li><li>将数据元素e赋值给s-&gt;data;</li><li>单链表的插入标准语句s-&gt;next = p-&gt;next; p-&gt;next = s;</li><li>返回成功。</li></ol><h3 id="单链表的删除"><a href="#单链表的删除" class="headerlink" title="单链表的删除"></a>单链表的删除</h3><p>单链表第i个数据删除结点的算法思路：</p><ol><li>声明一指针p指向链表头指针，初始化j从1开始；</li><li>当j&lt;i时，就遍历链表，让p的指针向后移动，不断指向下一个结点，j累加1；</li><li>若到链表末尾p为空，则说明第i个结点不存在；</li><li>否则查找成功，将欲删除的结点p-&gt;next赋值给q；</li><li>单链表的删除标准语句p-&gt;next = q-&gt;next;</li><li>将q结点中的数据赋值给e，作为返回；</li><li>释放q结点。</li><li>返回成功。</li></ol><h2 id="单链表的整表创建"><a href="#单链表的整表创建" class="headerlink" title="单链表的整表创建"></a>单链表的整表创建</h2><p>头插法</p><p>尾插法</p><h2 id="单链表的整表删除"><a href="#单链表的整表删除" class="headerlink" title="单链表的整表删除"></a>单链表的整表删除</h2><p>单链表整表删除的算法思路如下：</p><ol><li>声明一节点p和q；</li><li>将第一个结点赋值给p；</li><li>循环：<ul><li>将下一个结点赋值给q；</li><li>释放p；</li><li>将q赋值给p。</li></ul></li></ol><h2 id="单链表结构与顺序存储结构优缺点"><a href="#单链表结构与顺序存储结构优缺点" class="headerlink" title="单链表结构与顺序存储结构优缺点"></a>单链表结构与顺序存储结构优缺点</h2><p>存储分配方式</p><ul><li>顺序存储结构用一段连续的存储单元依次存储线性表的数据元素。</li><li>单链表采用链式存储结构，用一组任意的存储单元存放线性表中的元素。</li></ul><p>时间性能</p><ul><li>查找 顺序存储结构O(1) 单链表O(n)</li><li>插入和删除 顺序存储结构需要平均移动表长一半的元素，时间为O(n) 单链表在显出某位置的指针后，插入和删除时间仅为O(1)</li></ul><p>空间性能</p><ul><li>顺序存储结构需要预分配存储空间，分大了，浪费，分小了易发生上溢。</li><li>单链表不需要分配存储空间，只要有就可以分配，元素个数也不受限制。</li></ul><h2 id="静态链表"><a href="#静态链表" class="headerlink" title="静态链表"></a>静态链表</h2><p>用数组描述的链表叫做静态链表，这种描述方法还有起名叫做游标实现法。</p><p>静态链表优缺点</p><p>优点：在插入和删除操作时，只需要修改游标，不需要移动元素，从而改进了顺序存储结构中的插入和删除操作需要移动大量元素的缺点。</p><p>缺点：</p><ul><li>没有解决连续存储分配带来的表长难以确定的问题。</li><li>失去了顺序存储结构随机性存取的特性。</li></ul><h2 id="循环链表"><a href="#循环链表" class="headerlink" title="循环链表"></a>循环链表</h2><p>将单链表中终端节点的指针端由空指针改为指向头结点，就使整个单链表形成一个环，这种头尾相接的单链表称为单循环链表，简称循环链表(circular linked list)。</p><h2 id="双向链表"><a href="#双向链表" class="headerlink" title="双向链表"></a>双向链表</h2><p>双向链表(double linked list)是在单链表的每个结点中，再设置一个指向其前驱结点的指针域。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;第3章-线性表&quot;&gt;&lt;a href=&quot;#第3章-线性表&quot; class=&quot;headerlink&quot; title=&quot;第3章 线性表&quot;&gt;&lt;/a&gt;第3章 线性表&lt;/h1&gt;&lt;p&gt;线性表：零个或多个数据元素的有限序列。&lt;/p&gt;
&lt;h2 id=&quot;线性表的定义&quot;&gt;&lt;a href=&quot;#
      
    
    </summary>
    
      <category term="《大话数据结构》" scheme="http://chanceli.com/categories/%E3%80%8A%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E3%80%8B/"/>
    
    
      <category term="学习笔记" scheme="http://chanceli.com/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title>《大话数据结构》一</title>
    <link href="http://chanceli.com/DataStructurePart1/"/>
    <id>http://chanceli.com/DataStructurePart1/</id>
    <published>2019-03-05T15:41:00.000Z</published>
    <updated>2021-03-14T13:39:20.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="第1章-数据结构绪论"><a href="#第1章-数据结构绪论" class="headerlink" title="第1章 数据结构绪论"></a>第1章 数据结构绪论</h1><ul><li>数据：是描述客观事物的符号，是计算机中可以操作的对象，是能被计算机识别，并输入给计算机处理的符号集合。</li><li>数据元素： 是组成数据的、有一定意义的基本单位，在计算机中通常作为整体处理。也被称为记录。</li><li>数据项：一个数据元素可以由若干个数据项组成。数据项是数据不可分割的最小单位。</li><li>数据对象：是性质相同的数据元素的集合，是数据的子集。</li><li>数据结构：是相互之间存在一种或多种特定关系的数据元素的集合。</li></ul><h2 id="逻辑结构与物理结构"><a href="#逻辑结构与物理结构" class="headerlink" title="逻辑结构与物理结构"></a>逻辑结构与物理结构</h2><h4 id="1-逻辑结构：是指数据对象中数据元素之间的相互关系。分为以下4种："><a href="#1-逻辑结构：是指数据对象中数据元素之间的相互关系。分为以下4种：" class="headerlink" title="1. 逻辑结构：是指数据对象中数据元素之间的相互关系。分为以下4种："></a>1. 逻辑结构：是指数据对象中数据元素之间的相互关系。分为以下4种：</h4><ul><li>集合机构</li><li>线性结构</li><li>树形结构</li><li>图形结构</li></ul><h4 id="2-物理结构：是指数据的逻辑结构在计算机中的存储形式。"><a href="#2-物理结构：是指数据的逻辑结构在计算机中的存储形式。" class="headerlink" title="2. 物理结构：是指数据的逻辑结构在计算机中的存储形式。"></a>2. 物理结构：是指数据的逻辑结构在计算机中的存储形式。</h4><ul><li>顺序存储结构：是把数据元素放在地址连续的存储单元里，其数据间的逻辑关系和物理关系是一致的。</li><li>链式存储结构：是把数据元素存放在任意的存储单元里，这组单元可以是连续的，也可以是不连续的。</li></ul><h2 id="抽象数据类型"><a href="#抽象数据类型" class="headerlink" title="抽象数据类型"></a>抽象数据类型</h2><p>数据类型：是指一组性质相同的值得集合及定义在此集合上的一些操作的总称。<br>C语言中，按照取值的不同，可以分为两类：</p><ul><li>原子类型：是不可以再分解的基本类型，包括整型、实型、字符型等。</li><li>结构类型：由若干个类型组合而成，是可以再分解的。例如，整型数组是由若干整型数组组成的。</li></ul><p>抽象数据类型（Abstract Data Type, ADT）：是指一个数学模型及定义在该模型上的一组操作。</p><h1 id="第2章-算法"><a href="#第2章-算法" class="headerlink" title="第2章 算法"></a>第2章 算法</h1><p>算法是解决特定问题求解步骤的描述，在计算机中表现为指令的有序序列，并且每条指令表示一个或多个操作。</p><h2 id="算法的特性"><a href="#算法的特性" class="headerlink" title="算法的特性"></a>算法的特性</h2><ol><li>输入输出</li><li>有穷性</li><li>确定性</li><li>可行性</li></ol><h2 id="算法设计的要求"><a href="#算法设计的要求" class="headerlink" title="算法设计的要求"></a>算法设计的要求</h2><ol><li>正确性</li><li>可读性</li><li>健壮性</li><li>时间效率高和存储量低</li></ol><h2 id="算法效率的度量方法"><a href="#算法效率的度量方法" class="headerlink" title="算法效率的度量方法"></a>算法效率的度量方法</h2><ol><li>事后统计方法（不科学、不准确）</li><li>事前分析估算方法</li></ol><h2 id="函数的渐近式增长"><a href="#函数的渐近式增长" class="headerlink" title="函数的渐近式增长"></a>函数的渐近式增长</h2><p>函数的渐近增长：给定两个函数 f(n) 和 g(n), 如果存在一个整数N，使得对于所有的 n &gt; N, f(n) 总是比 g(n) 大，那么，我们说 f(n) 的增长渐近快于 g(n)。</p><h2 id="算法时间复杂度"><a href="#算法时间复杂度" class="headerlink" title="算法时间复杂度"></a>算法时间复杂度</h2><p>在进行算法分析时，语句总的执行次数 T(n) 是关于问题规模 n 的函数，进而分析 T(n) 随 n 的变化情况并确定 T(n) 的数量级。算法的时间复杂度，也就是算法的时间度量，记作：T(n) = O(f(n))。它表示随问题规模 n 的增大，算法执行时间的增长率和 f(n) 的增长率相同，称作算法的渐近时间复杂度，简称为时间复杂度。其中 f(n) 是问题规模 n 的某个函数。</p><h2 id="推导大-O-阶方法"><a href="#推导大-O-阶方法" class="headerlink" title="推导大 O 阶方法"></a>推导大 O 阶方法</h2><ol><li>用常数 1 取代运行时间中的所有加法常数。</li><li>在修改后的运行次数函数中，只保留最高阶项。</li><li>如果最高阶存在且不是 1，则去除与这个项相乘的常数。<br>得到的结果就是大 O 阶。</li></ol><h2 id="常见的时间复杂度"><a href="#常见的时间复杂度" class="headerlink" title="常见的时间复杂度"></a>常见的时间复杂度</h2><p>O(1) &lt; O(logn) &lt; O(n) &lt; O(nlogn) &lt; O(n^2) &lt; O(n^3) &lt; O(2^n) &lt; O(n!) &lt; O(n^n)</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;第1章-数据结构绪论&quot;&gt;&lt;a href=&quot;#第1章-数据结构绪论&quot; class=&quot;headerlink&quot; title=&quot;第1章 数据结构绪论&quot;&gt;&lt;/a&gt;第1章 数据结构绪论&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;数据：是描述客观事物的符号，是计算机中可以操作的对象，是能被计算
      
    
    </summary>
    
      <category term="《大话数据结构》" scheme="http://chanceli.com/categories/%E3%80%8A%E5%A4%A7%E8%AF%9D%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E3%80%8B/"/>
    
    
      <category term="学习笔记" scheme="http://chanceli.com/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
  </entry>
  
  <entry>
    <title>【iOS 开发】iOS 代码规范</title>
    <link href="http://chanceli.com/CodingGuidelines/"/>
    <id>http://chanceli.com/CodingGuidelines/</id>
    <published>2019-03-04T16:00:00.000Z</published>
    <updated>2021-03-14T13:45:46.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="原则"><a href="#原则" class="headerlink" title="原则"></a>原则</h1><ol><li>长的、描述性的方法和变量命名是好的命名方式。不要使用简写，除非是一些大家都知道的场景比如 VIP。不要使用 bgView，推荐使用 backgroundView。</li><li>见名知意。含义清楚，做好不加注释代码自我表述能力强。（前提是代码足够规范）</li><li>不要过分追求技巧，降低代码可读性。</li><li>删除没必要的代码。比如我们新建一个控制器，里面会有一些不会用到的代码，或者注释起来的代码，如果这些代码不需要，那就删除它，留着偷懒吗？下次需要自己手写。</li><li>在方法内部不要重复计算某个值，适当的情况下可以将计算结果缓存起来。</li><li>尽量减少单例的使用。</li><li>提供一个统一的数据管理入口，不管是 MVC、MVVM、MVP 模块内提供一个统一的数据管理入口会使得代码变得更容易管理和维护。</li><li>除了 .m 文件中方法，其他的地方”{“不需要另起一行。<figure class="highlight plain"><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">- (void)getGooodsList</span><br><span class="line">&#123;</span><br><span class="line">    // ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">- (void)doHomework</span><br><span class="line">&#123;</span><br><span class="line">    if (self.hungry) &#123;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line">    if (self.thirsty) &#123;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line">    if (self.tired) &#123;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line">    papapa.then.over;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><h1 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h1><ol><li>一个变量最好只有一个作用，切勿为了节省代码行数，觉得一个变量可以做多个用途。（单一原则）</li><li>方法内部如果有局部变量，那么局部变量应该靠近在使用的地方，而不是全部在顶部声明全部的局部变量。</li></ol><h1 id="运算符"><a href="#运算符" class="headerlink" title="运算符"></a>运算符</h1><ol><li>1元运算符和变量之间不需要空格。例如：<code>++n</code></li><li>2元运算符与变量之间需要空格隔开。例如： <code>containerWidth = 0.3 * Screen_Width</code><br>当有多个运算符的时候需要使用括号来明确正确的顺序，可读性较好。例如：<code>2 &lt;&lt; (1 + 2 * 3 - 4)</code></li></ol><h1 id="条件表达式"><a href="#条件表达式" class="headerlink" title="条件表达式"></a>条件表达式</h1><ol><li><p>当有条件过多、过长的时候需要换行，为了代码看起来整齐些。</p><figure class="highlight plain"><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">//good</span><br><span class="line">if (condition1() &amp;&amp; </span><br><span class="line">    condition2() &amp;&amp; </span><br><span class="line">    condition3() &amp;&amp; </span><br><span class="line">    condition4()) &#123;</span><br><span class="line">  // Do something</span><br><span class="line">&#125;</span><br><span class="line">//bad</span><br><span class="line">if (condition1() &amp;&amp; condition2() &amp;&amp; condition3() &amp;&amp; condition4(）) &#123; // Do something &#125;</span><br></pre></td></tr></table></figure></li><li><p>在一个代码块里面有个可能的情况时善于使用 return 来结束异常的情况。</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">- (void)doHomework</span><br><span class="line">&#123;</span><br><span class="line">    if (self.hungry) &#123;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line">    if (self.thirsty) &#123;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line">    if (self.tired) &#123;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line">    papapa.then.over;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>每个分支的实现都必须使用 {} 包含。</p><figure class="highlight plain"><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">// bad</span><br><span class="line">if (self.hungry) self.eat() </span><br><span class="line">// good</span><br><span class="line">if (self.hungry) &#123;</span><br><span class="line">    self.eat()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>条件判断的时候应该是变量在左，条件在右。 if ( currentCursor == 2 ) { //… }</p></li><li>switch 语句后面的每个分支都需要用大括号括起来。</li><li>switch 语句后面的 default 分支必须存在，除非是在对枚举进行 switch。<figure class="highlight plain"><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">switch (menuType) &#123;  </span><br><span class="line">  case menuTypeLeft: &#123;</span><br><span class="line">    // ...  </span><br><span class="line">    break; </span><br><span class="line">   &#125;</span><br><span class="line">  case menuTypeRight: &#123;</span><br><span class="line">    // ...  </span><br><span class="line">    break; </span><br><span class="line">  &#125;</span><br><span class="line">  case menuTypeTop: &#123;</span><br><span class="line">    // ...  </span><br><span class="line">    break; </span><br><span class="line">  &#125;</span><br><span class="line">  case menuTypeBottom: &#123;</span><br><span class="line">    // ...  </span><br><span class="line">    break; </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><h1 id="类名"><a href="#类名" class="headerlink" title="类名"></a>类名</h1><ol><li>大写驼峰式命名。每个单词首字母大写。比如「申请记录控制器」ApplyRecordsViewController</li><li>每个类型的命名以该类型结尾。</li></ol><ul><li>ViewController：使用 ViewController 结尾。例子：ApplyRecordsViewController</li><li>View：使用 View 结尾。例子：分界线：boundaryView</li><li>NSArray：使用 s 结尾。比如商品分类数据源。categories</li><li>UITableViewCell：使用 Cell 结尾。比如 MyProfileCell</li><li>Protocol：使用 Delegate 或者 Datasource 结尾。比如 XQScanViewDelegate</li><li>Tool：工具类</li><li>代理类：Delegate</li><li>Service 类：Service</li></ul><h1 id="类的注释"><a href="#类的注释" class="headerlink" title="类的注释"></a>类的注释</h1><p>有时候我们需要为我们创建的类设置一些注释。我们可以在类的下面添加。</p><h1 id="枚举"><a href="#枚举" class="headerlink" title="枚举"></a>枚举</h1><p>枚举的命名和类的命名相近。<br><figure class="highlight plain"><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">typedef NS_ENUM(NSInteger, UIControlContentVerticalAlignment) &#123;</span><br><span class="line">    UIControlContentVerticalAlignmentCenter  = 0,</span><br><span class="line">    UIControlContentVerticalAlignmentTop     = 1,</span><br><span class="line">    UIControlContentVerticalAlignmentBottom  = 2,</span><br><span class="line">    UIControlContentVerticalAlignmentFill    = 3,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></p><h1 id="宏"><a href="#宏" class="headerlink" title="宏"></a>宏</h1><ol><li>全部大写，单词与单词之间用 _ 连接。</li><li>以 K 开头。后面遵循大写驼峰命名。「不带参数」<figure class="highlight plain"><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">#define HOME_PAGE_DID_SCROLL @&quot;com.xq.home.page.tableview.did.scroll&quot;</span><br><span class="line">#define KHomePageDidScroll @&quot;com.xq.home.page.tableview.did.scroll&quot;</span><br></pre></td></tr></table></figure></li></ol><h1 id="属性"><a href="#属性" class="headerlink" title="属性"></a>属性</h1><p>书写规则，基本上就是 @property 之后空一格，括号，里面的 线程修饰词、内存修饰词、读写修饰词，空一格 类 对象名称 根据不同的场景选择合适的修饰符。<br><figure class="highlight plain"><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">@property (nonatomic, strong) UITableView *tableView;</span><br><span class="line">@property (nonatomic, assign, readonly) BOOL loading;   </span><br><span class="line">@property (nonatomic, weak) id&lt;#delegate#&gt; delegate;</span><br><span class="line">@property (nonatomic, copy) &lt;#returnType#&gt; (^&lt;#Block#&gt;)(&lt;#parType#&gt;);</span><br></pre></td></tr></table></figure></p><h1 id="单例"><a href="#单例" class="headerlink" title="单例"></a>单例</h1><p>单例适合全局管理状态或者事件的场景。一旦创建，对象的指针保存在静态区，单例对象在堆内存中分配的内存空间只有程序销毁的时候才会释放。基于这种特点，那么我们类似 UIApplication 对象，需要全局访问唯一一个对象的情况才适合单例，或者访问频次较高的情况。我们的功能模块的生命周期肯定小于 App 的生命周期，如果多个单例对象的话，势必 App 的开销会很大，糟糕的情况系统会杀死 App。如果觉得非要用单例比较好，那么注意需要在合适的场合 tearDown 掉。</p><p>单例的使用场景概括如下：</p><ul><li>控制资源的使用，通过线程同步来控制资源的并发访问。</li><li>控制实例的产生，以达到节约资源的目的。</li><li>控制数据的共享，在不建立直接关联的条件下，让多个不相关的进程或线程之间实现通信。</li></ul><h1 id="私有变量"><a href="#私有变量" class="headerlink" title="私有变量"></a>私有变量</h1><p>推荐以<code>_</code>开头，写在 .m 文件中。例如<code>NSString * _somePrivateVariable</code></p><h2 id="代理方法"><a href="#代理方法" class="headerlink" title="代理方法"></a>代理方法</h2><ol><li>类的实例必须作为方法的参数之一。</li><li>对于一些连续的状态的，可以加一些 will（将要）、did（已经）</li><li>以类的名称开头<figure class="highlight plain"><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">- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;</span><br><span class="line"></span><br><span class="line">- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;</span><br></pre></td></tr></table></figure></li></ol><h2 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h2><ol><li>方法与方法之间间隔一行</li><li>大量的方法尽量要以组的形式放在一起，比如生命周期函数、公有方法、私有方法、setter &amp;&amp; getter、代理方法..</li><li>方法最后面的括号需要另起一行。遵循 Apple 的规范</li><li>对于其他场景的括号，括号不需要单独换行。比如 if 后面的括号。</li><li>如果方法参数过多过长，建议多行书写。用冒号进行对齐。</li><li>一个方法内的代码最好保持在50行以内，一般经验来看如果一个方法里面的代码行数过多，代码的阅读体验就很差（别问为什么，做过重构代码行数很长的人都有类似的心情）</li><li>一个函数只做一个事情，做到单一原则。所有的类、方法设计好后就可以类似搭积木一样实现一个系统。</li><li>对于有返回值的函数，且函数内有分支情况。确保每个分支都有返回值。</li><li>函数如果有多个参数，外部传入的参数需要检验参数的非空、数据类型的合法性，参数错误做一些措施：立即返回、断言。</li><li>多个函数如果有逻辑重复的代码，建议将重复的部分抽取出来，成为独立的函数进行调用</li><li><p>方法如果有多个参数的情况下需要注意是否需要介词和连词。很多时候在不知道如何抉择测时候思考下苹果的一些 API 的方法命名。</p><figure class="highlight plain"><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">//good</span><br><span class="line">- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name;</span><br><span class="line"></span><br><span class="line">- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;</span><br><span class="line"></span><br><span class="line">//bad</span><br><span class="line">- (instancetype)initWithAge:(NSInteger)age andName:(NSString *)name;</span><br><span class="line"></span><br><span class="line">- (void)tableView:(UITableView *)tableView :(NSIndexPath *)indexPath;</span><br></pre></td></tr></table></figure></li><li><p>.m 文件中的私有方法需要在顶部进行声明 </p></li><li>方法组之间也有个顺序问题。</li></ol><ul><li>在文件最顶部实现属性的声明、私有方法的声明（很多人省去这一步，问题不大，但是蛮多第三方的库都写了，看起来还是会很方便，建议书写）。</li><li>在生命周期的方法里面，比如 viewDidLoad 里面只做界面的添加，而不是做界面的初始化，所有的 view 初始化建议放在 getter 里面去做。往往 view 的初始化的代码长度会比较长、且一般会有多个 view 所以 getter 和 setter 一般建议放在最下面，这样子顶部就可以很清楚的看到代码的主要逻辑。</li><li>所有button、gestureRecognizer 的响应事件都放在这个区域里面，不要到处乱放。</li></ul><p>文件基本上就是<br><figure class="highlight plain"><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><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br></pre></td><td class="code"><pre><span class="line">#import &quot;ViewController.h&quot;</span><br><span class="line"></span><br><span class="line">/*ViewController*/</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">/*View&amp;&amp;Util*/</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">/*model*/</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">/*NetWork InterFace*/</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">/*Vender*/</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">@interface ViewController ()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">@end</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">@implementation ViewController</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#pragma mark - life cycle</span><br><span class="line"></span><br><span class="line">- (void)viewWillAppear:(BOOL)animated</span><br><span class="line"></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">[super viewDidAppear:animated];</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">- (void)viewDidAppear:(BOOL)animated</span><br><span class="line"></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">[super viewDidAppear:animated];</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">- (void)viewDidLoad</span><br><span class="line"></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">[super viewDidLoad];</span><br><span class="line"></span><br><span class="line">self.title = @&quot;标准模版&quot;;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">- (void)viewWillDisappear:(BOOL)animated</span><br><span class="line"></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">[super viewDidAppear:animated];</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">- (void)viewDidDisappear:(BOOL)animated</span><br><span class="line"></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">[super viewDidAppear:animated];</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">- (void)dealloc</span><br><span class="line"></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">NSLog(@&quot;%s&quot;,__func__);</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#pragma mark - public Method</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#pragma mark - private method</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#pragma mark - event response</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#pragma mark - UITableViewDelegate</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#pragma mark - UITableViewDataSource</span><br><span class="line"></span><br><span class="line">//...(多个代理方法依次往下写)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#pragma mark - getters and setters</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">@end</span><br></pre></td></tr></table></figure></p><h2 id="图片资源"><a href="#图片资源" class="headerlink" title="图片资源"></a>图片资源</h2><ol><li>单个文件的命名 文件资源的命名也需要一定的规范，形式为：<code>功能模块名_类别_功能_状态</code><br>@nx.png <a href="mailto:Setting_Button_search_selected@2x.png" target="_blank" rel="noopener">Setting_Button_search_selected@2x.png</a>、<a href="mailto:Setting_Button_search_selected@3x.png" target="_blank" rel="noopener">Setting_Button_search_selected@3x.png</a> <a href="mailto:Setting_Button_search_unselected@2x.png" target="_blank" rel="noopener">Setting_Button_search_unselected@2x.png</a>、<a href="mailto:Setting_Button_search_unselected@3x.png" target="_blank" rel="noopener">Setting_Button_search_unselected@3x.png</a></li><li>资源的文件夹命名 最好也参考 App 按照功能模块建立对应的实体文件夹目录，最后到对应的目录下添加相应的资源文件。</li></ol><h2 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h2><ol><li>对于类的注释写在当前类文件的顶部</li><li>对于属性的注释需要写在属性后面的地方。 <code>//**&lt;userId*/</code></li><li>对于 .h 文件中方法的注释，一律按快捷键 <code>command+option+/</code>。三个快捷键解决。按需在旁边对方法进行说明解释、返回值、参数的说明和解释 </li><li>对于 .m 文件中的方法的注释，在方法的旁边添加 //。 </li><li>注释符和注释内容需要间隔一个空格。 例如： // fetch goods list</li></ol><h2 id="版本规范"><a href="#版本规范" class="headerlink" title="版本规范"></a>版本规范</h2><p>采用 A.B.C 三位数字命名，比如：1.0.2，当有更新的情况下按照下面的依据</p><p>版本号示例<br>A.b.c属于重大内容的更新1.0.2 -&gt; 2.0.0<br>a.B.c属于小部分内容的更新1.0.2 -&gt; 1.1.1<br>a.b.C属于补丁更新1.0.2 -&gt; 1.0.3</p><p>原文链接：<a href="https://juejin.im/post/5c7c7e0cf265da2ddb298123" target="_blank" rel="noopener">有了这些你们团队的代码肯定规范</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;原则&quot;&gt;&lt;a href=&quot;#原则&quot; class=&quot;headerlink&quot; title=&quot;原则&quot;&gt;&lt;/a&gt;原则&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;长的、描述性的方法和变量命名是好的命名方式。不要使用简写，除非是一些大家都知道的场景比如 VIP。不要使用 bgView，推荐使
      
    
    </summary>
    
      <category term="代码规范" scheme="http://chanceli.com/categories/%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/"/>
    
    
      <category term="代码规范" scheme="http://chanceli.com/tags/%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/"/>
    
  </entry>
  
  <entry>
    <title>我曾七次鄙视我的灵魂（卡里.纪伯伦）</title>
    <link href="http://chanceli.com/SevenTimesHaveIDespisedMySoul/"/>
    <id>http://chanceli.com/SevenTimesHaveIDespisedMySoul/</id>
    <published>2018-12-21T10:09:00.000Z</published>
    <updated>2021-03-14T13:50:10.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>Seven times have I despised my soul:</p><p>——Kahlil Gibran</p><p>The first time when I saw her being meek that she might attain height.</p><p>The second time when I saw her limping before the crippled.</p><p>The third time when she was given to choose between the hard and the easy, and she chose the easy.</p><p>The fourth time when she committed a wrong, and comforted herself that others also commit wrong.</p><p>The fifth time when she forbore for weakness, and attributed her patience to strength.</p><p>The sixth time when she despised the ugliness of a face, and knew not that it was one of her own masks.</p><p>And the seventh time when she sang a song of praise, and deemed it a virtue.</p></blockquote><h1 id="译文"><a href="#译文" class="headerlink" title="译文"></a>译文</h1><p>我曾七次鄙视我的灵魂</p><p>——卡里.纪伯伦</p><p>第一次，当它本可进取时，却故作谦卑；</p><p>第二次，当它在空虚时，用爱欲来填充；</p><p>第三次，在困难和容易之间，它选择了容易；</p><p>第四次，它犯了错，却借由别人也会犯错来宽慰自己；</p><p>第五次，它自由软弱，却把它认为是生命的坚韧；</p><p>第六次，当它鄙夷一张丑恶的嘴脸时，却不知那正是自己面具中的一副；</p><p>第七次，它侧身于生活的污泥中，虽不甘心，却又畏首畏尾。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;Seven times have I despised my soul:&lt;/p&gt;
&lt;p&gt;——Kahlil Gibran&lt;/p&gt;
&lt;p&gt;The first time when I saw her being meek that she might a
      
    
    </summary>
    
      <category term="诗和远方" scheme="http://chanceli.com/categories/%E8%AF%97%E5%92%8C%E8%BF%9C%E6%96%B9/"/>
    
    
      <category term="诗和远方" scheme="http://chanceli.com/tags/%E8%AF%97%E5%92%8C%E8%BF%9C%E6%96%B9/"/>
    
  </entry>
  
</feed>
