/
index.html
459 lines (316 loc) · 23.6 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
459
<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>深入理解Tagged Pointer | 唐巧的博客</title>
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
<meta name="author" content="唐巧">
<meta name="description" content="版权说明本文为 InfoQ 中文站特供稿件,首发地址为:文章链接。如需转载,请与 InfoQ 中文站联系。 【摘要】:为了节省内存和提高执行效率,苹果提出了Tagged Pointer的概念。对于 64 位程序,引入 Tagged Pointer 后,相关逻辑能减少一半的内存占用,以及 3 倍的访问速度提升,100 倍的创建、销毁速度提升。本文从Tagged Pointer试图解决的问题入手,带领">
<meta property="og:type" content="article">
<meta property="og:title" content="深入理解Tagged Pointer">
<meta property="og:url" content="https://blog.devtang.com/2014/05/30/understand-tagged-pointer/index.html">
<meta property="og:site_name" content="唐巧的博客">
<meta property="og:description" content="版权说明本文为 InfoQ 中文站特供稿件,首发地址为:文章链接。如需转载,请与 InfoQ 中文站联系。 【摘要】:为了节省内存和提高执行效率,苹果提出了Tagged Pointer的概念。对于 64 位程序,引入 Tagged Pointer 后,相关逻辑能减少一半的内存占用,以及 3 倍的访问速度提升,100 倍的创建、销毁速度提升。本文从Tagged Pointer试图解决的问题入手,带领">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://blog.devtang.com/images/tagged_pointer_before.jpg">
<meta property="og:image" content="https://blog.devtang.com/images/tagged_pointer_after.jpg">
<meta property="og:image" content="https://blog.devtang.com/images/tagged_pointer_switch_64bit_simulator.jpg">
<meta property="og:image" content="https://blog.devtang.com/images/tagged_pointer_isa_forbidden.jpg">
<meta property="article:published_time" content="2014-05-30T06:15:29.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/tagged_pointer_before.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/05/30/understand-tagged-pointer/" title="深入理解Tagged Pointer" itemprop="url">深入理解Tagged Pointer</a>
</h1>
<p class="article-time">
<time datetime="2014-05-30T06:15:29.000Z" itemprop="datePublished"> 发表于 2014-05-30 06:15</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="#前言"><span class="toc-number">2.</span> <span class="toc-text">前言</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="#Tagged-Pointer"><span class="toc-number">4.</span> <span class="toc-text">Tagged Pointer</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="#isa-指针"><span class="toc-number">6.</span> <span class="toc-text">isa 指针</span></a></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>本文为 InfoQ 中文站特供稿件,首发地址为:<a href="http://www.infoq.com/cn/articles/deep-understanding-of-tagged-pointer" target="_blank" rel="noopener">文章链接</a>。如需转载,请与 InfoQ 中文站联系。</p>
<p>【摘要】:为了节省内存和提高执行效率,苹果提出了<code>Tagged Pointer</code>的概念。对于 64 位程序,引入 Tagged Pointer 后,相关逻辑能减少一半的内存占用,以及 3 倍的访问速度提升,100 倍的创建、销毁速度提升。本文从<code>Tagged Pointer</code>试图解决的问题入手,带领读者理解<code>Tagged Pointer</code>的实现细节和优势,最后指出了使用时的注意事项。</p>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在 2013 年 9 月,苹果推出了 <a href="http://en.wikipedia.org/wiki/IPhone_5S" target="_blank" rel="noopener">iPhone5s</a>,与此同时,iPhone5s 配备了首个采用 64 位架构的 <a href="http://en.wikipedia.org/wiki/Apple_A7" target="_blank" rel="noopener">A7 双核处理器</a>,为了节省内存和提高执行效率,苹果提出了<code>Tagged Pointer</code>的概念。对于 64 位程序,引入 Tagged Pointer 后,相关逻辑能减少一半的内存占用,以及 3 倍的访问速度提升,100 倍的创建、销毁速度提升。本文从<code>Tagged Pointer</code>试图解决的问题入手,带领读者理解<code>Tagged Pointer</code>的实现细节和优势,最后指出了使用时的注意事项。</p>
<h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><p>我们先看看原有的对象为什么会浪费内存。假设我们要存储一个 NSNumber 对象,其值是一个整数。正常情况下,如果这个整数只是一个 NSInteger 的普通变量,那么它所占用的内存是与 CPU 的位数有关,在 32 位 CPU 下占 4 个字节,在 64 位 CPU 下是占 8 个字节的。而指针类型的大小通常也是与 CPU 位数相关,一个指针所占用的内存在 32 位 CPU 下为 4 个字节,在 64 位 CPU 下也是 8 个字节。</p>
<p>所以一个普通的 iOS 程序,如果没有<code>Tagged Pointer</code>对象,从 32 位机器迁移到 64 位机器中后,虽然逻辑没有任何变化,但这种 NSNumber、NSDate 一类的对象所占用的内存会翻倍。如下图所示:</p>
<img src="/images/tagged_pointer_before.jpg" class="">
<p>我们再来看看效率上的问题,为了存储和访问一个 NSNumber 对象,我们需要在堆上为其分配内存,另外还要维护它的引用计数,管理它的生命期。这些都给程序增加了额外的逻辑,造成运行效率上的损失。</p>
<h2 id="Tagged-Pointer"><a href="#Tagged-Pointer" class="headerlink" title="Tagged Pointer"></a>Tagged Pointer</h2><p>为了改进上面提到的内存占用和效率问题,苹果提出了<code>Tagged Pointer</code>对象。由于 NSNumber、NSDate 一类的变量本身的值需要占用的内存大小常常不需要 8 个字节,拿整数来说,4 个字节所能表示的有符号整数就可以达到 20 多亿(注:2^31=2147483648,另外 1 位作为符号位),对于绝大多数情况都是可以处理的。</p>
<p>所以我们可以将一个对象的指针拆成两部分,一部分直接保存数据,另一部分作为特殊标记,表示这是一个特别的指针,不指向任何一个地址。所以,引入了<code>Tagged Pointer</code>对象之后,64 位 CPU 下 NSNumber 的内存图变成了以下这样:</p>
<img src="/images/tagged_pointer_after.jpg" class="">
<p>对此,我们也可以用 Xcode 做实验来验证。我们的实验代码如下:</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">char</span> * argv[])</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">@autoreleasepool</span> {</span><br><span class="line"> <span class="built_in">NSNumber</span> *number1 = @<span class="number">1</span>;</span><br><span class="line"> <span class="built_in">NSNumber</span> *number2 = @<span class="number">2</span>;</span><br><span class="line"> <span class="built_in">NSNumber</span> *number3 = @<span class="number">3</span>;</span><br><span class="line"> <span class="built_in">NSNumber</span> *numberFFFF = @(<span class="number">0xFFFF</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"number1 pointer is %p"</span>, number1);</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"number2 pointer is %p"</span>, number2);</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"number3 pointer is %p"</span>, number3);</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"numberffff pointer is %p"</span>, numberFFFF);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">UIApplicationMain</span>(argc, argv, <span class="literal">nil</span>, <span class="built_in">NSStringFromClass</span>([AppDelegate <span class="keyword">class</span>]));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>在该代码中,我们将几个 Number 类型的指针的值直接输出。需要注意的是,我们需要将模拟器切换成 64 位的 CPU 来测试,如下图所示:</p>
<img src="/images/tagged_pointer_switch_64bit_simulator.jpg" class="">
<p>运行之后,我们得到的结果如下,可以看到,除去最后的数字最末尾的 2 以及最开头的 0xb,其它数字刚好表示了相应 NSNumber 的值。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">number1 pointer is 0xb000000000000012</span><br><span class="line">number2 pointer is 0xb000000000000022</span><br><span class="line">number3 pointer is 0xb000000000000032</span><br><span class="line">numberFFFF pointer is 0xb0000000000ffff2</span><br></pre></td></tr></table></figure>
<p>可见,苹果确实是将值直接存储到了指针本身里面。我们还可以猜测,数字最末尾的 2 以及最开头的 0xb 是否就是苹果对于<code>Tagged Pointer</code>的特殊标记呢?我们尝试放一个 8 字节的长的整数到<code>NSNumber</code>实例中,对于这样的实例,由于<code>Tagged Pointer</code>无法将其按上面的压缩方式来保存,那么应该就会以普通对象的方式来保存,我们的实验代码如下:</p>
<figure class="highlight objc"><table><tr><td class="code"><pre><span class="line"><span class="built_in">NSNumber</span> *bigNumber = @(<span class="number">0xEFFFFFFFFFFFFFFF</span>);</span><br><span class="line"><span class="built_in">NSLog</span>(<span class="string">@"bigNumber pointer is %p"</span>, bigNumber);</span><br></pre></td></tr></table></figure>
<p>运行之后,结果如下,验证了我们的猜测,<code>bigNumber</code>的地址更像是一个普通的指针地址,和它本身的值看不出任何关系:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">bigNumber pointer is 0x10921ecc0</span><br></pre></td></tr></table></figure>
<p>可见,当 8 字节可以承载用于表示的数值时,系统就会以<code>Tagged Pointer</code>的方式生成指针,如果 8 字节承载不了时,则又用以前的方式来生成普通的指针。关于以上关于<code>Tag Pointer</code>的存储细节,我们也可以在 <a href="https://www.mikeash.com/pyblog/friday-qa-2012-07-27-lets-build-tagged-pointers.html" target="_blank" rel="noopener">这里</a> 找到相应的讨论,但是其中关于<code>Tagged Pointer</code>的实现细节与我们的实验并不相符,笔者认为可能是苹果更改了具体的实现细节,并且这并不影响<code>Tagged Pointer</code>我们讨论<code>Tagged Pointer</code>本身的优点。</p>
<h2 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h2><p>我们也可以在 WWDC2013 的《Session 404 Advanced in Objective-C》视频中,看到苹果对于<code>Tagged Pointer</code>特点的介绍:</p>
<ol>
<li><code>Tagged Pointer</code>专门用来存储小的对象,例如<code>NSNumber</code>和<code>NSDate</code></li>
<li><code>Tagged Pointer</code>指针的值不再是地址了,而是真正的值。所以,实际上它不再是一个对象了,它只是一个披着对象皮的普通变量而已。所以,它的内存并不存储在堆中,也不需要 malloc 和 free。</li>
<li>在内存读取上有着 3 倍的效率,创建时比以前快 106 倍。</li>
</ol>
<p>由此可见,苹果引入<code>Tagged Pointer</code>,不但减少了 64 位机器下程序的内存占用,还提高了运行效率。完美地解决了小内存对象在存储和访问效率上的问题。</p>
<h2 id="isa-指针"><a href="#isa-指针" class="headerlink" title="isa 指针"></a>isa 指针</h2><p><code>Tagged Pointer</code>的引入也带来了问题,即<code>Tagged Pointer</code>因为并不是真正的对象,而是一个伪对象,所以你如果完全把它当成对象来使,可能会让它露马脚。比如我在 <a href="/2013/10/15/objective-c-object-model/">《Objective-C 对象模型及应用》</a> 一文中就写道,所有对象都有 <code>isa</code> 指针,而<code>Tagged Pointer</code>其实是没有的,因为它不是真正的对象。<br>因为不是真正的对象,所以如果你直接访问<code>Tagged Pointer</code>的<code>isa</code>成员的话,在编译时将会有如下警告:</p>
<img src="/images/tagged_pointer_isa_forbidden.jpg" class="">
<p>对于上面的写法,应该换成相应的方法调用,如 <code>isKindOfClass</code> 和 <code>object_getClass</code>。只要避免在代码中直接访问对象的 isa 变量,即可避免这个问题。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>苹果将<code>Tagged Pointer</code>引入,给 64 位系统带来了内存的节省和运行效率的提高。<code>Tagged Pointer</code>通过在其最后一个 bit 位设置一个特殊标记,用于将数据直接保存在指针本身中。因为<code>Tagged Pointer</code>并不是真正的对象,我们在使用时需要注意不要直接访问其 isa 变量。</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/05/30/understand-tagged-pointer/" data-title="深入理解Tagged Pointer | 唐巧的博客" data-tsina="" class="share clearfix">
</div>
</div>
</footer>
</article>
<nav class="article-nav clearfix">
<div class="prev" >
<a href="/2014/06/01/interview-on-miqun/" title="专访《iOS测试指南》作者羋峮">
<strong>上一篇:</strong><br/>
<span>
专访《iOS测试指南》作者羋峮</span>
</a>
</div>
<div class="next">
<a href="/2014/05/25/use-cocoapod-to-manage-ios-lib-dependency/" title="用CocoaPods做iOS程序的依赖管理">
<strong>下一篇:</strong><br/>
<span>用CocoaPods做iOS程序的依赖管理
</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>