/
index.html
458 lines (317 loc) · 23.7 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>开源一个Key-Value存储工具类 | 唐巧的博客</title>
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
<meta name="author" content="唐巧">
<meta name="description" content="前言还记得大学刚学数据库那会儿,天真地以为世界上所有的存储都需要用数据库来做。后来毕业后,正值NOSQL流行,那时我在网易参与了网易微博的开发,我们当时使用了有道自己做的“BigTable”— OMAP来存储微博数据,那个时候才发现,其实Key-Value这种简单的存储也能搞定微博这类不太简单的存储逻辑。 相比MYSQL,当数据量上千万后,NOSQL的优势体现出来了:对于海量数据,NOSQL在存取">
<meta property="og:type" content="article">
<meta property="og:title" content="开源一个Key-Value存储工具类">
<meta property="og:url" content="https://blog.devtang.com/2014/10/03/opensouce-a-key-value-storage-tool/index.html">
<meta property="og:site_name" content="唐巧的博客">
<meta property="og:description" content="前言还记得大学刚学数据库那会儿,天真地以为世界上所有的存储都需要用数据库来做。后来毕业后,正值NOSQL流行,那时我在网易参与了网易微博的开发,我们当时使用了有道自己做的“BigTable”— OMAP来存储微博数据,那个时候才发现,其实Key-Value这种简单的存储也能搞定微博这类不太简单的存储逻辑。 相比MYSQL,当数据量上千万后,NOSQL的优势体现出来了:对于海量数据,NOSQL在存取">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://blog.devtang.com/images/key-value-store-setup.jpg">
<meta property="article:published_time" content="2014-10-03T06:06:18.000Z">
<meta property="article:modified_time" content="2020-02-08T18:08:22.161Z">
<meta property="article:author" content="唐巧">
<meta property="article:tag" content="iOS">
<meta property="article:tag" content="唐巧">
<meta property="article:tag" content="猿辅导">
<meta property="article:tag" content="开发">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://blog.devtang.com/images/key-value-store-setup.jpg">
<link rel="alternative" href="/atom.xml" title="唐巧的博客" type="application/atom+xml">
<link rel="icon" href="/img/favicon.png">
<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/%02.css">
<link rel="stylesheet" href="/.css">
<meta name="generator" content="Hexo 4.2.0"></head>
<body>
<header>
<div>
<div id="textlogo">
<h1 class="site-name"><a href="/" title="唐巧的博客">唐巧的博客</a></h1>
<h2 class="blog-motto">记录下自己学习的点滴</h2>
</div>
<div class="navbar"><a class="navbutton navmobile" href="#" title="菜单">
</a></div>
<nav class="animated">
<ul>
<ul>
<li><a href="/">首页</a></li>
<li><a href="/archives">归档</a></li>
<li><a href="/about">关于</a></li>
<li>
<form class="search" action="//google.com/search" method="get" accept-charset="utf-8">
<label>Search</label>
<input type="search" id="search" name="q" autocomplete="off" maxlength="20" placeholder="搜索" />
<input type="hidden" name="q" value="site:blog.devtang.com">
</form>
</li>
</ul>
</nav>
</div>
</header>
<div id="container">
<div id="main" class="post" itemscope itemprop="blogPost">
<article itemprop="articleBody">
<header class="article-info clearfix">
<h1 itemprop="name">
<a href="/2014/10/03/opensouce-a-key-value-storage-tool/" title="开源一个Key-Value存储工具类" itemprop="url">开源一个Key-Value存储工具类</a>
</h1>
<p class="article-time">
<time datetime="2014-10-03T06:06:18.000Z" itemprop="datePublished"> 发表于 2014-10-03 06:06</time>
</p>
</header>
<div class="article-content">
<div id="toc" class="toc-article">
<strong class="toc-title">文章目录</strong>
<ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#前言"><span class="toc-number">1.</span> <span class="toc-text">前言</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#iOS端的尝试"><span class="toc-number">2.</span> <span class="toc-text">iOS端的尝试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#实现方案"><span class="toc-number">3.</span> <span class="toc-text">实现方案</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#开源"><span class="toc-number">4.</span> <span class="toc-text">开源</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#集成说明"><span class="toc-number">5.</span> <span class="toc-text">集成说明</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#使用说明"><span class="toc-number">6.</span> <span class="toc-text">使用说明</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#打开(或创建)数据库"><span class="toc-number">6.1.</span> <span class="toc-text">打开(或创建)数据库</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#创建数据库表"><span class="toc-number">6.2.</span> <span class="toc-text">创建数据库表</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#读写数据"><span class="toc-number">6.3.</span> <span class="toc-text">读写数据</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#删除数据接口"><span class="toc-number">6.4.</span> <span class="toc-text">删除数据接口</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#更多接口"><span class="toc-number">6.5.</span> <span class="toc-text">更多接口</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#其它"><span class="toc-number">7.</span> <span class="toc-text">其它</span></a></li></ol>
</div>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>还记得大学刚学数据库那会儿,天真地以为世界上所有的存储都需要用数据库来做。后来毕业后,正值NOSQL流行,那时我在网易参与了网易微博的开发,我们当时使用了有道自己做的“BigTable”— OMAP来存储微博数据,那个时候才发现,其实Key-Value这种简单的存储也能搞定微博这类不太简单的存储逻辑。</p>
<p>相比MYSQL,当数据量上千万后,NOSQL的优势体现出来了:对于海量数据,NOSQL在存取速度上没有任何影响,另外,天生的多备份和分布式,也说数据安全和扩容变得异常容易。</p>
<h2 id="iOS端的尝试"><a href="#iOS端的尝试" class="headerlink" title="iOS端的尝试"></a>iOS端的尝试</h2><p>后来我从后台转做iOS端的开发,我就尝试了在iOS端直接使用Key-Value式的存储。经过在粉笔网、猿题库、小猿搜题三个客户端中的尝试后,我发现Key-Value式的存储不但完全能够满足大多数移动端开发的需求,而且非常适合移动端采用。主要原因是:移动端存储的数据量不会很大:</p>
<ul>
<li>如果是单机的应用(例如效率工具Clear),用户自己一个人创建的数据最多也就上万条。</li>
<li>如果是有服务端的应用(例如网易新闻,微博),那移动端通常不会保存全量的数据,每次会从服务器上获取数据,本地只是做一些内容的缓存而已,所以也不会有很大的数据量。</li>
</ul>
<p>如果数据量不大的话,那么在iOS端使用最简单直接的Key-Value存储就能带来开发上的效率优势。它能保证:</p>
<ol>
<li>Model层的代码编写简单,易于测试。</li>
<li>由于Value是JSON格式,所以在做Model字段更改时,易于扩展和兼容。</li>
</ol>
<h2 id="实现方案"><a href="#实现方案" class="headerlink" title="实现方案"></a>实现方案</h2><p>在存储引擎上,2年前我直接选择了Sqlite当做存储引擎,相当于每个数据库表只有Key,Value两个字段。后来,随着LevelDB的流行,业界也有一些应用采用了LevelDB来做iOS端的Key-Value存储引擎,例如开源的<a href="https://github.com/viewfinderco/viewfinder" target="_blank" rel="noopener">ViewFinder</a>。</p>
<p>因为LevelDB本身并不是为移动端设计的,我担心它过于占用内存,我自己也没有看到业界有在移动端针对LevelDB做很详细的测试,连LevelDB的iOS端移植都不是官方做的。加上我自己写的基于Sqlite的Key-Value存储用着也没有什么问题,所以我也就一直没有更换成LevelDB。</p>
<h2 id="开源"><a href="#开源" class="headerlink" title="开源"></a>开源</h2><p>经过两年的使用和测试,我认为它非常好用,而且代码也非常简单,只有不到400行。所以现在开源分享给大家,这个项目叫<a href="https://github.com/yuantiku/YTKKeyValueStore" target="_blank" rel="noopener">YTKKeyValueStore</a>,项目代码在<a href="https://github.com/yuantiku/YTKKeyValueStore" target="_blank" rel="noopener">这里</a>。</p>
<p>以下是一个简单的使用示例:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">YTKKeyValueStore *store = [[YTKKeyValueStore alloc] initDBWithName:@"test.db"];</span><br><span class="line">NSString *tableName = @"user_table";</span><br><span class="line">[store createTableWithName:tableName];</span><br><span class="line">// 保存</span><br><span class="line">NSString *key = @"1";</span><br><span class="line">NSDictionary *user = @{@"id": @1, @"name": @"tangqiao", @"age": @30};</span><br><span class="line">[store putObject:user withId:key intoTable:tableName];</span><br><span class="line">// 查询</span><br><span class="line">NSDictionary *queryUser = [store getObjectById:key fromTable:tableName];</span><br><span class="line">NSLog(@"query data result: %@", queryUser);</span><br></pre></td></tr></table></figure>
<h2 id="集成说明"><a href="#集成说明" class="headerlink" title="集成说明"></a>集成说明</h2><p>使用本项目,你需要将开源代码中的<code>YTKKeyValueStore.h</code>和<code>YTKKeyValueStore.m</code>添加到你的工程中,并且在工程设置的<code>Link Binary With Libraries</code>中,增加<code>libsqlite3.dylib</code>,如下图所示:</p>
<img src="/images/key-value-store-setup.jpg" class="">
<p>由于时间关系,当前还未提供Cocoapods方式集成。</p>
<h2 id="使用说明"><a href="#使用说明" class="headerlink" title="使用说明"></a>使用说明</h2><p>所有的接口都封装在<code>YTKKeyValueStore</code>类中。以下是一些常用方法说明。</p>
<h3 id="打开(或创建)数据库"><a href="#打开(或创建)数据库" class="headerlink" title="打开(或创建)数据库"></a>打开(或创建)数据库</h3><p>通过<code>initDBWithName</code>方法,即可在程序的<code>Document</code>目录打开指定的数据库文件。如果该文件不存在,则会创建一个新的数据库。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">// 打开名为test.db的数据库,如果该文件不存在,则创新一个新的。</span><br><span class="line">YTKKeyValueStore *store = [[YTKKeyValueStore alloc] initDBWithName:@"test.db"];</span><br></pre></td></tr></table></figure>
<h3 id="创建数据库表"><a href="#创建数据库表" class="headerlink" title="创建数据库表"></a>创建数据库表</h3><p>通过<code>createTableWithName</code>方法,我们可以在打开的数据库中创建表,如果表名已经存在,则会忽略该操作。如下所示:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">YTKKeyValueStore *store = [[YTKKeyValueStore alloc] initDBWithName:@"test.db"];</span><br><span class="line">NSString *tableName = @"user_table";</span><br><span class="line">// 创建名为user_table的表,如果已存在,则忽略该操作</span><br><span class="line">[store createTableWithName:tableName];</span><br></pre></td></tr></table></figure>
<h3 id="读写数据"><a href="#读写数据" class="headerlink" title="读写数据"></a>读写数据</h3><p><code>YTKKeyValueStore</code>类提供key-value的存储接口,存入的所有数据需要提供key以及其对应的value,读取的时候需要提供key来获得相应的value。</p>
<p><code>YTKKeyValueStore</code>类支持的value类型包括:NSString, NSNumber, NSDictionary和NSArray,为此提供了以下接口:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">- (void)putString:(NSString *)string withId:(NSString *)stringId intoTable:(NSString *)tableName;</span><br><span class="line">- (void)putNumber:(NSNumber *)number withId:(NSString *)numberId intoTable:(NSString *)tableName;</span><br><span class="line">- (void)putObject:(id)object withId:(NSString *)objectId intoTable:(NSString *)tableName;</span><br></pre></td></tr></table></figure>
<p>与此对应,有以下value为NSString, NSNumber, NSDictionary和NSArray的读取接口:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">- (NSString *)getStringById:(NSString *)stringId fromTable:(NSString *)tableName;</span><br><span class="line">- (NSNumber *)getNumberById:(NSString *)numberId fromTable:(NSString *)tableName;</span><br><span class="line">- (id)getObjectById:(NSString *)objectId fromTable:(NSString *)tableName;</span><br></pre></td></tr></table></figure>
<h3 id="删除数据接口"><a href="#删除数据接口" class="headerlink" title="删除数据接口"></a>删除数据接口</h3><p><code>YTKKeyValueStore</code>提供了以下接口用于删除数据。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">// 清除数据表中所有数据</span><br><span class="line">- (void)clearTable:(NSString *)tableName;</span><br><span class="line"></span><br><span class="line">// 删除指定key的数据</span><br><span class="line">- (void)deleteObjectById:(NSString *)objectId fromTable:(NSString *)tableName;</span><br><span class="line"></span><br><span class="line">// 批量删除一组key数组的数据</span><br><span class="line">- (void)deleteObjectsByIdArray:(NSArray *)objectIdArray fromTable:(NSString *)tableName;</span><br><span class="line"></span><br><span class="line">// 批量删除所有带指定前缀的数据</span><br><span class="line">- (void)deleteObjectsByIdPrefix:(NSString *)objectIdPrefix fromTable:(NSString *)tableName;</span><br></pre></td></tr></table></figure>
<h3 id="更多接口"><a href="#更多接口" class="headerlink" title="更多接口"></a>更多接口</h3><p><code>YTKKeyValueStore</code>还提供了以下接口来获取表示内部存储的key-value对象。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">// 获得指定key的数据</span><br><span class="line">- (YTKKeyValueItem *)getYTKKeyValueItemById:(NSString *)objectId fromTable:(NSString *)tableName;</span><br><span class="line">// 获得所有数据</span><br><span class="line">- (NSArray *)getAllItemsFromTable:(NSString *)tableName;</span><br></pre></td></tr></table></figure>
<p>由于<code>YTKKeyValueItem</code>类带有<code>createdTime</code>字段,可以获得该条数据的插入(或更新)时间,以便上层做复杂的处理(例如用来做缓存过期逻辑)。</p>
<h2 id="其它"><a href="#其它" class="headerlink" title="其它"></a>其它</h2><p>两年前写过不少测试用例,后来给弄丢了,所以现在开项项目中还没有测试用例。由于时间关系,更详细的使用说明稍后会更新到项目中。</p>
</div>
<footer class="article-footer clearfix">
<div class="article-catetags">
<div class="article-categories">
<span></span>
<a class="article-category-link" href="/categories/iOS/">iOS</a>
</div>
</div>
<div class="article-share" id="share">
<div data-url="https://blog.devtang.com/2014/10/03/opensouce-a-key-value-storage-tool/" data-title="开源一个Key-Value存储工具类 | 唐巧的博客" data-tsina="" class="share clearfix">
</div>
</div>
</footer>
</article>
<nav class="article-nav clearfix">
<div class="prev" >
<a href="/2014/10/12/ios-weekly-27/" title="iOS移动开发周报-第27期">
<strong>上一篇:</strong><br/>
<span>
iOS移动开发周报-第27期</span>
</a>
</div>
<div class="next">
<a href="/2014/10/01/startup-2nd-year-summary/" title="写在创业两周年">
<strong>下一篇:</strong><br/>
<span>写在创业两周年
</span>
</a>
</div>
</nav>
</div>
<div class="openaside"><a class="navbutton" href="#" title="显示侧边栏"></a></div>
<div id="asidepart">
<div class="closeaside"><a class="closebutton" href="#" title="隐藏侧边栏"></a></div>
<aside class="clearfix">
<div class="sponsor">
</div>
<div class="categorieslist">
<p class="asidetitle">分类</p>
<ul>
<li><a href="/categories/books-summary/" title="books summary">books summary</a></li>
<li><a href="/categories/iOS/" title="iOS">iOS</a></li>
<li><a href="/categories/iOS-weekly/" title="iOS weekly">iOS weekly</a></li>
<li><a href="/categories/mac/" title="mac">mac</a></li>
<li><a href="/categories/shell/" title="shell">shell</a></li>
<li><a href="/categories/summary/" title="summary">summary</a></li>
</ul>
</div>
<div class="weixin">
<br />
<p class="asidetitle">微信公众号</p>
<p>关注我的微信公众号,和我一起成长:</p>
<img src="/images/weixin-qr.jpg" width="230px" />
</div>
<div class="rsspart">
<a href="/atom.xml" target="_blank" title="rss">RSS 订阅</a>
</div>
</aside>
</div>
</div>
<footer><div id="footer" >
<div class="social-font" class="clearfix">
</div>
<p class="copyright" style="margin-top: 10px;">
Powered by <a href="http://hexo.io" target="_blank" title="hexo">hexo</a> and Theme by <a href="https://github.com/wuchong/jacman" target="_blank" title="Jacman">Jacman</a> © 2020
<a href="/about" target="_blank" title="唐巧">唐巧</a>
</p>
</div>
</footer>
<script src="/js/jquery-2.0.3.min.js"></script>
<script src="/js/jquery.imagesloaded.min.js"></script>
<script src="/js/gallery.js"></script>
<script src="/js/jquery.qrcode-0.12.0.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$('.navbar').click(function(){
$('header nav').toggleClass('shownav');
});
var myWidth = 0;
function getSize(){
if( typeof( window.innerWidth ) == 'number' ) {
myWidth = window.innerWidth;
} else if( document.documentElement && document.documentElement.clientWidth) {
myWidth = document.documentElement.clientWidth;
};
};
var m = $('#main'),
a = $('#asidepart'),
c = $('.closeaside'),
o = $('.openaside');
c.click(function(){
a.addClass('fadeOut').css('display', 'none');
o.css('display', 'block').addClass('fadeIn');
m.addClass('moveMain');
});
o.click(function(){
o.css('display', 'none').removeClass('beforeFadeIn');
a.css('display', 'block').removeClass('fadeOut').addClass('fadeIn');
m.removeClass('moveMain');
});
$(window).scroll(function(){
o.css("top",Math.max(80,260-$(this).scrollTop()));
});
$(window).resize(function(){
getSize();
if (myWidth >= 1024) {
$('header nav').removeClass('shownav');
}else{
m.removeClass('moveMain');
a.css('display', 'block').removeClass('fadeOut');
o.css('display', 'none');
}
});
});
</script>
<script type="text/javascript">
$(document).ready(function(){
var ai = $('.article-content>iframe'),
ae = $('.article-content>embed'),
t = $('#toc'),
ta = $('#toc.toc-aside'),
o = $('.openaside'),
c = $('.closeaside');
if(ai.length>0){
ai.wrap('<div class="video-container" />');
};
if(ae.length>0){
ae.wrap('<div class="video-container" />');
};
c.click(function(){
ta.css('display', 'block').addClass('fadeIn');
});
o.click(function(){
ta.css('display', 'none');
});
$(window).scroll(function(){
ta.css("top",Math.max(140,320-$(this).scrollTop()));
});
});
</script>
<script type="text/javascript">
$(document).ready(function(){
var $this = $('.share'),
url = $this.attr('data-url'),
encodedUrl = encodeURIComponent(url),
title = $this.attr('data-title'),
tsina = $this.attr('data-tsina'),
description = $this.attr('description');
var html = [
'<div class="hoverqrcode clearfix"></div>',
'<a class="overlay" id="qrcode"></a>',
'<a href="https://www.facebook.com/sharer.php?u=' + encodedUrl + '" class="article-share-facebook" target="_blank" title="Facebook"></a>',
'<a href="https://twitter.com/intent/tweet?url=' + encodedUrl + '" class="article-share-twitter" target="_blank" title="Twitter"></a>',
'<a href="#qrcode" class="article-share-qrcode" title="微信"></a>',
'<a href="http://widget.renren.com/dialog/share?resourceUrl=' + encodedUrl + '&srcUrl=' + encodedUrl + '&title=' + title +'" class="article-share-renren" target="_blank" title="人人"></a>',
'<a href="http://service.weibo.com/share/share.php?title='+title+'&url='+encodedUrl +'&ralateUid='+ tsina +'&searchPic=true&style=number' +'" class="article-share-weibo" target="_blank" title="微博"></a>',
'<span title="Share to"></span>'
].join('');
$this.append(html);
$('.hoverqrcode').hide();
var myWidth = 0;
function updatehoverqrcode(){
if( typeof( window.innerWidth ) == 'number' ) {
myWidth = window.innerWidth;
} else if( document.documentElement && document.documentElement.clientWidth) {
myWidth = document.documentElement.clientWidth;
};
var qrsize = myWidth > 1024 ? 200:100;
var options = {render: 'image', size: qrsize, fill: '#2ca6cb', text: url, radius: 0.5, quiet: 1};
var p = $('.article-share-qrcode').position();
$('.hoverqrcode').empty().css('width', qrsize).css('height', qrsize)
.css('left', p.left-qrsize/2+20).css('top', p.top-qrsize-10)
.qrcode(options);
};
$(window).resize(function(){
$('.hoverqrcode').hide();
});
$('.article-share-qrcode').click(function(){
updatehoverqrcode();
$('.hoverqrcode').toggle();
});
$('.article-share-qrcode').hover(function(){}, function(){
$('.hoverqrcode').hide();
});
});
</script>
<!-- Analytics Begin -->
<script type="text/javascript">
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-28029597-1', '');
ga('send', 'pageview');
</script>
<!-- Analytics End -->
<!-- Totop Begin -->
<div id="totop">
<a title="返回顶部"><img src="/img/scrollup.png"/></a>
</div>
<script src="/js/totop.js"></script>
<!-- Totop End -->
<!-- MathJax Begin -->
<!-- mathjax config similar to math.stackexchange -->
<!-- MathJax End -->
<!-- Tiny_search Begin -->
<!-- Tiny_search End -->
</body>
</html>