<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>独奏’s BLOG</title>
        <link>https://honglu.me/</link>
        <description>道阻且长，行则将至</description>
        <lastBuildDate>Fri, 23 May 2025 00:42:18 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <copyright>All rights reserved 2025, 独奏</copyright>
        <item>
            <title><![CDATA[unraid 公网 Traefik 统一网关]]></title>
            <link>https://honglu.me/article/1231a7b4-8fbf-8028-b0fd-d6733d2c5fc3</link>
            <guid>https://honglu.me/article/1231a7b4-8fbf-8028-b0fd-d6733d2c5fc3</guid>
            <pubDate>Fri, 18 Oct 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[unraid 开启 ipv6 之后会把内网服务全部暴露到公网，存在安全隐患，这里使用防火墙来增加安全性，以及通过 Traefik 统一网关对外提供服务。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1231a7b48fbf8028b0fdd6733d2c5fc3"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1231a7b48fbf81038ea9db42e04d3b68" data-id="1231a7b48fbf81038ea9db42e04d3b68"><span><div id="1231a7b48fbf81038ea9db42e04d3b68" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1231a7b48fbf81038ea9db42e04d3b68" title="背景"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">背景</span></span></h3><div class="notion-text notion-block-1231a7b48fbf802085cee8ba0f4af9f9">最近玩 pt 使用 qbittorrent 想要增加上传，由于家中是移动宽带无公网 v4，且 qbittorrent 安装在 unraid 上面，因此只能通过公网 ipv6 来增加 bt 上传。</div><div class="notion-text notion-block-1231a7b48fbf81db8e0dd9e2dd36eadc">家中的网络是<code class="notion-inline-code">光猫 → 路由 → unraid</code>，光猫与路由桥接，路由负责拨号以及 ipv4 dhcp。</div><div class="notion-text notion-block-1231a7b48fbf8195a3cce63439540d7f">之前 unraid 一直是默认关闭 ipv6，把需要暴露到外网的服务通过 <a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/ehang-io/nps">NPS</a> 做端口转发，因此不存在服务暴露在公网的问题，但是开启 ipv6 之后，由于 unraid 获取到公网的 ipv6 地址，意味着上面的所有服务都会暴露到公网，存在一定的安全风险。</div><div class="notion-text notion-block-1231a7b48fbf802485f4ec11d962fafe">综上，需要通过防火墙来屏蔽公网的访问，同时把需要暴露的服务进行放行。这里使用 Traefik 来实现一个统一网关，由网关来对外提供服务。</div><div class="notion-blank notion-block-1231a7b48fbf80d29472d1b99db1ffa1"> </div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1231a7b48fbf8021a9f8d6a289410309" data-id="1231a7b48fbf8021a9f8d6a289410309"><span><div id="1231a7b48fbf8021a9f8d6a289410309" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1231a7b48fbf8021a9f8d6a289410309" title="方案"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">方案</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1231a7b48fbf80b6abd6df99a78751b9" data-id="1231a7b48fbf80b6abd6df99a78751b9"><span><div id="1231a7b48fbf80b6abd6df99a78751b9" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1231a7b48fbf80b6abd6df99a78751b9" title="1. unraid 开启 ipv6"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">1. unraid 开启 ipv6</span></span></h4><div class="notion-text notion-block-1231a7b48fbf80e6a422fff07d8b95ad">首先需要路由开启 ipv6，让下面的设备能够获取到公网 ipv6 地址，这里不同路由设置方式不同，不详细说明了。</div><div class="notion-text notion-block-1231a7b48fbf8062a3defa0a1dda533e">unraid 开启 ipv6 相对简单，首先关闭 docker 和虚拟机服务，在【设置】→【网络设置】→【网络协议】里面选择【ipv4+ipv6】即可。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1231a7b48fbf804db3a0fa9032a5dc7c"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fa253e2c0-c392-4d5d-9882-55bc928a1eb6%2F752716a5-bb27-4942-8fe3-cf6b53cf7d52%2Fimage.png?table=block&amp;id=1231a7b4-8fbf-804d-b3a0-fa9032a5dc7c&amp;t=1231a7b4-8fbf-804d-b3a0-fa9032a5dc7c&amp;width=707.9921875&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-1231a7b48fbf80b7a4c1dbd7642748b9"> </div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1231a7b48fbf80d9acabc0d35e26ba0c" data-id="1231a7b48fbf80d9acabc0d35e26ba0c"><span><div id="1231a7b48fbf80d9acabc0d35e26ba0c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1231a7b48fbf80d9acabc0d35e26ba0c" title="2. 设置防火墙"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">2. 设置防火墙</span></span></h4><div class="notion-text notion-block-1231a7b48fbf80ee9f84d1bee243ff83">开启 ipv6 后，即可发现已经有了 ipv6 公网地址，通过这个地址，再加上你的服务端口（如：http://[2409:8a:xxx:1]:8080），即可在公网直接访问你的服务了。</div><div class="notion-text notion-block-1231a7b48fbf80b4bb9dd37caf8024ef">这样显然是不安全的，我们并不希望所有服务都暴露在公网上。因此需要增加防火墙来防止公网上的访问。</div><div class="notion-text notion-block-1231a7b48fbf8086a2bde445e20f0427">这里需要先安装【user scripts】插件，安装完之后添加一个 script，设置为【At First Array Start Only】即可。第一次设置需要手动执行一次，可以通过<code class="notion-inline-code">ip6tables --list</code>查看规则生效情况。</div><div class="notion-blank notion-block-1231a7b48fbf80539036eabd430597e3"> </div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1231a7b48fbf8012880df03995e1735f" data-id="1231a7b48fbf8012880df03995e1735f"><span><div id="1231a7b48fbf8012880df03995e1735f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1231a7b48fbf8012880df03995e1735f" title="3. 域名动态解析"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">3. 域名动态解析</span></span></h4><div class="notion-text notion-block-1231a7b48fbf801a9728ecba3f3a94c4">配置 Traefik 网关之前，我们需要安装 DDNS-go 服务，能够把域名动态解析到 unraid 的公网 ipv6 上面，这里通过后台配置启用 ipv6 解析即可。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1231a7b48fbf8049848be3c87ca501c1"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fa253e2c0-c392-4d5d-9882-55bc928a1eb6%2F60ef85b6-3c79-4871-8baa-156c9a06f56c%2Fimage.png?table=block&amp;id=1231a7b4-8fbf-8049-848b-e3c87ca501c1&amp;t=1231a7b4-8fbf-8049-848b-e3c87ca501c1&amp;width=707.984375&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1231a7b48fbf80e68597dd56be6d5dc6" data-id="1231a7b48fbf80e68597dd56be6d5dc6"><span><div id="1231a7b48fbf80e68597dd56be6d5dc6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1231a7b48fbf80e68597dd56be6d5dc6" title="4. Traefik 统一网关"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">4. Traefik 统一网关</span></span></h4><div class="notion-text notion-block-1231a7b48fbf80ae9b61e3c664a12b96">设置好防火墙之后会发现所有服务都无法通过公网 ipv6 地址来访问了。这里我希望能通过域名来访问 alist 等部分服务，因此这里加入了 Traefik 网关来对外提供 http 服务。</div><div class="notion-blank notion-block-1231a7b48fbf80e29278d29b22fa991a"> </div><div class="notion-text notion-block-1231a7b48fbf8015b546f120fd370805">首先安装 Traefik，在 unraid 的【应用】中心直接下载 Traefik 即可。Traefik 提供不同的配置方式，网络上大部分都是通过 docker label 来进行配置，但是这样需要修改现有的所有 docker 容器，因此这里我是通过 yml config file 的方式来进行配置。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1231a7b48fbf8088987de45eac4bf022"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fa253e2c0-c392-4d5d-9882-55bc928a1eb6%2Fe0f4abce-4472-4531-bc49-b3f7350dd63e%2Fimage.png?table=block&amp;id=1231a7b4-8fbf-8088-987d-e45eac4bf022&amp;t=1231a7b4-8fbf-8088-987d-e45eac4bf022&amp;width=2272&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-1231a7b48fbf80dd9040dd7a7d286310">安装完之后需要创建如下文件：</div><details class="notion-toggle notion-block-1231a7b48fbf80afa87dd5f1bb32239b"><summary>创建 <code class="notion-inline-code">acme.json</code></summary><div></div></details><details class="notion-toggle notion-block-1231a7b48fbf80d4afc6c1f42ee1e94c"><summary>在 <code class="notion-inline-code">/mnt/user/appdata/traefik</code> 目录下新建 traefik.yml 文件</summary><div></div></details><details class="notion-toggle notion-block-1231a7b48fbf8026877ef994300b697e"><summary>创建反向代理服务：在 <code class="notion-inline-code">/mnt/user/appdata/traefik/dynamic-conf/</code> 下创建 <code class="notion-inline-code">alist.yml</code> </summary><div></div></details><div class="notion-blank notion-block-1231a7b48fbf80bf8b23e0d7fc6e6934"> </div><div class="notion-text notion-block-1231a7b48fbf805fae80d82c9de35e20">上述文件修改好之后，开启 Traefik docker 服务即可，这个时候可以通过  <code class="notion-inline-code">https://alist.YOURDOMAIN.COM:HTTP_PORT</code> 来访问 unraid 上面的 alist 服务了。同时也可以通过后台来查看 Traefik 的链路。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1231a7b48fbf804099e6f7b6efd60418"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fa253e2c0-c392-4d5d-9882-55bc928a1eb6%2F54fdca3b-ab35-4cd2-a59f-e41d3fb05478%2Fimage.png?table=block&amp;id=1231a7b4-8fbf-8040-99e6-f7b6efd60418&amp;t=1231a7b4-8fbf-8040-99e6-f7b6efd60418&amp;width=2696&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[百度WormHole漏洞分析]]></title>
            <link>https://honglu.me/article/91759555-3865-4f3a-95fa-977ca7473059</link>
            <guid>https://honglu.me/article/91759555-3865-4f3a-95fa-977ca7473059</guid>
            <pubDate>Wed, 04 Nov 2015 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-9175955538654f3a95fa977ca7473059"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-text notion-block-759e1748ecce46f69ae894c1295bf97b">还记得9月份 Xcode 曝 XcodeGhost 漏洞的时候，网络上一片哗然，大家纷纷叹息一直以安全性著称的 iOS 系统也不再安全了，像微信、网易云音乐等安装量过亿的 APP 纷纷中枪。接着是各种漏洞利用的分析，分析一轮下来，最令人咋舌的就是可以通过这个漏洞来获取用户 appleID 了。</div><div class="notion-text notion-block-dd9d85adb36f4505bb34f68bb5f814ff">然而事过两个月之后百度 Android APP 被爆出了 WormHole 虫洞漏洞，与其说这是一个漏洞，倒不如说这是百度自己留下的一个后门。百度全系APP连同使用了百度地图的APP纷纷中枪，影响用户同样过亿。该后门之流氓程度与XcodeGhost相比有过之而无不及，包括静默下载任意文件、后台上传用户文件等各种流氓行径。</div><div class="notion-text notion-block-d3ec768b70314a38a171b9c298b0e643">下面就来分析一下 WormHole 漏洞吧</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-9b5f1dcb11314983a4ce0c19094a1068" data-id="9b5f1dcb11314983a4ce0c19094a1068"><span><div id="9b5f1dcb11314983a4ce0c19094a1068" class="notion-header-anchor"></div><a class="notion-hash-link" href="#9b5f1dcb11314983a4ce0c19094a1068" title="1. 反编译"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">1. 反编译</span></span></h3><div class="notion-text notion-block-a9427535b25544038b1ff9e06d43bdd6">首先我从网上公开资料得知在百度地图8.7.0上存在该漏洞，所以我就从『安卓市场』找到了这个版本的<a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://apk.hiapk.com/appinfo/com.baidu.BaiduMap/688">百度地图apk</a>，下载下来之后解压得到 classes.dex 文件，接下来需要反编译这个 dex 文件。</div><div class="notion-text notion-block-16037ce7e8384dbe9828f96274de18df">使用 <a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/pxb1988/dex2jar">dex2jar</a> 反编译这个 dex 文件之后，得到了反编译之后的 jar 文件。</div><div class="notion-text notion-block-c63ca8383b6b4ed9b8608ef717509835">接下来使用 <a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/java-decompiler/jd-gui">JD-GUI</a> 就可以查看反编译后的 jar 的代码了。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-4f7f1eac8ce145da807fe302bcb667bd" data-id="4f7f1eac8ce145da807fe302bcb667bd"><span><div id="4f7f1eac8ce145da807fe302bcb667bd" class="notion-header-anchor"></div><a class="notion-hash-link" href="#4f7f1eac8ce145da807fe302bcb667bd" title="2. 代码分析"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">2. 代码分析</span></span></h3><div class="notion-text notion-block-863241a8b7354fc7bb195c127160aa4f">查看 jar 代码之后发现百度其实作了代码混淆工作，然并卵。</div><div class="notion-text notion-block-e3f3ae612e2442cba4da395ed6b0994c">从公开资料得知 APP 开了一个 http 服务器，监听 40310/6259 端口，而且 APP 中存在<code class="notion-inline-code">geolocation</code>等字符串，通过查找功能，我找到了相关的代码位置。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-9ff0dac29ac7453b8c9996888c28d425"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img src="http://blog.qiniu.lololo.store/image/15/11/04/WormHole.png?spaceId=a253e2c0-c392-4d5d-9882-55bc928a1eb6&amp;t=9ff0dac2-9ac7-453b-8c99-96888c28d425" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-a5e42f9ad4c64c9686c3eceee7841240">WormHole</div><div class="notion-text notion-block-ba59e2a68b964eacba6fcd678792ff94">可见文件是在 moplus 的包下的，这个 moplus 其实就是该后门的根源。</div><div class="notion-text notion-block-6a4148efe8bf45aabac671806e9eb260">通过这个 <code class="notion-inline-code">e.class</code> 我们可以看到，百度做了一个动态调用，通过接口名映射到对应的类里面去，再看这几个类名，惊出一身冷汗，各种权限都有相应的功能接口。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-8f936de4c9a245bfa16a1a8459158b29" data-id="8f936de4c9a245bfa16a1a8459158b29"><span><div id="8f936de4c9a245bfa16a1a8459158b29" class="notion-header-anchor"></div><a class="notion-hash-link" href="#8f936de4c9a245bfa16a1a8459158b29" title="3. 后续"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">3. 后续</span></span></h3><div class="notion-text notion-block-632e988d069142689add41cba2b55d83">网络上该漏洞利用的相关视频已经不少了，包括静默下载/上传、增加联系人等，都有相关视频。百度在10月28日官方发表声明称已经修复该漏洞，现在是11月4日，我写了个脚本测试内网环境下依然存在该漏洞的机器数目，扫描了255*3个IP，发现了13台机器依然存在该后门。可能会觉得目前存在该后门设备并不算太多，但是公司觉大部分人都是 iPhone 用户，相比于 Android 用户来说，这个数目应该已经算不少了。</div><div class="notion-text notion-block-635fffe2ae37413a9c93606b4623e78c">目前已经有<a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://drops.wooyun.org/papers/10180">文章</a>披露百度地图确实在利用此后门在获取用户隐私信息，而且该文章指出这个 moplus 应该是为百度统计服务的。类似获取用户隐私的不光百度一家，包括搜狗、高德地图同样也在通过类似方式获取用户隐私数据。看来对于安卓平台来说，各大厂商暗地获取用户隐私早已成为大家的共识。</div><div class="notion-text notion-block-2cb2189382a54e6cac6183ee8245991b">另外今天乌云上面又公布了一个 NanoHttpd 的一个<a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://www.wooyun.org/bugs/wooyun-2015-0151955">相关漏洞</a>，看来是屋漏偏逢连夜雨啊，连工具都出问题了~</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-3a4df4ac11dd4f2382a3af4665858aaf"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img src="http://blog.qiniu.lololo.store/image/15/11/04/20151103065149470481.png?spaceId=a253e2c0-c392-4d5d-9882-55bc928a1eb6&amp;t=3a4df4ac-11dd-4f23-82a3-af4665858aaf" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-60a13c694fb7486ca3140a892526c4a6"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img src="http://blog.qiniu.lololo.store/image/15/11/04/20151103065151777452.png?spaceId=a253e2c0-c392-4d5d-9882-55bc928a1eb6&amp;t=60a13c69-4fb7-486c-a314-0a892526c4a6" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-8e87290bbe69488f9a5cd0685ff95e63"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img src="http://blog.qiniu.lololo.store/image/15/11/04/20151103065203470917.png?spaceId=a253e2c0-c392-4d5d-9882-55bc928a1eb6&amp;t=8e87290b-be69-488f-9a5c-d0685ff95e63" alt="notion image" loading="lazy" decoding="async"/></div></figure><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-c75194f401784913920151aaec696ac4" data-id="c75194f401784913920151aaec696ac4"><span><div id="c75194f401784913920151aaec696ac4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#c75194f401784913920151aaec696ac4" title="参考"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">参考</span></span></h3><div class="notion-text notion-block-4fe4466075824a97886a1371d573485e"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://www.apple.com/cn/xcodeghost/">有关 XcodeGhost 的问题和解答</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://drops.wooyun.org/papers/10061">比葫芦娃还可怕的百度全系APP SDK漏洞 - WormHole虫洞漏洞分析报告</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://drops.wooyun.org/papers/10180">WormHole分析第二弹</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://blog.trendmicro.com/trendlabs-security-intelligence/setting-the-record-straight-on-moplus-sdk-and-the-wormhole-vulnerability/">Setting the Record Straight on Moplus SDK and the Wormhole Vulnerability</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://www.wooyun.org/bugs/wooyun-2015-0151955">NanoHttpd一处设计不当可导致remote-hddr被覆盖</a></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ChatGPT 国内乱象]]></title>
            <link>https://honglu.me/article/65d44c34-f4db-4feb-a8fc-599ade5a1486</link>
            <guid>https://honglu.me/article/65d44c34-f4db-4feb-a8fc-599ade5a1486</guid>
            <pubDate>Fri, 31 Mar 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[ChatGPT 确实是一个非常惊艳的产品，不过我也很佩服国内大佬们的赚钱能力，列举下国内利用 ChatGPT 赚钱的几个套路]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-65d44c34f4db4feba8fc599ade5a1486"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-text notion-block-40c606ede3654d93b0768a57a4319ed9">ChatGPT 确实是一个非常惊艳的产品，不过我也很佩服国内大佬们的赚钱能力，列举下国内利用 ChatGPT 赚钱的几个套路：</div><ul class="notion-list notion-list-disc notion-block-cefb847b78374b849f4fc14e67d8a5cf"><li>卖号：各种机器注册的账号，价格从20元一路打到3元。</li></ul><ul class="notion-list notion-list-disc notion-block-ac1f3d80a8054116b3936e0385c0f663"><li>建站卖广告：这种最多，搭个web站挂上广告给大家使用，一般都有次数限制。</li></ul><ul class="notion-list notion-list-disc notion-block-ebbfa95d2ddc4b0f8c403d8d36b644b4"><li>卖软件：iOS 应用商店各种收费软件。</li></ul><ul class="notion-list notion-list-disc notion-block-f4bf3caee1fa4d8da7887f46d35f726a"><li>卖使用次数：微信小程序、web上卖使用次数。</li></ul><ul class="notion-list notion-list-disc notion-block-37ce956ff13d49cc8592402c01d8c54d"><li>卖企业搭建服务：提供team搭建方案，卖搭建服务。</li></ul><ul class="notion-list notion-list-disc notion-block-fba2443ff57e48258675456994e2f899"><li>卖课、卖文章：制造焦虑卖prompt相关课程，卖 ChatGPT 搭建文章。</li></ul><ul class="notion-list notion-list-disc notion-block-c735002bd6524f408902466e407a7897"><li>用来生成文章，短视频</li></ul><div class="notion-text notion-block-8c132e11674c401d83f399887389291c">当然，不是说上面的赚钱套路有什么不对，这里只是列举一些隐含的安全问题，大家在使用的时候注意一下。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-be90742cec294723922a463c1be534c6" data-id="be90742cec294723922a463c1be534c6"><span><div id="be90742cec294723922a463c1be534c6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#be90742cec294723922a463c1be534c6" title="注册"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">注册</span></span></h3><div class="notion-text notion-block-83c136ccc3334dc39cda533ca589f7fe">有条件的同学当然最好还是自己注册，这里说一下卖号的套路。</div><div class="notion-text notion-block-b33160ffddfc4141aa6f7a6c04c5b729">网上目前能买到的号基本上都是机器注册的账号，单账号价格10元以下吧。</div><div class="notion-text notion-block-76a6beef9e2f404087212d4644d45181">另外官方plus的价格是$20，折合人民币在130+，但是淘宝上一些商家能卖到130元，甚至更低，这种基本上就是信用卡违规操作出来的，同样随时被封。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-b70c53ddbf7f4b3484b1c646fcea642d" data-id="b70c53ddbf7f4b3484b1c646fcea642d"><span><div id="b70c53ddbf7f4b3484b1c646fcea642d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#b70c53ddbf7f4b3484b1c646fcea642d" title="使用"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">使用</span></span></h3><div class="notion-text notion-block-33187b958bc6455caf26c9e40735c5bc">国内搭建的 ChatGPT 服务基本上都可以认为不安全，因为国内是要求做敏感词屏蔽的，凡是网站带备案，在国内提供服务的，一方面有敏感词屏蔽，另一方面你的聊天记录也是可以从后台看到的。</div><div class="notion-text notion-block-30635c4d86b64bf6a3323974a2015e80">那么部署在海外的服务就安全么？不见得，现在 ChatGPT 的后端服务做得也很完善了，可以看下这个「小狐狸」系统，专门给 app 和微信小程序做的后台，你的聊天记录从后台同样可以一览无余。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0c827db50f1f4df88e26a176754e3d8f"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F296ddeff-2eb8-4d05-9d54-7459941dd107%2FUntitled.png?table=block&amp;id=0c827db5-0f1f-4df8-8e26-a176754e3d8f&amp;t=0c827db5-0f1f-4df8-8e26-a176754e3d8f&amp;width=800&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-506435e1fd424d90a86b7c090d2ed715"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://tuan.chinaz.com/goods_detail/3649">小狐狸后台界面</a></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-8532a96a467143a5aefa101bb3b7a19c" data-id="8532a96a467143a5aefa101bb3b7a19c"><span><div id="8532a96a467143a5aefa101bb3b7a19c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#8532a96a467143a5aefa101bb3b7a19c" title="建议"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">建议</span></span></h3><div class="notion-text notion-block-e560eccd6e404db7a3802df64b4a7522">有条件的最好还是自己搞个梯子来直接使用 ChatGPT 的官方服务。</div><div class="notion-text notion-block-891e113bfa7f49ffa30d50e3d68d244a">访问不了的可以退而求其次，使用开源项目来自己搭建，比如：<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/Chanzhaoyu/chatgpt-web">ChatGPT-web</a>、<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/Yidadaa/ChatGPT-Next-Web">ChatGPT-next-web</a>。</div><div class="notion-text notion-block-4969ed13ca4f4793ba072f9538ca55fd">不会搭建的，可以使用开源的 ChatGPT 客户端，比如：<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/pljhonglu/ChatGPT-T">ChatGPT-T</a>。</div><div class="notion-text notion-block-2525573c62874adc85249922b54c7dea">最后还是不推荐大家使用各种免费或付费的二次封装的 ChatGPT 服务。</div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[负数取模]]></title>
            <link>https://honglu.me/article/9764bee1-c06f-4bec-841a-5aead1658815</link>
            <guid>https://honglu.me/article/9764bee1-c06f-4bec-841a-5aead1658815</guid>
            <pubDate>Sat, 27 May 2017 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-9764bee1c06f4bec841a5aead1658815"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-text notion-block-e44f08c6a74f4843a5d6fea6e9a5313f">有两个答案：<code class="notion-inline-code">-7</code> 或 <code class="notion-inline-code">3</code></div><div class="notion-text notion-block-7a809b4416c44a86a0269e5492c88303">这就要说到编程语言对于模运算的定义</div><div class="notion-text notion-block-b87ebf7cb0944f8b89641b89f4f11c5d">这其中对于除法运算 <code class="notion-inline-code">a / b</code> 有两种不同的取整方式，一种是向 0 取整，一种是向负无穷取整。这就导致了取模会有两种结果。</div><div class="notion-text notion-block-7693bd35aa104cfab2eb990fb91e089d">例如：</div><blockquote class="notion-quote notion-block-4d9b16c5468c4f289eb61b4615c715e8"><div>-7/10 向 0 取整，得到的结果是 0，如果向负无穷取整得到的结果是 -1。因此，如果采用向 0 取整的方式，-7%10 结果就是 -7，如果向负无穷取整，得到的结果就是 3。</div></blockquote><div class="notion-text notion-block-576bddb28910434d9b724b27eb701b56">到底是向 0 取整还是向负无穷取整，不同的语言实现方式不同，通常默认是按照向 0 取整来计算的，Python 是按照向负无穷取整来计算的，部分语言干脆提供两个函数，rem 和 mod。</div><div class="notion-text notion-block-488cdfebe6284d08a3739c5a3185da06">有时候确实需要使用向负无穷取整这种方式怎么办呢。可以通过 <code class="notion-inline-code">floor</code> 函数来计算，该函数是向下取整。那么取模变为如下方式:</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-7153ca9ba32c43e68e100ab6fdab12c7" data-id="7153ca9ba32c43e68e100ab6fdab12c7"><span><div id="7153ca9ba32c43e68e100ab6fdab12c7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#7153ca9ba32c43e68e100ab6fdab12c7" title="参考"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">参考</span></span></h3><ul class="notion-list notion-list-disc notion-block-e8345f6a1f10417f9e87fa8663908a92"><li><a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://www.jianshu.com/p/452c1a5acd31">负数取模怎么算</a></li></ul><ul class="notion-list notion-list-disc notion-block-7a3222f144e9403a8187cf5af4f342b3"><li><a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://blog.csdn.net/solstice/article/details/5139302">带符号整数的除法与余数</a></li></ul><ul class="notion-list notion-list-disc notion-block-3da46a7851da4a41a9e387e09d80a5ec"><li><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://www.zhihu.com/question/34116024">为什么负数的取余计算各个编程语言结果不统一</a></li></ul><ul class="notion-list notion-list-disc notion-block-a1fca5e6ab4746e38c6c775db5bb1f01"><li><a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://baike.baidu.com/item/%E5%8F%96%E6%A8%A1%E8%BF%90%E7%AE%97">取模运算</a></li></ul><div class="notion-blank notion-block-4f3e9e65ed7e496f83d12da0c3bde32f"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[深入理解RunLoop]]></title>
            <link>https://honglu.me/article/3b6ae60e-3c1d-4d53-8d48-7bcc085af0da</link>
            <guid>https://honglu.me/article/3b6ae60e-3c1d-4d53-8d48-7bcc085af0da</guid>
            <pubDate>Thu, 30 Mar 2017 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-3b6ae60e3c1d4d538d487bcc085af0da"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-19eac9a36a77423393043ea954aa9184" data-id="19eac9a36a77423393043ea954aa9184"><span><div id="19eac9a36a77423393043ea954aa9184" class="notion-header-anchor"></div><a class="notion-hash-link" href="#19eac9a36a77423393043ea954aa9184" title="事件驱动模型"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">事件驱动模型</span></span></h3><div class="notion-text notion-block-d48c09a01175412ab6c20ef76acf72a8">大部分的 GUI 程序都使用了事件驱动模型，有些服务器(nginx, node.js)也使用了事件驱动模型。相比于轮询等其他方式，其优点在于极大的提高了 CPU 使用率，在没有事件的时候，能够让出 CPU 时间片，来事件时也可以快速的得到响应。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-0e72d28613604358b6ceb9cb93d25345" data-id="0e72d28613604358b6ceb9cb93d25345"><span><div id="0e72d28613604358b6ceb9cb93d25345" class="notion-header-anchor"></div><a class="notion-hash-link" href="#0e72d28613604358b6ceb9cb93d25345" title="Cocoa 中的 RunLoop"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Cocoa 中的 RunLoop</span></span></h3><div class="notion-text notion-block-546e8789ed294598ab8b75ef5b5a2347">我们常用到的是 NSRunLoop ，其位于 Foundation 框架中。Foundation 框架实际上是 <code class="notion-inline-code">Core Foundation</code> 的部分导出。而 <code class="notion-inline-code">Core Foundation</code> 的源码是开源的。NSRunLoop 实际上是 CFRunLoop 的高层抽象。CFRunLoop 源码可以从<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://opensource.apple.com/source/CF/">这里</a> 下载到。</div><div class="notion-text notion-block-225217de138c4fdf851aefc1cd22564e">RunLoop 在 Cocoa 中的应用相当广泛。</div><div class="notion-text notion-block-2cc30ba4c7bc4bc09c10e91c8977bfe1">NSTimer，UIEvent，NSObject (NSDelayedPerforming)，NSObject (NSThreadPerformAdditions)，NSURLConnection 是通过 Source 的机制把事件加入到 RunLoop 中。</div><div class="notion-text notion-block-8bc568494dcf4cbab8d7b464bea6d12c">Autorelease，CADisplayLink，CATransition，CAAnimation 则是利用 observer 的机制，在 RunLoop 的循环周期中执行各自的操作。</div><div class="notion-text notion-block-f37467c89d7e44ae88d5967b18f89dba">更加底层的 GCD，mach kernel 也与 RunLoop 存在协作关系。</div><div class="notion-text notion-block-b41349efa1b149a0b9eae114ba8792dc">使用 Xcode 调试的时候我们经常可以看到 RunLoop 相关的堆栈信息：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0966de18960f4b57988978afd58a8b30"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img src="http://blog.qiniu.lololo.store/2017-03-31-runloop%E6%96%AD%1C%E7%82%B9.jpg?spaceId=a253e2c0-c392-4d5d-9882-55bc928a1eb6&amp;t=0966de18-960f-4b57-9889-78afd58a8b30" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-cf9404468de24deb8fce86929b1d4b18">runloop断点</div><div class="notion-text notion-block-459959722f0245d7ac85532dd1c5f95a">图中可以看到四处关于 RunLoop 的堆栈信息，最上面一条 <code class="notion-inline-code">__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__</code> 则是 RunLoop 中的 observer 的 call out 调用。其它的调用还包括：</div><div class="notion-text notion-block-22100d0ea4f14ecd9527c0094dc9e289">图中还可以看出 CATransition 利用了 RunLoop 的 observer 做视图的相关操作。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-289349d68fc643d1b6831508b464e2ef" data-id="289349d68fc643d1b6831508b464e2ef"><span><div id="289349d68fc643d1b6831508b464e2ef" class="notion-header-anchor"></div><a class="notion-hash-link" href="#289349d68fc643d1b6831508b464e2ef" title="RunLoop 机制"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">RunLoop 机制</span></span></h3><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0a9c6e9723944bdb85e4c2e61d041867"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img src="http://blog.qiniu.lololo.store/2017-03-31-RunLoop%E7%BB%93%E6%9E%84.png?spaceId=a253e2c0-c392-4d5d-9882-55bc928a1eb6&amp;t=0a9c6e97-2394-4bdb-85e4-c2e61d041867" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-26cde699a1404197afc72a4c0ad77e5d">RunLoop结构</div><div class="notion-text notion-block-f2282bf5e7584bbbbbe9a73700d59d12">这一张图我们可以看出 RunLoop 的组织结构关系。</div><div class="notion-text notion-block-b3c7e67ac16b4d8d982f04de69325eea">下面针对各个部分进行了说明</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-ea5a58a7194d4695abe9560f44ae7e92" data-id="ea5a58a7194d4695abe9560f44ae7e92"><span><div id="ea5a58a7194d4695abe9560f44ae7e92" class="notion-header-anchor"></div><a class="notion-hash-link" href="#ea5a58a7194d4695abe9560f44ae7e92" title="CFRunLoop"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">CFRunLoop</span></span></h4><ul class="notion-list notion-list-disc notion-block-5d47495e50584eb2bc05797ca19d3ad5"><li>CFRunLoop 里面包含了线程，若干个 mode。</li></ul><ul class="notion-list notion-list-disc notion-block-74dd376f9c804af4a890e6341409c34f"><li>CFRunLoop 和线程是一一对应的。</li></ul><ul class="notion-list notion-list-disc notion-block-0f152ea9f9ea4e8d84a113694221922f"><li>_blocks_head 是 perform block 加入到里面的</li></ul><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-078ccde315ad466abe44ef9ff12a4dad" data-id="078ccde315ad466abe44ef9ff12a4dad"><span><div id="078ccde315ad466abe44ef9ff12a4dad" class="notion-header-anchor"></div><a class="notion-hash-link" href="#078ccde315ad466abe44ef9ff12a4dad" title="CFRunLoopMode"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">CFRunLoopMode</span></span></h4><ul class="notion-list notion-list-disc notion-block-6336daa2b168417f9438cc7719aaaaaa"><li>runloop mode 里面包含了source0、source1、timer、observer 各种事件，以及 port 集合。其负责管理这些事件。</li></ul><div class="notion-text notion-block-bf607d23c41e43cb8ce2b25df15a6088">CFRunLoopMode 包含下面几种 mode</div><table class="notion-simple-table notion-block-035585a00e29460297a060142d67914f"><tbody><tr class="notion-simple-table-row notion-block-2ebbc6ac046a43348413e26d856caf84"><td class="" style="width:120px"><div class="notion-simple-table-cell">mode</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">name</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">description</div></td></tr><tr class="notion-simple-table-row notion-block-9f1ace17fe3349be9399fc1e8953bb63"><td class="" style="width:120px"><div class="notion-simple-table-cell">Default</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">NSDefaultRunLoopMode (Cocoa) kCFRunLoopDefaultMode (Core Foundation)</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">用的最多的模式，大多数情况下应该使用该模式开始 RunLoop并配置 input source</div></td></tr><tr class="notion-simple-table-row notion-block-b401fdf5ff4845e99e0d1a14c66cf831"><td class="" style="width:120px"><div class="notion-simple-table-cell">Connection</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">NSConnectionReplyMode (Cocoa)</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Cocoa用这个模式结合 NSConnection 对象监测回应，我们应该很少使用这种模式</div></td></tr><tr class="notion-simple-table-row notion-block-24c52df4b9744beca0ea83b2456230c5"><td class="" style="width:120px"><div class="notion-simple-table-cell">Modal</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">NSModalPanelRunLoopMode (Cocoa)</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Cocoa用此模式来标识用于模态面板的事件</div></td></tr><tr class="notion-simple-table-row notion-block-cbacd6dc41504ef183b67a4fdb0aaeb7"><td class="" style="width:120px"><div class="notion-simple-table-cell">Event tracking</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">NSEventTrackingRunLoopMode (Cocoa)</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">Cocoa使用此模式在鼠标拖动loop和其它用户界面跟踪 loop期间限制传入事件</div></td></tr><tr class="notion-simple-table-row notion-block-86136ccd879e4240882b9e7668db8a90"><td class="" style="width:120px"><div class="notion-simple-table-cell">Common modes</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">NSRunLoopCommonModes (Cocoa) kCFRunLoopCommonModes (Core Foundation)</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">这是一组可配置的常用模式。将输入源与些模式相关联会与组中的每个模式相关联。Cocoa applications 里面此集包括Default、Modal和Event tracking。Core Foundation只包括默认模式，你可以自己把自定义mode用CFRunLoopAddCommonMode函数加入到集合中.</div></td></tr></tbody></table><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1fa58f29c5224d4f8ca56caafe2381f2" data-id="1fa58f29c5224d4f8ca56caafe2381f2"><span><div id="1fa58f29c5224d4f8ca56caafe2381f2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1fa58f29c5224d4f8ca56caafe2381f2" title="RunLoopSource"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">RunLoopSource</span></span></h4><div class="notion-text notion-block-5578598c696b446ab82eb91c5e5aa2e2">RunLoopSource 分为 source0 和 source1。</div><ul class="notion-list notion-list-disc notion-block-fc5da44daddd49f1bf79c3cfcac5bc38"><li>source0 是非基于 port 的事件，主要是 APP 内部事件，如点击事件，触摸事件等。</li></ul><ul class="notion-list notion-list-disc notion-block-be8a6421b03a422a96a35f767e4fd90a"><li>source1 是基于Port的，通过内核和其他线程通信，接收，分发系统事件。</li></ul><ul class="notion-list notion-list-disc notion-block-10c82585c5e343e7b5810d08a3859bcb"><li>__CFRunLoopSource 里面包含一个 _runLoops，也就意味着一个 __CFRunLoopSource 可以被添加到多个 runloop mode 中去。</li></ul><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-4bba9341630b4a5b8fbc16764df150db" data-id="4bba9341630b4a5b8fbc16764df150db"><span><div id="4bba9341630b4a5b8fbc16764df150db" class="notion-header-anchor"></div><a class="notion-hash-link" href="#4bba9341630b4a5b8fbc16764df150db" title="RunLoopObserver"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">RunLoopObserver</span></span></h4><div class="notion-text notion-block-75806552c59042a2a57992cb0b08c244">CFRunLoopObserver 是观察者，可以观察RunLoop的各种状态，并抛出回调。可以监听得状态如下：</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-3d6a9298906f43e499e9dd43689507c1" data-id="3d6a9298906f43e499e9dd43689507c1"><span><div id="3d6a9298906f43e499e9dd43689507c1" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3d6a9298906f43e499e9dd43689507c1" title="RunLoopTimer"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">RunLoopTimer</span></span></h4><ul class="notion-list notion-list-disc notion-block-283e9c5bc2c54ef8b47a7379ea6a6e52"><li>CFRunLoopTimer 是定时器，可以在设定的时间点抛出回调</li></ul><ul class="notion-list notion-list-disc notion-block-729ccde0b0fa40ba8e48343b8c708a6a"><li>CFRunLoopTimer和NSTimer是toll-free bridged的，可以相互转换。</li></ul><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-8d902ca2cb7d4c899e3e5c5679bd6eb8" data-id="8d902ca2cb7d4c899e3e5c5679bd6eb8"><span><div id="8d902ca2cb7d4c899e3e5c5679bd6eb8" class="notion-header-anchor"></div><a class="notion-hash-link" href="#8d902ca2cb7d4c899e3e5c5679bd6eb8" title="CFRunLoopGet"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">CFRunLoopGet</span></span></h3><ul class="notion-list notion-list-disc notion-block-1bbc94bc371047f5b468f920c4894d52"><li>RunLoop 和线程是一一对应的</li></ul><ul class="notion-list notion-list-disc notion-block-3317f1e0d3874b33b2cb28976cab71af"><li>不允许直接创建RunLoop</li></ul><ul class="notion-list notion-list-disc notion-block-886045c6632d4827b6c0125f7655b045"><li>线程刚创建时并没有 RunLoop, 如果你不主动获取, 那它一直都不会有</li></ul><ul class="notion-list notion-list-disc notion-block-e21db4db9ada448fa18287f5551a00bc"><li>你只能在一个线程的内部获取其 RunLoop(main thread 除外)</li></ul><ul class="notion-list notion-list-disc notion-block-0a43116c67ed41efa149a2b1b70e7f16"><li>RunLoop 会在线程销毁时销毁</li></ul><ul class="notion-list notion-list-disc notion-block-aa5a9c6e5df94a129cb88f9dcc9ff2f8"><li>RunLoop 和线程的一一对应关系保存一个全局 Dictionary 里 __CFRunLoops</li></ul><ul class="notion-list notion-list-disc notion-block-1e6d2755283f46428129b638c044c83f"><li>即使主线程，不主动创建也不会有 RunLoop，只是 application 会在主线程默认创建 RunLoop</li></ul><ul class="notion-list notion-list-disc notion-block-6c8bd841346a48068f1c799ef8b02b3b"><li>如果创建子线程 RunLoop，主线程会默认加上一个 RunLoop</li></ul><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-b21c1ddb7fc5452a93b9fbcfc3431b13" data-id="b21c1ddb7fc5452a93b9fbcfc3431b13"><span><div id="b21c1ddb7fc5452a93b9fbcfc3431b13" class="notion-header-anchor"></div><a class="notion-hash-link" href="#b21c1ddb7fc5452a93b9fbcfc3431b13" title="CFRunLoopRun"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">CFRunLoopRun</span></span></h3><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-901216d89d3a4700a32ed9ecae43dbdf"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img src="http://blog.qiniu.lololo.store/2017-03-31-RunLoopRun.png?spaceId=a253e2c0-c392-4d5d-9882-55bc928a1eb6&amp;t=901216d8-9d3a-4700-a32e-d9ecae43dbdf" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-9b14f1564d954882b2574db0fe942657">RunLoopRun</div><div class="notion-text notion-block-42ece29e9caa4232a32aeef58037ba50">执行过程大致描述如下：</div><ol start="1" class="notion-list notion-list-numbered notion-block-77aaf84e28374d80a1fc33c40b5dc90e"><li>通知 observers 即将进入 run loop</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-f1436e54553b41b4837e337bce667ed9"><li>通知 observes 即将开始处理 timer source</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-6faf050d442a422299507b3b52c53c4f"><li>通知 observes 即将开始处理 source0 事件</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-fe32f2571a0f437f92c434a36cab0190"><li>执行 source0 事件</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-9b9e1a8af9b24465be3dd3ec8eb0e4e6"><li>如果处于主线程有 dispatchPort 消息，跳转到第9步</li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-52b060c21f9a4bc58ccd76432c4f6226"><li>通知 observes 线程即将进入休眠</li></ol><ol start="7" class="notion-list notion-list-numbered notion-block-ceb74186bc0f4c0d892870e411cd62a1"><li>内循环阻塞等待接收系统消息，包括：</li><ol class="notion-list notion-list-numbered notion-block-ceb74186bc0f4c0d892870e411cd62a1"><ul class="notion-list notion-list-disc notion-block-ac22c46227764781aac543ab4c3fcda3"><li>收到内核发送过来的消息 （source1消息）</li></ul><ul class="notion-list notion-list-disc notion-block-ecc57133e1f1469e9c1f22bf0f6240ee"><li>定时器事件需要执行</li></ul><ul class="notion-list notion-list-disc notion-block-a0c59718874f4e108e293ab9ef318535"><li>run loop 的超时时间到了</li></ul><ul class="notion-list notion-list-disc notion-block-64dc628c8c09439d97cc6a12b18dad06"><li>手动唤醒 run loop</li></ul></ol></ol><ol start="8" class="notion-list notion-list-numbered notion-block-f62e083ee8204119ad001a9a16649c25"><li>通知 observes 线程被唤醒</li></ol><ol start="9" class="notion-list notion-list-numbered notion-block-d6f6610ea1994398a94e35127a53c380"><li>处理通过端口收到的消息：</li><ol class="notion-list notion-list-numbered notion-block-d6f6610ea1994398a94e35127a53c380"><ul class="notion-list notion-list-disc notion-block-1fd90c330610413b8d299457d2592a2b"><li>如果自定义的 timer 被 fire，那么执行该 timer 事件并重新开始循环，完成后跳转到第2步</li></ul><ul class="notion-list notion-list-disc notion-block-0780c0689e5a47b08fe1bbd9855cc8b4"><li>如果 input source 被 fire，则处理该事件</li></ul><ul class="notion-list notion-list-disc notion-block-43d6a341fdc24439bdf5547502ca713e"><li>如果 run loop 被手动唤醒，并且没有超时，完成后跳转到第2步</li></ul></ol></ol><ol start="10" class="notion-list notion-list-numbered notion-block-6bdf8019eb354d6a94b8b0e04252639d"><li>通知 observes run loop 已经退出</li></ol><div class="notion-text notion-block-545566ca7b674a359d780e9827be6f0b">注意：</div><ul class="notion-list notion-list-disc notion-block-c16b4f29e7914c979a370e9230bd6717"><li>CFRunLoopDoBlocks 是执行 perform block 中的 block</li></ul><ul class="notion-list notion-list-disc notion-block-d913d159c02d47f18b7a4086a85eecbe"><li>绿色的是RunLoopRun()</li></ul><ul class="notion-list notion-list-disc notion-block-3d96aa797c324c11bc6cb442308b4fc1"><li>第一次循环 CFRunLoopServiceMachPort 是不走的</li></ul><ul class="notion-list notion-list-disc notion-block-f8a00ab1807a4bdf85fa1d9bb5a60043"><li>handle_msg 处理 timer 事件，处理 main queue block 事件，处理 source1 事件</li></ul><ul class="notion-list notion-list-disc notion-block-e64587f9abde44bf8de7c7b2e8921ab0"><li>中间的红色<code class="notion-inline-code">CFRunLoopServiceMachPort</code>是监听 GCD 的端口事件，只监听一个端口，左下角的<code class="notion-inline-code">CFRunLoopServiceMachPort</code>是坚挺 source1,timer 的，是一个 MutableSet</li></ul><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-6e3cfcac98ad469fb6e348c4e4a09ca6" data-id="6e3cfcac98ad469fb6e348c4e4a09ca6"><span><div id="6e3cfcac98ad469fb6e348c4e4a09ca6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#6e3cfcac98ad469fb6e348c4e4a09ca6" title="RunLoop 与 GCD"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">RunLoop 与 GCD</span></span></h3><div class="notion-text notion-block-0ed8339f89f7497c9fabd00184185901">RunLoop 与 GCD 是互相协作的关系，RunLoop 的最开始部分使用了 GCD 的 timer 做超时的回调；通过 GCD 调用带有 RunLoop 的线程的 block，会通过 <code class="notion-inline-code">dispatch port CFRunLoopServiceMachPort</code> 把事件发送到该线程的 RunLoop 里面。</div><div class="notion-text notion-block-cfa90ca1398d4747b83a97bcd0eedd30">比如：</div><div class="notion-text notion-block-e00976d53239494191f282ad03b14de2">主线程存在 runloop，那么 GCD 会通过 <code class="notion-inline-code">dispatch port CFRunLoopServiceMachPort</code>，把事件发送给 RunLoop，RunLoop 接收到时间之后，会执行这个 block。</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-7872a71ad8db4242be5752e53af3f465" data-id="7872a71ad8db4242be5752e53af3f465"><span><div id="7872a71ad8db4242be5752e53af3f465" class="notion-header-anchor"></div><a class="notion-hash-link" href="#7872a71ad8db4242be5752e53af3f465" title="NSTimer 与 GCD Timer"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">NSTimer 与 GCD Timer</span></span></h4><div class="notion-text notion-block-fd9c6a57717d48dfa3c987d31dd447a7">NSTimer 是通过 RunLoop 的 RunLoopTimer 把时间加入到 RunLoopMode 里面。官方文档里面也有说 CFRunLoopTimer 和 NSTimer 是可以互相转换的。由于 NSTimer 的这种机制，因此 NSTimer 的执行必须依赖于 RunLoop，如果没有 RunLoop，NSTimer 是不会执行的。</div><div class="notion-text notion-block-5ab5e6a10ee340499f39a8768599a88d">GCD 则不同，GCD 的线程管理是通过系统来直接管理的。GCD Timer 是通过 dispatch port 给 RunLoop 发送消息，来使 RunLoop 执行相应的 block，如果所在线程没有 RunLoop，那么 GCD 会临时创建一个线程去执行 block，执行完之后再销毁掉，因此 GCD 的 Timer 是不依赖 RunLoop 的。</div><div class="notion-text notion-block-dd86e5e616ff477682b0329a048fee3e">至于这两个 Timer 的准确性问题，如果不再 RunLoop 的线程里面执行，那么只能使用 GCD Timer，由于 GCD Timer 是基于 MKTimer(mach kernel timer)，已经很底层了，因此是很准确的。</div><div class="notion-text notion-block-7a2de946e00a4655bd6063cd3f842d6e">如果在 RunLoop 的线程里面执行，由于 GCD Timer 和 NSTimer 都是通过 port 发送消息的机制来触发 RunLoop 的，因此准确性差别应该不是很大。如果线程 RunLoop 阻塞了，不管是 GCD Timer 还是 NSTimer 都会存在延迟问题。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-d667e92363034c249aad40bd61ca4bcb" data-id="d667e92363034c249aad40bd61ca4bcb"><span><div id="d667e92363034c249aad40bd61ca4bcb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#d667e92363034c249aad40bd61ca4bcb" title="应用"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">应用</span></span></h3><ul class="notion-list notion-list-disc notion-block-e53decab9b6d4a03a48082424a10cef9"><li>异步的回调如果存在延时操作，那么就要放到有 RunLoop 的线程里面，否则回调没有着陆点无法执行</li></ul><ul class="notion-list notion-list-disc notion-block-16ada4d70f4e4475a3c2ccce4d6ff7c4"><li>NSTimer 必须得在有 RunLoop 的线程里面才能执行，另外，使用 NSTimer 的时候会出现滑动 TableView，Timer 停止的问题，是由于 RunLoopMode 切换的问题，只要把 NSTimer 加到 common mode 就好了。</li></ul><ul class="notion-list notion-list-disc notion-block-9fd99e0a61c04565b68a1805a921f0e3"><li>滚动过程中延迟加载，可以利用滚动时 RunLoopMode 切换到 NSEventTrackingRunLoopMode 模式下这个机制，在 Default mode 下添加加载图片的方法，在滚动时就不会触发。</li></ul><ul class="notion-list notion-list-disc notion-block-3e40136804fb4675b6c0849da56945a2"><li>崩溃后处理 <a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/walkdianzi/DSSignalHandlerDemo">DSSignalHandlerDemo</a></li></ul><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-c0335b6a31f540ff98c821b554b31870" data-id="c0335b6a31f540ff98c821b554b31870"><span><div id="c0335b6a31f540ff98c821b554b31870" class="notion-header-anchor"></div><a class="notion-hash-link" href="#c0335b6a31f540ff98c821b554b31870" title="演示代码"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">演示代码</span></span></h3><div class="notion-text notion-block-ff7168929c47490a902b1e6af07c3f40">代码中演示了 RunLoop 的不同事件机制，也演示了 GCD Timer 和 NStimer 的区别。</div><div class="notion-text notion-block-99561613f17145b7bf8b08ae2ca9bfda">Demo: <a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://blog.qiniu.lololo.store/RunLoopTest.zip">RunLoopTest</a></div><div class="notion-text notion-block-fcf5e199c42b49ad9a67f0190eb8539e">源码注释: <a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://blog.qiniu.lololo.store/source.zip">source</a></div><div class="notion-text notion-block-a846dfd90ae04c058e9142d777cc6063">PPT: <a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://blog.qiniu.lololo.store/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%20RunLoop.key">深入理解 RunLoop</a></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-e97669320cf64084912b3e218d0f24eb" data-id="e97669320cf64084912b3e218d0f24eb"><span><div id="e97669320cf64084912b3e218d0f24eb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#e97669320cf64084912b3e218d0f24eb" title="参考"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">参考</span></span></h3><div class="notion-text notion-block-a5b9822d623443a4aa651d4d15a8dbf0"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://blog.ibireme.com/2015/05/18/runloop/">深入理解RunLoop</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://aaaboom.com/?p=34#wow24">RunLoop系列之源码分析</a><a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://aaaboom.com/?p=37">RunLoop系列之要点提炼</a></div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[计算机浮点数那些事]]></title>
            <link>https://honglu.me/article/c9e01d84-a2b2-43ce-aa5b-8049e09e12aa</link>
            <guid>https://honglu.me/article/c9e01d84-a2b2-43ce-aa5b-8049e09e12aa</guid>
            <pubDate>Sun, 12 Mar 2017 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-c9e01d84a2b243ceaa5b8049e09e12aa"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-34049337220f405abd02d14b21a0a994" data-id="34049337220f405abd02d14b21a0a994"><span><div id="34049337220f405abd02d14b21a0a994" class="notion-header-anchor"></div><a class="notion-hash-link" href="#34049337220f405abd02d14b21a0a994" title="十进制小数转二进制小数"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">十进制小数转二进制小数</span></span></h2><div class="notion-text notion-block-bd168a6167fa4e298d6259db1b538284">对十进制小数乘2得到的整数部分和小数部分，整数部分既是相应的二进制数码；<!-- -->
再用2乘小数部分(之前乘后得到新的小数部分)，又得到整数和小数部分；<!-- -->
如此不断重复，直到小数部分为0或达到精度要求为止。<!-- -->
第一次所得到为最高位，最后一次得到为最低位。</div><div class="notion-text notion-block-8a1c58a9ba16446eb5491619ef1ca8fe">如:</div><div class="notion-text notion-block-deb633a2a32b4664a8b7074063e8fedc">0.8125的二进制：</div><div class="notion-text notion-block-8563d4022de04e868619fb0b8c461761">0.8125<em>2=1.625 取整是1</em><em>
0.625</em>2=1.25 取整是1<!-- -->
0.25<em>2=0.5 取整是0</em><em>
0.5</em>2=1.0 取整是1</div><div class="notion-text notion-block-c3eb8bb4c506491582c508e28dbe0a94">即0.8125的二进制是0.1101（第一次所得到为最高位,最后一次得到为最低位）</div><div class="notion-text notion-block-2feed158cb7740d28c07fd70a1518b7c">0.1 的二进制：</div><table class="notion-simple-table notion-block-52dc829e055143f9944591a306a5def4"><tbody><tr class="notion-simple-table-row notion-block-df347cca130b4b13bed2d2cbc6f71618"><td class="" style="width:120px"><div class="notion-simple-table-cell">乘数</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">乘2结果</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">整数部分</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">说明</div></td></tr><tr class="notion-simple-table-row notion-block-3676122799794b91ad5f1b12b5a1c14d"><td class="" style="width:120px"><div class="notion-simple-table-cell">0.1</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0.2</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">ㅤ</div></td></tr><tr class="notion-simple-table-row notion-block-c0fc0431ff534735b87e71e2dedff38d"><td class="" style="width:120px"><div class="notion-simple-table-cell">0.2</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0.4</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">ㅤ</div></td></tr><tr class="notion-simple-table-row notion-block-c476ac20d5c64341bafef6ddace1835e"><td class="" style="width:120px"><div class="notion-simple-table-cell">0.4</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0.8</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">ㅤ</div></td></tr><tr class="notion-simple-table-row notion-block-1de09086aaa449e2a449a8a90d923e25"><td class="" style="width:120px"><div class="notion-simple-table-cell">0.8</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1.6</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">减1</div></td></tr><tr class="notion-simple-table-row notion-block-3a9408fdc8754604b1f41c7d11abdcba"><td class="" style="width:120px"><div class="notion-simple-table-cell">0.6</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1.2</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">减1</div></td></tr><tr class="notion-simple-table-row notion-block-db534895f7c1421f8f82caf6198d15e6"><td class="" style="width:120px"><div class="notion-simple-table-cell">0.2</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0.4</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">ㅤ</div></td></tr><tr class="notion-simple-table-row notion-block-04af0ec2c6eb4ebe84a9d7eb9478da42"><td class="" style="width:120px"><div class="notion-simple-table-cell">0.4</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0.8</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">0</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">ㅤ</div></td></tr><tr class="notion-simple-table-row notion-block-ca0c423d99b640348f45d7f6c345422e"><td class="" style="width:120px"><div class="notion-simple-table-cell">0.8</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1.6</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">减1</div></td></tr><tr class="notion-simple-table-row notion-block-5ef14789282d49d48e32dea7bdf2b62f"><td class="" style="width:120px"><div class="notion-simple-table-cell">0.6</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1.2</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">减1</div></td></tr><tr class="notion-simple-table-row notion-block-8b127ba07f9f4aaaba22470dfe3ba017"><td class="" style="width:120px"><div class="notion-simple-table-cell">…</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">…</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">…</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">ㅤ</div></td></tr></tbody></table><div class="notion-text notion-block-5d5e8e49f0d343a4a0a94eebe2f4be04">可见，十进制 0.1 转为二进制后为 <code class="notion-inline-code">0.000110011001100110011...</code> 无限循环小数</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-72b66035219b4a3c9898b4d97e58930a" data-id="72b66035219b4a3c9898b4d97e58930a"><span><div id="72b66035219b4a3c9898b4d97e58930a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#72b66035219b4a3c9898b4d97e58930a" title="计算机浮点数表示"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">计算机浮点数表示</span></span></h2><div class="notion-text notion-block-5202ac05694d45b2a929c2be518da13e">计算机存储浮点数遵循 <a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://www.ibm.com/developerworks/cn/java/j-jtp0114/index.html">IEEE 754</a> 格式。</div><blockquote class="notion-quote notion-block-ed673906f2044679bb4e090fea036e16"><div>V = (-1)^S * M * 2^E</div></blockquote><ul class="notion-list notion-list-disc notion-block-92ce9a220406479ab76f7015ecb215ff"><li>S(Sign/1bit)：符号部分，当 S=0，V 为正数；当 S=1，V 为负数。</li></ul><ul class="notion-list notion-list-disc notion-block-e553dc5db5434162b467f1eb8106d3ed"><li>E(Exponent/8bits)：指数部分。以2为底。需要注意的是，E 在这里是一个无符号整数，但是实际上指数是可以存在负数的，所以 <a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://www.ibm.com/developerworks/cn/java/j-jtp0114/index.html">IEEE 754</a> 规定，E 需要偏移127(64位为1023)，如，2^10 的指数是10，保存成32位浮点数时，必须保存成 10+127=137，即10001001。</li></ul><ul class="notion-list notion-list-disc notion-block-47c19497dc64474da266c23ff0c293f7"><li>M(Mantissa/23bits)：表示有效数字，大于等于1，小于2。第一位 1 可以被舍去，只保留小数部分节省一位有效数字。</li></ul><div class="notion-text notion-block-c70dcd339a9d472187266f377bbfe3a3">指数 E 还分为三种情况：</div><ol start="1" class="notion-list notion-list-numbered notion-block-f0d107f4ae87434ebe04d2fc894a67e5"><li>E 不全为0或不全为1。这时，浮点数就采用上面的规则表示，即指数E的计算值减去127（或1023），得到真实值，再将有效数字M前加上第一位的1。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-cf028e50af574b058630fcb2ec6c7d6e"><li>E全为0。这时，浮点数的指数E等于 1-127（或者 1-1023），有效数字M不再加上第一位的1，而是还原为 0.xxxxxx 的小数。这样做是为了表示±0，以及接近于0的很小的数字。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-c119deb4621e4518b2c6d928ee97e03c"><li>E全为1。这时，如果有效数字M全为0，表示±无穷大（正负取决于符号位s）；如果有效数字M不全为0，表示这个数不是一个数（NaN）。</li></ol><div class="notion-text notion-block-39aa46ff2d504a2e9a77618e1d8661bd"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://www.ibm.com/developerworks/cn/java/j-jtp0114/index.html">IEEE 754</a> 中规定：</div><div class="notion-text notion-block-75c8288f71074097a5a6ba3dce6799ef">对于32位的浮点数，最高的1位是符号位S，接着的8位是指数E，剩下的23位为有效数字M；<!-- -->
对于64位的浮点数，最高的1位是符号位S，接着的11位是指数E，剩下的52位为有效数字M。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1cad77f6b0dd4970ab7bd41fc1e0aa97"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img src="http://blog.qiniu.lololo.store/2016-08-02-14701202537550.gif?spaceId=a253e2c0-c392-4d5d-9882-55bc928a1eb6&amp;t=1cad77f6-b0dd-4970-ab7b-d41fc1e0aa97" alt="notion image" loading="lazy" decoding="async"/></div></figure><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-03f9fc4ffe4949f8b8fcac9637ab232f" data-id="03f9fc4ffe4949f8b8fcac9637ab232f"><span><div id="03f9fc4ffe4949f8b8fcac9637ab232f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#03f9fc4ffe4949f8b8fcac9637ab232f" title="计算误差"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">计算误差</span></span></h3><div class="notion-text notion-block-4317b308e0744b069ea416f64f73d9de">下面以双精度 double 类型的 0.1+0.2 为例</div><div class="notion-text notion-block-23389e7396ca44bab5cb541c28070eb7">可以发现，由于十进制下的部分小数无法使用二进制精确表示，所以会存在一定的计算误差。在平常应用中我们很少会遇到这个问题，比如遇到支付宝这样的货币系统，需要高精度的计算时，通常我们会定义精确到小数点后两位，然后使用整形来代替浮点型，即使用一分钱的整数倍。</div></main></div>]]></content:encoded>
        </item>
    </channel>
</rss>