PageSourceSearch

https://poos.github.io/2024/05/11/swiftmacro/

html poos.github.io collected 2026-10-03 09:46:39 UTC 32,731 bytes, 1,051 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5    <meta charset="utf-8">
6    <meta http-equiv="X-UA-Compatible" content="IE=edge">
7    <meta name="google-site-verification" content="xBT4GhYoi5qRD5tr338pgPM5OWHHIDR6mNg1a3euekI" />
8    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
9    <meta name="description" content="Learning... Codeing...">
10    <meta name="keywords"  content="Shown, ShownBie, poos, iOS, Apple, iPhone">
11    <meta name="theme-color" content="#000000">
12    
13    <!-- Open Graph -->
14    <meta property="og:title" content="Swift Macro 实践,封装 OSLog - Welocme to Shown's blog">
15    
16    <meta property="og:type" content="article">
17    <meta property="og:description" content="
18  仓库已开源:NoLogMacro。
19
20
21">
22    
23    <meta property="article:published_time" content="2024-05-11T00:00:00Z">
24    
25    
26    <meta property="article:author" content="poos">
27    
28    
29    <meta property="article:tag" content="Swift Macro">
30    
31    <meta property="article:tag" content="Code">
32    
33    
34    <meta property="og:image" content="https://poos.github.io/img/about-Me.png">
35    <meta property="og:url" content="https://poos.github.io/2024/05/11/swiftmacro/">
36    <meta property="og:site_name" content="Welocme to Shown's blog">
37    
38    <title>Swift Macro 实践,封装 OSLog - Welocme to Shown's blog</title>
39
40    <!-- Web App Manifest -->
41    <link rel="manifest" href="/pwa/manifest.json">
42
43    <!-- Favicon -->
44    <link rel="shortcut icon" href="/img/favicon.ico">
45    
46    <!-- Canonical URL -->
47    <link rel="canonical" href="https://poos.github.io/2024/05/11/swiftmacro/">
48
49    <!-- Bootstrap Core CSS -->
50    <link rel="stylesheet" href="/css/bootstrap.min.css">
51
52    <!-- Custom CSS -->
53    <link rel="stylesheet" href="/css/hux-blog.min.css">
54
55    <!-- Custom Fonts -->
56    <!-- <link href="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" type="text/css"> -->
57    <!-- Hux change font-awesome CDN to qiniu -->
58    <link href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet" type="text/css">
59
60
61    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
62    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
63    <!--[if lt IE 9]>
64        
64<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
64
65        
65<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
65
66    <![endif]-->
67
68    <!-- ga & ba script hoook -->
69    
69<script></script>
69
70</head>
71
72
73<!-- hack iOS CSS :active style -->
74<body ontouchstart="">
75
76    <!-- Navigation -->
77
78    <nav class="navbar navbar-default navbar-custom navbar-fixed-top">
79        
80        <div class="container-fluid">
81            <!-- Brand and toggle get grouped for better mobile display -->
82            <div class="navbar-header page-scroll">
83                <button type="button" class="navbar-toggle">
84                    <span class="sr-only">Toggle navigation</span>
85                    <span class="icon-bar"></span>
86                    <span class="icon-bar"></span>
87                    <span class="icon-bar"></span>
88                </button>
89                <a class="navbar-brand" href="/">Shown's blog</a>
90            </div>
91
92            <!-- Collect the nav links, forms, and other content for toggling -->
93            <div id="huxblog_navbar">
94                <div class="navbar-collapse">
95                    <ul class="nav navbar-nav navbar-right">
96                        <li>
97                            <a href="/">Home</a>
98                        </li>
99                        
100                        
101                        
102                        
103                        <li>
104                            <a href="/about/">About</a>
105                        </li>
106                        
107                        
108                        
109                        <li>
110                            <a href="/archive/">Archive</a>
111                        </li>
112                        
113                        
114                        
115                        
116                        
117                        
118                        
119                        <li>
120                            <a href="/offline.html">Offline</a>
121                        </li>
122                        
123                        
124                        
125                        
126                        
127                        
128                        
129                        
130                        
131                        
132                        
133                        
134                        
135                        
136                        
137                        
138                        <li class="search-icon">
139                            <a href="javascript:void(0)">
140                                <i class="fa fa-search"></i>
141                            </a>
142                        </li>
143                    </ul>
144                </div>
145            </div>
146            <!-- /.navbar-collapse -->
147        </div>
148        <!-- /.container -->
149    </nav>
150
151    
151<script>
152        // Drop Bootstarp low-performance Navbar
153        // Use customize navbar with high-quality material design animation
154        // in high-perf jank-free CSS3 implementation
155        var $body = document.body;
156        var $toggle = document.querySelector('.navbar-toggle');
157        var $navbar = document.querySelector('#huxblog_navbar');
158        var $collapse = document.querySelector('.navbar-collapse');
159
160        var __HuxNav__ = {
161            close: function () {
162                $navbar.className = " ";
163                // wait until animation end.
164                setTimeout(function () {
165                    // prevent frequently toggle
166                    if ($navbar.className.indexOf('in') < 0) {
167                        $collapse.style.height = "0px"
168                    }
169                }, 400)
170            },
171            open: function () {
172                $collapse.style.height = "auto"
173                $navbar.className += " in";
174            }
175        }
176
177        // Bind Event
178        $toggle.addEventListener('click', function (e) {
179            if ($navbar.className.indexOf('in') > 0) {
180                __HuxNav__.close()
181            } else {
182                __HuxNav__.open()
183            }
184        })
185
186        /**
187         * Since Fastclick is used to delegate 'touchstart' globally
188         * to hack 300ms delay in iOS by performing a fake 'click',
189         * Using 'e.stopPropagation' to stop 'touchstart' event from 
190         * $toggle/$collapse will break global delegation.
191         * 
192         * Instead, we use a 'e.target' filter to prevent handler
193         * added to document close HuxNav.  
194         *
195         * Also, we use 'click' instead of 'touchstart' as compromise
196         */
197        document.addEventListener('click', function (e) {
198            if (e.target == $toggle) return;
199            if (e.target.className == 'icon-bar') return;
200            __HuxNav__.close();
201        })
202    </script>
202
203    <!-- Search -->
204<div class="search-page">
205  <div class="search-icon-close-container">
206    <span class="search-icon-close">
207      <i class="fa fa-chevron-down"></i>
208    </span>
209  </div>
210  <div class="search-main container">
211    <div class="row">
212      <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
213        <form></form>
214        <input type="text" id="search-input" placeholder="$ grep...">
215        </form>
216        <div id="search-results" class="mini-post-list"></div>
217      </div>
218    </div>
219  </div>
220</div>
221
222    <!-- Image to hack wechat -->
223<!-- <img src="/img/icon_wechat.png" width="0" height="0"> -->
224<!-- <img src="/img/post-bg-2015.jpg" width="0" height="0"> -->
225
226<!-- Post Header -->
227
228
229
230<style type="text/css">
231    header.intro-header{
232        position: relative;
233        background-image: url('/img/post-bg-2015.jpg');
234        background: ;
235    }
236
237    
238</style>
239
240<header class="intro-header" >
241
242    <div class="header-mask"></div>
243    
244    <div class="container">
245        <div class="row">
246            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
247                <div class="post-heading">
248                    <div class="tags">
249                        
250                        <a class="tag" href="/archive/?tag=Swift+Macro" title="Swift Macro">Swift Macro</a>
251                        
252                        <a class="tag" href="/archive/?tag=Code" title="Code">Code</a>
253                        
254                    </div>
255                    <h1>Swift Macro 实践,封装 OSLog</h1>
256                    
257                    <h2 class="subheading">Xcode15后,debug控制台提供了非常有用的日志输入,其中包括了可直接定位到代码行的功能。本文的实践让我们自己的日志库可以实现类似的功能。</h2>
258                    <span class="meta">Posted by poos on May 11, 2024</span>
259                </div>
260            </div>
261        </div>
262    </div>
263</header>
264
265
266
267
268
269
270<!-- Post Content -->
271<article>
272    <div class="container">
273        <div class="row">
274
275    <!-- Post Container -->
276            <div class="
277                col-lg-8 col-lg-offset-2
278                col-md-10 col-md-offset-1
279                post-container">
280
281                <!-- Multi-Lingual -->
282                
283
284				<blockquote>
285  <p>仓库已开源:<a href="https://github.com/poos/NoLogMacro">NoLogMacro</a>。</p>
286</blockquote>
287
288<h1 id="swift-macro">Swift Macro</h1>
289
290<p>Swift Macro 是 2023 WWDC 推出的。对于Swift Macro的介绍文章不少,但是大部分都是官方视频的翻译,例子和一些问题介绍处理真的是比较少。本文还真的有可能是第一个实践应用的…</p>
291
292<h2 id="特点">特点</h2>
293
294<p>简单了解 WWDC https://developer.apple.com/videos/play/wwdc2023/10166/ 后,应该能得出一下结论:</p>
295
296<p>Swfit Macro 大概分两种,表达式宏(#),和扩展宏(@)。这么一想限制还真多,但是保证了swift macro的安全性,就是说它支持类型判断,泛型等。</p>
297
298<h1 id="swift-macro-和-oslog-的-碰撞">Swift Macro 和 OSLog 的 碰撞</h1>
299
300<p>Xcode15后,debug控制台提供了非常有用的日志输入,其中包括了可直接定位到代码行的功能。</p>
301
302<p>初见便想到,如果我们自己的日志库能够实现相应的功能,那调试bug不是手到擒来。</p>
303
304<p>要支持的功能很简单:</p>
305<ul>
306  <li>支持日志,转发到自己的日志库处理</li>
307  <li>支持代码行定位</li>
308</ul>
309
310<p>咱就是说,搞个统一的消息转发,然后在使用宏进行OSLog处理,把两个表达式用宏定义,如果验证代码行能正确定位即可。</p>
311
312<p>预验证确实能正确定位到代码行,那么可以开干了。</p>
313
314<h2 id="火花1仅支持单行表达式">火花1,仅支持单行表达式</h2>
315
316<p>根据WWDC的文档,搞起:</p>
317<ul>
318  <li>项目创建</li>
319  <li>宏定义</li>
320  <li>测试success</li>
321  <li>duang,运行起来报错了</li>
322</ul>
323
324<p><img src="https://poos.github.io/img/post-swift-macro.png" alt="img" /></p>
325
326<p>
326这个报错没有有用信息,忽然想到表达式可以用于初始化变量,是不是有可能不支持多个表达式,通过修改宏,始终不能正确编译。</p>
327
328<p>既然<strong>Swift 表达式宏不支持多个表达式</strong>,那么就用单行表达式吧。</p>
329
330<h1 id="火花2不支持闭包">火花2,不支持闭包</h1>
331
332<p>通过使用闭包,这样就能执行方法,然后再闭包里面执行log收归或者调用系统log即可。</p>
333
334<p>方式1:</p>
335<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
3362
3373
3384
3395
340</pre></td><td class="rouge-code"><pre>Logger()
341.sync {
342    NoLog.noLog("msg", attrs: ["a": 1])
343}
344.log("msg")
345</pre></td></tr></tbody></table></code></pre></div></div>
346<p>可以正确定位到代码行,<strong>但是,NoLog消息中心收到的消息始终是第一个</strong>。
347方式2:</p>
348
349<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
3502
3513
3524
3535
354</pre></td><td class="rouge-code"><pre>NoLog
355.sync {
356    Logger().log("msg")
357}
358.noLog("msg", attrs: ["a": 1])
359</pre></td></tr></tbody></table></code></pre></div></div>
360<p><strong>不能正确定位到代码行</strong>,NoLog消息中心收到的正确。</p>
361
362<p>这下闭包的路堵死了。猜测应该是宏展开,block被视为常量,最终展开时候相当于一个全局的block。</p>
363
364<h2 id="链式语法">链式语法</h2>
365<p>条条大路,那么试试链式语法看看:</p>
366<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
3672
3683
369</pre></td><td class="rouge-code"><pre>Logger()
370.noLog("msg", attrs: ["a": 1])
371.log("msg")
372</pre></td></tr></tbody></table></code></pre></div></div>
373<p>还是这个方法ok,既能够显示到代码行,也能到消息中心打出日志。剩下的就剩下封装宏了。按照文档一步步来即可。</p>
374
375<p>那最后,简单看下 OC define 和 Swfit Macro 的区别:</p>
376
377<table>
378  <thead>
379    <tr>
380      <th>·</th>
381      <th>OC define</th>
382      <th>Swfit Macro</th>
383    </tr>
384  </thead>
385  <tbody>
386    <tr>
387      <td>代码编辑器类型检测</td>
388      <td>N</td>
389      <td>Y</td>
390    </tr>
391    <tr>
392      <td>定义单个表达式</td>
393      <td>Y</td>
394      <td>Y</td>
395    </tr>
396    <tr>
397      <td>定义多个表达式</td>
398      <td>Y</td>
399      <td>N</td>
400    </tr>
401    <tr>
402      <td>定义各种结构的扩展</td>
403      <td>Y</td>
404      <td>Y</td>
405    </tr>
406  </tbody>
407</table>
408
409<p>也就是说,Swift Macro 在保证类型安全的情况下,支持单个表达式和数据结构的宏。</p>
410
411<h1 id="开源库">开源库</h1>
412
413<p><strong><a href="https://github.com/poos/NoLogMacro">Not Only Log</a></strong>,(灵感来自nosql),比较贴切的有木有。</p>
414
415<p><strong>先看效果,常规方法的log无法定位到正确的代码行。通过本库提供的宏方法可以完美的以宏的方式支持,非常轻量级。</strong></p>
416
417<p><img src="https://poos.github.io/img/post-swift-macro2.gif" alt="img" /></p>
418
419<h2 id="使用">使用</h2>
420<p>库已开源。使用相当简单,但是考虑到很多小伙伴没使用过 swift package,特别录制了一些步骤:</p>
421
422<ul>
423  <li>右键添加 Package,通过链接 <code class="language-plaintext highlighter-rouge">https://github.com/poos/NoLogMacro</code> 添加,同时选择 Lib 和 Client</li>
424  <li><code class="language-plaintext highlighter-rouge">import OSLog</code> 和 <code class="language-plaintext highlighter-rouge">import NoLogMacro</code>,初始化 <code class="language-plaintext highlighter-rouge">NoLogger.callback</code>(可选)</li>
425  <li>使用<code class="language-plaintext highlighter-rouge">#noLog()</code> 代替 <code class="language-plaintext highlighter-rouge">Logger().log()</code></li>
426</ul>
427
428<p>gif:</p>
429
430<p><img src="https://poos.github.io/img/post-swift-macro3.gif" alt="img" /></p>
431
432<h3 id="code">Code</h3>
433
434<p>
434具体代码,一共就几行,起飞:</p>
435
436<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
4372
4383
4394
4405
4416
4427
4438
4449
44510
44611
44712
44813
44914
45015
45116
45217
45318
45419
45520
45621
45722
45823
45924
46025
46126
46227
46328
46429
46530
46631
46732
46833
46934
470</pre></td><td class="rouge-code"><pre>//import
471import OSLog
472import NoLogMacro
473
474// set once
475NoLogger.callback = { (type: OSLogType, message: String, attrs: Dictionary&lt;String, Any&gt;?) in
476    print("simple type: \(type) message: \(message) dic: \(String(describing: attrs))")
477}
478
479
480// all log can send to `NoLogger.callback`
481// **can be located to a line**
482
483// same with `Logger().log(level: .default, "default")`
484#noLog("message")
485// and more custom info
486#noLogError("error", attrs: ["code": 404])
487
488// others
489Logger().log(level: .info, "info")
490#noLogInfo("info")
491#noLogInfo("info", attrs: ["a": 2])
492
493Logger().log(level: .debug, "debug")
494#noLogDebug("debug")
495#noLogDebug("debug", attrs: ["a": 3])
496
497Logger().log(level: .error, "error")
498#noLogError("error")
499#noLogError("error", attrs: ["a": 4])
500
501Logger().log(level: .fault, "fault")
502#noLogFault("fault")
503#noLogFault("fault", attrs: ["a": 5])
504</pre></td></tr></tbody></table></code></pre></div></div>
505
506<h2 id="todo-list">TODO List</h2>
507
508<p>当然还有几个TODO项,just waiting 一下子。</p>
509
510<ul>
511  <li>support <code class="language-plaintext highlighter-rouge">Logger(subsystem: &lt;#T##String#&gt;, category: &lt;#T##String#&gt;)</code></li>
512  <li>brew iOS 14, using print support</li>
513  <li>using Swift Macro writing this lib</li>
514</ul>
515
516
517                <hr style="visibility: hidden;">
518                <ul class="pager">
519                    
520                    <li class="previous">
521                        <a href="/2024/03/30/apm_launch_time/" data-toggle="tooltip" data-placement="top" title="优化app启动">
522                        Previous<br>
523                        <span>优化app启动</span>
524                        </a>
525                    </li>
526                    
527                    
528                </ul>
529                <hr style="visibility: hidden;">
530
531                
532                <!-- disqus 评论框 start -->
533                <div class="comment">
534                    <div id="disqus_thread" class="disqus-thread"></div>
535                </div>
536                <!-- disqus 评论框 end -->
537                
538
539                
540            </div>  
541
542    <!-- Side Catalog Container -->
543        
544            <div class="
545                col-lg-2 col-lg-offset-0
546                visible-lg-block
547                sidebar-container
548                catalog-container">
549                <div class="side-catalog">
550                    <hr class="hidden-sm hidden-xs">
551                    <h5>
552                        <a class="catalog-toggle" href="#">CATALOG</a>
553                    </h5>
554                    <ul class="catalog-body"></ul>
555                </div>
556            </div>
557        
558
559    <!-- Sidebar Container -->
560            <div class="
561                col-lg-8 col-lg-offset-2
562                col-md-10 col-md-offset-1
563                sidebar-container">
564
565                <!-- Featured Tags -->
566                
567
568
569<section>
570    
571        <hr class="hidden-sm hidden-xs">
572    
573    <h5><a href="/archive/">FEATURED TAGS</a></h5>
574    <div class="tags">
575        
576        
577        
578        
579        
580        
581                <a data-sort="0078" 
582                    href="/archive/?tag=Note"
583                    title="Note"
584                    rel="4">Note</a>
585        
586                <a data-sort="0047" 
587                    href="/archive/?tag=Summary"
588                    title="Summary"
589                    rel="35">Summary</a>
590        
591                <a data-sort="0053" 
592                    href="/archive/?tag=Code"
593                    title="Code"
594                    rel="29">Code</a>
595        
596                <a data-sort="0067" 
597                    href="/archive/?tag=Design"
598                    title="Design"
599                    rel="15">Design</a>
600        
601                <a data-sort="0071" 
602                    href="/archive/?tag=Linter"
603                    title="Linter"
604                    rel="11">Linter</a>
605        
606                <a data-sort="0075" 
607                    href="/archive/?tag=Flutter"
608                    title="Flutter"
609                    rel="7">Flutter</a>
610        
611                <a data-sort="0076" 
612                    href="/archive/?tag=APM"
613                    title="APM"
614                    rel="6">APM</a>
615        
616                <a data-sort="0076" 
617                    href="/archive/?tag=Book"
618                    title="Book"
619                    rel="6">Book</a>
620        
621                <a data-sort="0076" 
622                    href="/archive/?tag=Module"
623                    title="Module"
624                    rel="6">Module</a>
625        
626                <a data-sort="0077" 
627                    href="/archive/?tag=Debug"
628                    title="Debug"
629                    rel="5">Debug</a>
630        
631                <a data-sort="0077" 
632                    href="/archive/?tag=Easter+eggs"
633                    title="Easter eggs"
634                    rel="5">Easter eggs</a>
635        
636                <a data-sort="0077" 
637                    href="/archive/?tag=Improve"
638                    title="Improve"
639                    rel="5">Improve</a>
640        
641                <a data-sort="0077" 
642                    href="/archive/?tag=Knowledge"
643                    title="Knowledge"
644                    rel="5">Knowledge</a>
645        
646                <a data-sort="0077" 
647                    href="/archive/?tag=Repo"
648                    title="Repo"
649                    rel="5">Repo</a>
650        
651                <a data-sort="0078" 
652                    href="/archive/?tag=Script"
653                    title="Script"
654                    rel="4">Script</a>
655        
656                <a data-sort="0078" 
657                    href="/archive/?tag=Tool"
658                    title="Tool"
659                    rel="4">Tool</a>
660        
661                <a data-sort="0079" 
662                    href="/archive/?tag=CICD"
663                    title="CICD"
664                    rel="3">CICD</a>
665        
666                <a data-sort="0079" 
667                    href="/archive/?tag=Objective-C"
668                    title="Objective-C"
669                    rel="3">Objective-C</a>
670        
671                <a data-sort="0079" 
672                    href="/archive/?tag=RxSwift"
673                    title="RxSwift"
674                    rel="3">RxSwift</a>
675        
676                <a data-sort="0079" 
677                    href="/archive/?tag=Storyboard"
678                    title="Storyboard"
679                    rel="3">Storyboard</a>
680        
681                <a data-sort="0079" 
682                    href="/archive/?tag=Swifter+Tips"
683                    title="Swifter Tips"
684                    rel="3">Swifter Tips</a>
685        
686                <a data-sort="0079" 
687                    href="/archive/?tag=Texture"
688                    title="Texture"
689                    rel="3">Texture</a>
690        
691                <a data-sort="0079" 
692                    href="/archive/?tag=Toolchain"
693                    title="Toolchain"
694                    rel="3">Toolchain</a>
695        
696                <a data-sort="0080" 
697                    href="/archive/?tag=Algorithm"
698                    title="Algorithm"
699                    rel="2">Algorithm</a>
700        
701                <a data-sort="0080" 
702                    href="/archive/?tag=Functional+Programming"
703                    title="Functional Programming"
704                    rel="2">Functional Programming</a>
705        
706                <a data-sort="0080" 
707                    href="/archive/?tag=Project"
708                    title="Project"
709                    rel="2">Project</a>
710    </div>
711</section>
712
713
714                <!-- Friends Blog -->
715                
716<hr>
717<h5>FRIENDS</h5>
718<ul class="list-inline">
719  
720  <li><a href="https://github.com/poos">github/poos</a></li>
721  
722  <li><a href="https://gitee.com/poos">gitee/poos</a></li>
723  
724  <li><a href="https://my.oschina.net/bieshixuan">oschina/bieshixuan</a></li>
725  
726</ul>
727
728            </div>
729        </div>
730    </div>
731</article>
732
733<!-- add support for mathjax by voleking-->
734
735
736
737
738
739
740<!-- disqus 公共JS代码 start (一个网页只需插入一次) -->
741<script type="text/javascript">
742    /* * * CONFIGURATION VARIABLES * * */
743    var disqus_shortname = "poos";
744    var disqus_identifier = "/2024/05/11/swiftmacro";
745    var disqus_url = "https://poos.github.io/2024/05/11/swiftmacro/";
746
747    (function() {
748        var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
749        dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
750        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
751    })();
752</script>
752
753<!-- disqus 公共JS代码 end -->
754
755
756
757
758<!-- async load function -->
759<script>
760    function async(u, c) {
761      var d = document, t = 'script',
762          o = d.createElement(t),
763          s = d.getElementsByTagName(t)[0];
764      o.src = u;
765      if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
766      s.parentNode.insertBefore(o, s);
767    }
768</script>
768
769<!-- anchor-js, Doc:http://bryanbraun.github.io/anchorjs/ -->
770<script>
771    async("//cdnjs.cloudflare.com/ajax/libs/anchor-js/1.1.1/anchor.min.js",function(){
772        anchors.options = {
773          visible: 'hover',
774          placement: 'right',
775          // icon: '#'
776        };
777        anchors.add().remove('.intro-header h1').remove('.subheading').remove('.sidebar-container h5');
778    })
779</script>
779
780<style>
781    /* place left on bigger screen */
782    @media all and (min-width: 800px) {
783        .anchorjs-link{
784            position: absolute;
785            left: -0.75em;
786            font-size: 1.1em;
787            margin-top : -0.1em;
788        }
789    }
790</style>
791
792
793
794    <!-- Footer -->
795<footer>
796    <div class="container">
797        <div class="row">
798            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
799                <!-- SNS Link -->
800                
801
802
803<ul class="list-inline text-center">
804
805
806  
807  
808  
809  
810  <li>
811    <a target="_blank" href="http://weibo.com/poos_github.io">
812      <span class="fa-stack fa-lg">
813        <i class="fa fa-circle fa-stack-2x"></i>
814        <i class="fa fa-weibo fa-stack-1x fa-inverse"></i>
815      </span>
816    </a>
817  </li>
818  
819  
820  
821  <li>
822    <a target="_blank" href="https://github.com/poos">
823      <span class="fa-stack fa-lg">
824        <i class="fa fa-circle fa-stack-2x"></i>
825        <i class="fa fa-github fa-stack-1x fa-inverse"></i>
826      </span>
827    </a>
828  </li>
829  
830  
831</ul>
832
833                <p class="copyright text-muted">
834                    Copyright &copy; Shown's blog 2024
835                    <br>
836                    Powered by <a href="https://github.com/poos/poos.github.io">Shixuan'S Blog</a> |
837                    <iframe
838                        style="margin-left: 2px; margin-bottom:-5px;"
839                        frameborder="0" scrolling="0" width="100px" height="20px"
840                        src="https://ghbtns.com/github-btn.html?user=poos&repo=poos.github.io&type=star&count=true" >
841                    </iframe>
842                </p>
843            </div>
844        </div>
845    </div>
846</footer>
847
848<!-- jQuery -->
849<script src="/js/jquery.min.js "></script>
849
850
851<!-- Bootstrap Core JavaScript -->
852<!-- Currently, only navbar scroll-down effect at desktop still depends on this -->
853<script src="/js/bootstrap.min.js "></script>
853
854
855<!-- Custom Theme JavaScript -->
856<script src="/js/hux-blog.min.js "></script>
856
857
858<!-- Simple Jekyll Search -->
859<script src="/js/simple-jekyll-search.min.js"></script>
859
860
861<!-- Service Worker -->
862
863<script src="/js/snackbar.js "></script>
vendor: 1 bytes, line 863
863
864<script src="/js/sw-registration.js "></script>
864
865
866
867<!-- async load function -->
868<script>
869    function async(u, c) {
870        var d = document, t = 'script',
871            o = d.createElement(t),
872            s = d.getElementsByTagName(t)[0];
873        o.src = u;
874        if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
875        s.parentNode.insertBefore(o, s);
876    }
877</script>
877
878
879<!--
880     Because of the native support for backtick-style fenced code blocks
881     right within the Markdown is landed in Github Pages,
882     From V1.6, There is no need for Highlight.js,
883     so Huxblog drops it officially.
884
885     - https://github.com/blog/2100-github-pages-now-faster-and-simpler-with-jekyll-3-0
886     - https://help.github.com/articles/creating-and-highlighting-code-blocks/
887     - https://github.com/jneen/rouge/wiki/list-of-supported-languages-and-lexers
888-->
889<!--
890    
890<script>
891        async("http://cdn.bootcss.com/highlight.js/8.6/highlight.min.js", function(){
892            hljs.initHighlightingOnLoad();
893        })
894    </script>
894
895    <link href="http://cdn.bootcss.com/highlight.js/8.6/styles/github.min.css" rel="stylesheet">
896-->
897
898
899
900
901
902
903
904<!--fastClick.js -->
905<script>
906    async("//cdnjs.cloudflare.com/ajax/libs/fastclick/1.0.6/fastclick.min.js", function () {
907        var $nav = document.querySelector("nav");
908        if ($nav) FastClick.attach($nav);
909    })
910</script>
910
911
912
913<!-- Google Analytics -->
914
915
916
917<!-- Baidu Tongji -->
918
919
920
921<!-- Side Catalog -->
922
923<script type="text/javascript">
924    function generateCatalog(selector) {
925
926        // interop with multilangual 
927        if ('' == 'true') {
928            _containerSelector = 'div.post-container.active'
929        } else {
930            _containerSelector = 'div.post-container'
931        }
932
933        // init
934        var P = $(_containerSelector), a, n, t, l, i, c;
935        a = P.find('h1,h2,h3,h4,h5,h6');
936
937        // clean
938        $(selector).html('')
939
940        // appending
941        a.each(function () {
942            n = $(this).prop('tagName').toLowerCase();
943            i = "#" + $(this).prop('id');
944            t = $(this).text();
945            c = $('<a href="' + i + '" rel="nofollow">' + t + '</a>');
946            l = $('<li class="' + n + '_nav"></li>').append(c);
947            $(selector).append(l);
948        });
949        return true;
950    }
951
952    generateCatalog(".catalog-body");
953
954    // toggle side catalog
955    $(".catalog-toggle").click((function (e) {
956        e.preventDefault();
957        $('.side-catalog').toggleClass("fold")
958    }))
959
960    /*
961     * Doc: https://github.com/davist11/jQuery-One-Page-Nav
962     * Fork by Hux to support padding
963     */
964    async("/js/jquery.nav.js", function () {
965        $('.catalog-body').onePageNav({
966            currentClass: "active",
967            changeHash: !1,
968            easing: "swing",
969            filter: "",
970            scrollSpeed: 700,
971            scrollOffset: 0,
972            scrollThreshold: .2,
973            begin: null,
974            end: null,
975            scrollChange: null,
976            padding: 80
977        });
978    });
979</script>
979
980
981
982
983<!-- Multi-Lingual -->
984
985
986<!-- Simple Jekyll Search -->
987<script>
988    // https://stackoverflow.com/questions/1912501/unescape-html-entities-in-javascript
989    function htmlDecode(input) {
990        var e = document.createElement('textarea');
991        e.innerHTML = input;
992        // handle case of empty input
993        return e.childNodes.length === 0 ? "" : e.childNodes[0].nodeValue;
994    }
995
996    SimpleJekyllSearch({
997        searchInput: document.getElementById('search-input'),
998        resultsContainer: document.getElementById('search-results'),
999        json: '/search.json',
1000        searchResultTemplate: '<div class="post-preview item"><a href="{url}"><h2 class="post-title">{title}</h2><h3 class="post-subtitle">{subtitle}</h3><hr></a></div>',
1001        noResultsText: 'No results',
1002        limit: 50,
1003        fuzzy: false,
1004        // a hack to get escaped subtitle unescaped. for some reason, 
1005        // post.subtitle w/o escape filter nuke entire search.
1006        templateMiddleware: function (prop, value, template) {
1007            if (prop === 'subtitle' || prop === 'title') {
1008                if (value.indexOf("code")) {
1009                    return htmlDecode(value);
1010                } else {
1011                    return value;
1012                }
1013            }
1014        }
1015    });
1016
1017    $(document).ready(function () {
1018        var $searchPage = $('.search-page');
1019        var $searchOpen = $('.search-icon');
1020        var $searchClose = $('.search-icon-close');
1021        var $searchInput = $('#search-input');
1022        var $body = $('body');
1023
1024        $searchOpen.on('click', function (e) {
1025            e.preventDefault();
1026            $searchPage.toggleClass('search-active');
1027            var prevClasses = $body.attr('class') || '';
1028            setTimeout(function () {
1029                $body.addClass('no-scroll');
1030            }, 400)
1031
1032            if ($searchPage.hasClass('search-active')) {
1033                $searchClose.on('click', function (e) {
1034                    e.preventDefault();
1035                    $searchPage.removeClass('search-active');
1036                    $body.attr('class', prevClasses);  // from closure 
1037                });
1038                $searchInput.focus();
1039            }
1040        });
1041    });
1042</script>
1042
1043
1044
1045<!-- Image to hack wechat -->
1046<img src="/img/icon_wechat.png" width="0" height="0" />
1047<!-- Migrate from head to bottom, no longer block render and still work -->
1048
1049</body>
1050
1051</html>

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