-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
1050 lines (671 loc) · 161 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
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Arnold的技术博客</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="description" content="iOS开发,喜欢算法">
<meta property="og:type" content="website">
<meta property="og:title" content="Arnold的技术博客">
<meta property="og:url" content="www.arnold134777.com/index.html">
<meta property="og:site_name" content="Arnold的技术博客">
<meta property="og:description" content="iOS开发,喜欢算法">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Arnold的技术博客">
<meta name="twitter:description" content="iOS开发,喜欢算法">
<link rel="alternative" href="/atom.xml" title="Arnold的技术博客" type="application/atom+xml">
<link rel="icon" href="/favicon.png">
<link rel="stylesheet" href="/css/style.css" type="text/css">
<script type="text/javascript">
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "//hm.baidu.com/hm.js?b751aecb362d3c2a6826d793f68d6a94";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
</head>
<body>
<div id="container">
<div class="left-col">
<div class="overlay"></div>
<div class="intrude-less">
<header id="header" class="inner">
<a href="/" class="profilepic">
<img lazy-src="/assert/arnold.png" class="js-avatar">
</a>
<hgroup>
<h1 class="header-author"><a href="/">Arnold</a></h1>
</hgroup>
<p class="header-subtitle">iOS开发,喜欢算法([email protected])</p>
<div class="switch-btn">
<div class="icon">
<div class="icon-ctn">
<div class="icon-wrap icon-house" data-idx="0">
<div class="birdhouse"></div>
<div class="birdhouse_holes"></div>
</div>
<div class="icon-wrap icon-ribbon hide" data-idx="1">
<div class="ribbon"></div>
</div>
<div class="icon-wrap icon-link hide" data-idx="2">
<div class="loopback_l"></div>
<div class="loopback_r"></div>
</div>
<div class="icon-wrap icon-me hide" data-idx="3">
<div class="user"></div>
<div class="shoulder"></div>
</div>
</div>
</div>
<div class="tips-box hide">
<div class="tips-arrow"></div>
<ul class="tips-inner">
<li>菜单</li>
<li>标签</li>
<li>友情链接</li>
<li>关于我</li>
</ul>
</div>
</div>
<div class="switch-area">
<div class="switch-wrap">
<section class="switch-part switch-part1">
<nav class="header-menu">
<ul>
<li><a href="/">主页</a></li>
<li><a href="/archives">所有文章</a></li>
</ul>
</nav>
<nav class="header-nav">
<div class="social">
<a class="github" target="_blank" href="http://arnold134777.github.io/" title="github">github</a>
</div>
</nav>
</section>
<section class="switch-part switch-part2">
<div class="widget tagcloud" id="js-tagcloud">
<a href="/tags/GCD/" style="font-size: 10px;">GCD</a> <a href="/tags/JSpatch/" style="font-size: 12.5px;">JSpatch</a> <a href="/tags/LintCode/" style="font-size: 20px;">LintCode</a> <a href="/tags/React-Native/" style="font-size: 12.5px;">React Native</a> <a href="/tags/Xcode/" style="font-size: 10px;">Xcode</a> <a href="/tags/devTool/" style="font-size: 12.5px;">devTool</a> <a href="/tags/git/" style="font-size: 10px;">git</a> <a href="/tags/iOS/" style="font-size: 15px;">iOS</a> <a href="/tags/动态规划/" style="font-size: 17.5px;">动态规划</a> <a href="/tags/算法/" style="font-size: 20px;">算法</a>
</div>
</section>
<section class="switch-part switch-part3">
<div id="js-friends">
<a target="_blank" class="main-nav-link switch-friends-link" href="http://blog.sina.com.cn/u/2423600462">Arnold的新郎博客</a>
</div>
</section>
<section class="switch-part switch-part4">
<div id="js-aboutme">iOS开发,喜欢算法</div>
</section>
</div>
</div>
</header>
</div>
</div>
<div class="mid-col">
<nav id="mobile-nav">
<div class="overlay">
<div class="slider-trigger"></div>
<h1 class="header-author js-mobile-header hide">Arnold</h1>
</div>
<div class="intrude-less">
<header id="header" class="inner">
<div class="profilepic">
<img lazy-src="/assert/arnold.png" class="js-avatar">
</div>
<hgroup>
<h1 class="header-author">Arnold</h1>
</hgroup>
<p class="header-subtitle">iOS开发,喜欢算法([email protected])</p>
<nav class="header-menu">
<ul>
<li><a href="/">主页</a></li>
<li><a href="/archives">所有文章</a></li>
<div class="clearfix"></div>
</ul>
</nav>
<nav class="header-nav">
<div class="social">
<a class="github" target="_blank" href="http://arnold134777.github.io/" title="github">github</a>
</div>
</nav>
</header>
</div>
</nav>
<div class="body-wrap">
<article id="post-EasyJsWebView-源码分析" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2016/11/14/EasyJsWebView-源码分析/" class="article-date">
<time datetime="2016-11-14T14:53:56.000Z" itemprop="datePublished">2016-11-14</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/11/14/EasyJsWebView-源码分析/">EasyJsWebView 源码分析</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<hr>
<p>最近在做hybrid相关的工作,项目中用到了<code>EasyJsWebView</code>,代码量不大,一直想分析一下它的具体实现,抽空写了这篇文章。</p>
<h1 id="1-前言">1.前言</h1><p>原生代码+h5页面+甚至React Native(或其他) 的方式开发移动客户端已经成为当前的主流趋势,因此老生常谈的一个问题就是原生代码与js的交互。原生代码中执行js代码,没什么可讲的直接webView执行js代码即可,本文主要由安卓的js调用原生的方式切入,分析iOS端是如何实现类似比较方便的调用的。</p>
<h1 id="2-安卓端(js_->_native_interface)">2.安卓端(js -> native interface)</h1><p>对安卓的开发不是很熟,只是列举一个简单的例子讲述这样一种方式。</p>
<ul>
<li>native端</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>{</span><br><span class="line"> ...</span><br><span class="line"> <span class="comment">// 添加一个对象, 让JS可以访问该对象的方法, 该对象中可以调用JS中的方法</span></span><br><span class="line"> webView.addJavascriptInterface(<span class="keyword">new</span> Contact(), <span class="string">"contact"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">Contact</span> </span>{</span><br><span class="line"> <span class="comment">//Html调用此方法传递数据</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">showcontacts</span><span class="params">()</span> </span>{</span><br><span class="line"> String json = <span class="string">"[{\"name\":\"zxx\", \"amount\":\"9999999\", \"phone\":\"18600012345\"}]"</span>; </span><br><span class="line"> <span class="comment">// 调用JS中的方法</span></span><br><span class="line"> webView.loadUrl(<span class="string">"javascript:show('"</span> + json + <span class="string">"')"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ul>
<li>h5端</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><html></span><br><span class="line"><span class="xml"><span class="tag"><<span class="title">body</span> <span class="attribute">onload</span>=<span class="value">"javascript:contact.showcontacts()"</span>></span></span><br><span class="line"> <span class="tag"><<span class="title">table</span> <span class="attribute">border</span>=<span class="value">"0"</span> <span class="attribute">width</span>=<span class="value">"100%"</span> <span class="attribute">id</span>=<span class="value">"personTable"</span> <span class="attribute">cellspacing</span>=<span class="value">"0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="title">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="title">td</span> <span class="attribute">width</span>=<span class="value">"30%"</span>></span>姓名<span class="tag"></<span class="title">td</span>></span></span><br><span class="line"> <span class="tag"><<span class="title">td</span> <span class="attribute">width</span>=<span class="value">"30%"</span> <span class="attribute">align</span>=<span class="value">"center"</span>></span>存款<span class="tag"></<span class="title">td</span>></span></span><br><span class="line"> <span class="tag"><<span class="title">td</span> <span class="attribute">align</span>=<span class="value">"center"</span>></span>电话<span class="tag"></<span class="title">td</span>></span></span><br><span class="line"> <span class="tag"></<span class="title">tr</span>></span></span><br><span class="line"> <span class="tag"></<span class="title">table</span>></span></span><br><span class="line"><span class="tag"></<span class="title">body</span>></span></span><br><span class="line"><span class="tag"></<span class="title">html</span>></span></span></span><br></pre></td></tr></table></figure>
<p>当h5页面加载时,<code>onload</code>方法执行,对应的native端中的<code>Contact</code>类中的<code>showcontacts</code>方法被执行。因此核心思想就是通过webView将native原生的类与自定义的js对象关联,js就可以直接通过这个js对象调用它的实例方法。</p>
<h1 id="3-iOS端(js_->_native_interface)">3.iOS端(js -> native interface)</h1><p>上述安卓的js调用native的方式是如此简单明了,不禁想如果iOS端也有如此实现的话,这样同时即保证安卓,iOS,h5的统一性也能让开发者只用关心交互的接口即可。因此便引出了<code>EasyJSWebView</code>的第三方的框架(基于说明2设计),下面从该框架的使用出发,分析框架的具体实现。</p>
<p>说明:</p>
<blockquote>
<ul>
<li>1.iOS端虽然也可以通过<code>JSContext</code>注入全局的方法但是达不到与安卓端统一</li>
<li>2.iOS端可以通过拦截h5请求的url,通过url的格式区分类或方法,但是这样不够直观,也达不到与安卓端统一</li>
</ul>
</blockquote>
<h1 id="4-EasyJsWebView">4.EasyJsWebView</h1><h2 id="4-1_EasyJsWebView使用">4.1 EasyJsWebView使用</h2><p>本文直接列举EasyJsWebView <a href="https://github.com/dukeland/EasyJSWebView" target="_blank" rel="external">Github README例子</a></p>
<ul>
<li>native端</li>
</ul>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">MyJSInterface</span> : <span class="title">NSObject</span></span></span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>) test;</span><br><span class="line">- (<span class="keyword">void</span>) testWithParam: (<span class="built_in">NSString</span>*) param;</span><br><span class="line">- (<span class="keyword">void</span>) testWithTwoParam: (<span class="built_in">NSString</span>*) param AndParam2: (<span class="built_in">NSString</span>*) param2;</span><br><span class="line"></span><br><span class="line">- (<span class="built_in">NSString</span>*) testWithRet;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 注入</span></span><br><span class="line">MyJSInterface* interface = [MyJSInterface new];</span><br><span class="line">[<span class="keyword">self</span><span class="variable">.myWebView</span> addJavascriptInterfaces:interface WithName:<span class="string">@"MyJSTest"</span>];</span><br><span class="line">[interface release];</span><br></pre></td></tr></table></figure>
<ul>
<li>js端</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">MyJSTest.test();</span><br><span class="line">MyJSTest.testWithParam(<span class="string">"ha:ha"</span>);</span><br><span class="line">MyJSTest.testWithTwoParamAndParam2(<span class="string">"haha1"</span>, <span class="string">"haha2"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> str = MyJSTest.testWithRet();</span><br></pre></td></tr></table></figure>
<h2 id="4-2_EasyJsWebView具体实现">4.2 EasyJsWebView具体实现</h2><h3 id="4-2-1_EasyJsWebView初始化">4.2.1 EasyJsWebView初始化</h3><figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">id</span>)init{</span><br><span class="line"> <span class="keyword">self</span> = [<span class="keyword">super</span> init];</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">self</span>) {</span><br><span class="line"> [<span class="keyword">self</span> initEasyJS];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">self</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>) initEasyJS{</span><br><span class="line"> <span class="keyword">self</span><span class="variable">.proxyDelegate</span> = [[EasyJSWebViewProxyDelegate alloc] init];</span><br><span class="line"> <span class="keyword">self</span><span class="variable">.delegate</span> = <span class="keyword">self</span><span class="variable">.proxyDelegate</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">- (<span class="keyword">void</span>) setDelegate:(<span class="keyword">id</span><<span class="built_in">UIWebViewDelegate</span>>)delegate{</span><br><span class="line"> <span class="keyword">if</span> (delegate != <span class="keyword">self</span><span class="variable">.proxyDelegate</span>){</span><br><span class="line"> <span class="keyword">self</span><span class="variable">.proxyDelegate</span><span class="variable">.realDelegate</span> = delegate;</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> [<span class="keyword">super</span> setDelegate:delegate]; </span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>初始化设置webView的delegate,实际的webView的回调的在<code>EasyJSWebViewProxyDelegate</code>中实现,因此我们主要关注<code>EasyJSWebViewProxyDelegate</code>中的webView的回调的实现即可。</p>
<h3 id="4-2-2_EasyJSWebViewProxyDelegate_webView回调实现">4.2.2 EasyJSWebViewProxyDelegate webView回调实现</h3><h4 id="4-2-2-1_webViewDidStartLoad回调实现">4.2.2.1 webViewDidStartLoad回调实现</h4><p>代码片段一:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSMutableString</span>* injection = [[<span class="built_in">NSMutableString</span> alloc] init];</span><br><span class="line"> </span><br><span class="line"><span class="comment">//inject the javascript interface</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">id</span> key <span class="keyword">in</span> <span class="keyword">self</span><span class="variable">.javascriptInterfaces</span>) {</span><br><span class="line"> <span class="built_in">NSObject</span>* interface = [<span class="keyword">self</span><span class="variable">.javascriptInterfaces</span> objectForKey:key];</span><br><span class="line"> </span><br><span class="line"> [injection appendString:<span class="string">@"EasyJS.inject(\""</span>];</span><br><span class="line"> [injection appendString:key];</span><br><span class="line"> [injection appendString:<span class="string">@"\", ["</span>];</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">unsigned</span> <span class="keyword">int</span> mc = <span class="number">0</span>;</span><br><span class="line"> Class cls = object_getClass(interface);</span><br><span class="line"> Method * mlist = class_copyMethodList(cls, &mc);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < mc; i++){</span><br><span class="line"> [injection appendString:<span class="string">@"\""</span>];</span><br><span class="line"> [injection appendString:[<span class="built_in">NSString</span> stringWithUTF8String:sel_getName(method_getName(mlist[i]))]];</span><br><span class="line"> [injection appendString:<span class="string">@"\""</span>];</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (i != mc - <span class="number">1</span>){</span><br><span class="line"> [injection appendString:<span class="string">@", "</span>];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> free(mlist);</span><br><span class="line"> </span><br><span class="line"> [injection appendString:<span class="string">@"]);"</span>];</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> <span class="built_in">NSString</span>* js = INJECT_JS;</span><br><span class="line"> <span class="comment">//inject the basic functions first</span></span><br><span class="line"> [webView stringByEvaluatingJavaScriptFromString:js];</span><br><span class="line"> <span class="comment">//inject the function interface</span></span><br><span class="line"> [webView stringByEvaluatingJavaScriptFromString:injection];</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<ul>
<li>遍历注入的接口的列表key</li>
<li>通过key获取注入类的实例</li>
<li>通过类的实例获取实例方法的列表</li>
<li>依次拼接需要执行js函数的代码</li>
<li>EasyJS对象的加载,执行EasyJS.inject方法</li>
</ul>
</blockquote>
<p>例子:参考Demo调试结果如下</p>
<figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">EasyJS.<span class="keyword">inject</span>(<span class="string">"MyJSTest"</span>, </span><br><span class="line">[</span><br><span class="line"> <span class="string">"test"</span>,</span><br><span class="line"> <span class="string">"testWithParam:"</span>, </span><br><span class="line"> <span class="string">"testWithTwoParam:AndParam2:"</span>, </span><br><span class="line"> <span class="string">"testWithFuncParam:"</span>,</span><br><span class="line"> <span class="string">"testWithFuncParam2:"</span>, </span><br><span class="line"> <span class="string">"testWithRet"</span></span><br><span class="line">]);</span><br></pre></td></tr></table></figure>
<h4 id="4-2-2-2_EasyJS对象">4.2.2.2 EasyJS对象</h4><p>代码片段一:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">inject: <span class="function"><span class="keyword">function</span> (<span class="params">obj, methods</span>)</span>{</span><br><span class="line"> <span class="built_in">window</span>[obj] = {};</span><br><span class="line"> <span class="keyword">var</span> jsObj = <span class="built_in">window</span>[obj];</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, l = methods.length; i < l; i++){</span><br><span class="line"> (<span class="function"><span class="keyword">function</span> (<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> method = methods[i];</span><br><span class="line"> <span class="keyword">var</span> jsMethod = method.replace(<span class="keyword">new</span> <span class="built_in">RegExp</span>(<span class="string">":"</span>, <span class="string">"g"</span>), <span class="string">""</span>);</span><br><span class="line"> jsObj[jsMethod] = <span class="function"><span class="keyword">function</span> (<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">return</span> EasyJS.call(obj, method, <span class="built_in">Array</span>.prototype.slice.call(<span class="built_in">arguments</span>));</span><br><span class="line"> };</span><br><span class="line"> })();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>遍历注入的类的实例方法的列表,通过一个全局的window[obj]的字典维护对应方法的具体实现。下面我们具体看看<code>EasyJS.call</code>方法的实现。</p>
<p>代码片段二:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">call: function (obj, functionName, args){</span><br><span class="line"> var formattedArgs = [];</span><br><span class="line"> <span class="keyword">for</span> (var i = <span class="number">0</span>, l = args<span class="variable">.length</span>; i < l; i++){</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> args[i] == <span class="string">"function"</span>){</span><br><span class="line"> formattedArgs<span class="variable">.push</span>(<span class="string">"f"</span>);</span><br><span class="line"> var cbID = <span class="string">"__cb"</span> + (+new Date);</span><br><span class="line"> EasyJS<span class="variable">.__callbacks</span>[cbID] = args[i];</span><br><span class="line"> formattedArgs<span class="variable">.push</span>(cbID);</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> formattedArgs<span class="variable">.push</span>(<span class="string">"s"</span>);</span><br><span class="line"> formattedArgs<span class="variable">.push</span>(encodeURIComponent(args[i]));</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> var argStr = (formattedArgs<span class="variable">.length</span> > <span class="number">0</span> ? <span class="string">":"</span> + encodeURIComponent(formattedArgs<span class="variable">.join</span>(<span class="string">":"</span>)) : <span class="string">""</span>);</span><br><span class="line"> alert(argStr);</span><br><span class="line"> var iframe = document<span class="variable">.createElement</span>(<span class="string">"IFRAME"</span>);</span><br><span class="line"> iframe<span class="variable">.setAttribute</span>(<span class="string">"src"</span>, <span class="string">"easy-js:"</span> + obj + <span class="string">":"</span> + encodeURIComponent(functionName) + argStr);</span><br><span class="line"> document<span class="variable">.documentElement</span><span class="variable">.appendChild</span>(iframe);</span><br><span class="line"> iframe<span class="variable">.parentNode</span><span class="variable">.removeChild</span>(iframe);</span><br><span class="line"> iframe = null;</span><br><span class="line"> </span><br><span class="line"> var ret = EasyJS<span class="variable">.retValue</span>;</span><br><span class="line"> EasyJS<span class="variable">.retValue</span> = undefined;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (ret){</span><br><span class="line"> <span class="keyword">return</span> decodeURIComponent(ret);</span><br><span class="line"> }</span><br><span class="line">},</span><br></pre></td></tr></table></figure>
<p>这段代码做了三件事:</p>
<blockquote>
<ul>
<li>1.分别针对参数function类型与其他类型区分处理</li>
<li>2.创建一个<code>IFRAME</code>标签元素,设置<code>src</code></li>
<li>3.将新建的<code>IFRAME</code>添加到root元素上</li>
</ul>
</blockquote>
<p>修改<code>IFRAME</code>的<code>src</code>默认会触发webView的回调的执行,因此便有了下面方法<code>shouldStartLoadWithRequest</code>的拦截。</p>
<h4 id="4-2-2-3_shouldStartLoadWithRequest回调实现">4.2.2.3 shouldStartLoadWithRequest回调实现</h4><p>代码片段一:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSArray</span> *components = [requestString componentsSeparatedByString:<span class="string">@":"</span>];</span><br><span class="line"><span class="comment">//NSLog(@"req: %@", requestString);</span></span><br><span class="line"> </span><br><span class="line"><span class="built_in">NSString</span>* obj = (<span class="built_in">NSString</span>*)[components objectAtIndex:<span class="number">1</span>];</span><br><span class="line"><span class="built_in">NSString</span>* method = [(<span class="built_in">NSString</span>*)[components objectAtIndex:<span class="number">2</span>]</span><br><span class="line"> stringByReplacingPercentEscapesUsingEncoding:<span class="built_in">NSUTF8StringEncoding</span>];</span><br><span class="line"> </span><br><span class="line"><span class="built_in">NSObject</span>* interface = [javascriptInterfaces objectForKey:obj];</span><br><span class="line"> </span><br><span class="line"><span class="comment">// execute the interfacing method</span></span><br><span class="line">SEL selector = <span class="built_in">NSSelectorFromString</span>(method);</span><br><span class="line"><span class="built_in">NSMethodSignature</span>* sig = [[interface class] instanceMethodSignatureForSelector:selector];</span><br><span class="line"><span class="built_in">NSInvocation</span>* invoker = [<span class="built_in">NSInvocation</span> invocationWithMethodSignature:sig];</span><br><span class="line">invoker<span class="variable">.selector</span> = selector;</span><br><span class="line">invoker<span class="variable">.target</span> = interface;</span><br><span class="line"> </span><br><span class="line"><span class="built_in">NSMutableArray</span>* args = [[<span class="built_in">NSMutableArray</span> alloc] init];</span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> ([components count] > <span class="number">3</span>){</span><br><span class="line"> <span class="built_in">NSString</span> *argsAsString = [(<span class="built_in">NSString</span>*)[components objectAtIndex:<span class="number">3</span>]</span><br><span class="line"> stringByReplacingPercentEscapesUsingEncoding:<span class="built_in">NSUTF8StringEncoding</span>];</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">NSArray</span>* formattedArgs = [argsAsString componentsSeparatedByString:<span class="string">@":"</span>];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>, j = <span class="number">0</span>, l = [formattedArgs count]; i < l; i+=<span class="number">2</span>, j++){</span><br><span class="line"> <span class="built_in">NSString</span>* type = ((<span class="built_in">NSString</span>*) [formattedArgs objectAtIndex:i]);</span><br><span class="line"> <span class="built_in">NSString</span>* argStr = ((<span class="built_in">NSString</span>*) [formattedArgs objectAtIndex:i + <span class="number">1</span>]);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> ([<span class="string">@"f"</span> isEqualToString:type]){</span><br><span class="line"> EasyJSDataFunction* func = [[EasyJSDataFunction alloc] initWithWebView:(EasyJSWebView *)webView];</span><br><span class="line"> func<span class="variable">.funcID</span> = argStr;</span><br><span class="line"> [args addObject:func];</span><br><span class="line"> [invoker setArgument:&func atIndex:(j + <span class="number">2</span>)];</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span> ([<span class="string">@"s"</span> isEqualToString:type]){</span><br><span class="line"> <span class="built_in">NSString</span>* arg = [argStr stringByReplacingPercentEscapesUsingEncoding:<span class="built_in">NSUTF8StringEncoding</span>];</span><br><span class="line"> [args addObject:arg];</span><br><span class="line"> [invoker setArgument:&arg atIndex:(j + <span class="number">2</span>)];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">[invoker invoke];</span><br></pre></td></tr></table></figure>
<blockquote>
<ul>
<li>1.拆分拦截到的requestString拆分为<code>obj</code>,<code>method</code>,<code>formattedArgs</code>三个部分</li>
<li>2.获取类实例方法的签名,新建一个<code>NSInvocation</code>实例,指定实例与方法</li>
<li>3.<code>invoker</code>设置参数,然后执行invoke,注意参数中function类型的区分,以下5中会分析回调function的处理过程。</li>
</ul>
</blockquote>
<p>代码片段二:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ([sig methodReturnLength] > <span class="number">0</span>){</span><br><span class="line"> <span class="built_in">NSString</span>* retValue;</span><br><span class="line"> [invoker getReturnValue:&retValue];</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (retValue == <span class="literal">NULL</span> || retValue == <span class="literal">nil</span>){</span><br><span class="line"> [webView stringByEvaluatingJavaScriptFromString:<span class="string">@"EasyJS.retValue=null;"</span>];</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> retValue = (<span class="built_in">NSString</span> *)<span class="built_in">CFBridgingRelease</span>(<span class="built_in">CFURLCreateStringByAddingPercentEscapes</span>(<span class="literal">NULL</span>,(<span class="built_in">CFStringRef</span>) retValue, <span class="literal">NULL</span>, (<span class="built_in">CFStringRef</span>)<span class="string">@"!*'();:@&=+$,/?%#[]"</span>, k<span class="built_in">CFStringEncodingUTF8</span>));</span><br><span class="line"> [webView stringByEvaluatingJavaScriptFromString:[<span class="string">@""</span> stringByAppendingFormat:<span class="string">@"EasyJS.retValue=\"%@\";"</span>, retValue]];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>获取invoker执行的结果通过webView执行js代码返回结果值。</p>
<h2 id="5-EasyJSDataFunction_与_invokeCallback">5.EasyJSDataFunction 与 invokeCallback</h2><p>以下主要分析<code>EasyJsWebView</code>是如何处理回调方法参数的。</p>
<p>代码片段一:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> args[i] == <span class="string">"function"</span>){</span><br><span class="line"> formattedArgs.push(<span class="string">"f"</span>);</span><br><span class="line"> <span class="keyword">var</span> cbID = <span class="string">"__cb"</span> + (+<span class="keyword">new</span> <span class="built_in">Date</span>);</span><br><span class="line"> EasyJS.__callbacks[cbID] = args[i];</span><br><span class="line"> formattedArgs.push(cbID);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>js端call方法这样处理function参数,EasyJS对象一个全局的__callbacks字典存储方法实现对象</p>
<p>代码片段二:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> ([<span class="string">@"f"</span> isEqualToString:type]){</span><br><span class="line"> EasyJSDataFunction* func = [[EasyJSDataFunction alloc] initWithWebView:(EasyJSWebView *)webView];</span><br><span class="line"> func<span class="variable">.funcID</span> = argStr;</span><br><span class="line"> [args addObject:func];</span><br><span class="line"> [invoker setArgument:&func atIndex:(j + <span class="number">2</span>)];</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>native端拦截到请求,执行方法</p>
<p>代码片段三:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="built_in">NSString</span>*) executeWithParams: (<span class="built_in">NSArray</span>*) params{</span><br><span class="line"> <span class="built_in">NSMutableString</span>* injection = [[<span class="built_in">NSMutableString</span> alloc] init];</span><br><span class="line"> </span><br><span class="line"> [injection appendFormat:<span class="string">@"EasyJS.invokeCallback(\"%@\", %@"</span>, <span class="keyword">self</span><span class="variable">.funcID</span>, <span class="keyword">self</span><span class="variable">.removeAfterExecute</span> ? <span class="string">@"true"</span> : <span class="string">@"false"</span>];</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (params){</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>, l = params<span class="variable">.count</span>; i < l; i++){</span><br><span class="line"> <span class="built_in">NSString</span>* arg = [params objectAtIndex:i];</span><br><span class="line"> <span class="built_in">NSString</span>* encodedArg = (<span class="built_in">NSString</span>*) <span class="built_in">CFURLCreateStringByAddingPercentEscapes</span>(<span class="literal">NULL</span>, (<span class="built_in">CFStringRef</span>)arg, <span class="literal">NULL</span>, (<span class="built_in">CFStringRef</span>) <span class="string">@"!*'();:@&=+$,/?%#[]"</span>, k<span class="built_in">CFStringEncodingUTF8</span>);</span><br><span class="line"> </span><br><span class="line"> [injection appendFormat:<span class="string">@", \"%@\""</span>, encodedArg];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> [injection appendString:<span class="string">@");"</span>];</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">self</span><span class="variable">.webView</span>){</span><br><span class="line"> <span class="keyword">return</span> [<span class="keyword">self</span><span class="variable">.webView</span> stringByEvaluatingJavaScriptFromString:injection];</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>回调方法执行,将回调方法执行参数解析封装js函数字符串,注意前两个参数第一个表示js函数的唯一ID方便js端找到该函数对象,第二个表示第一次回调完成是否移除该回调执行的函数对象的bool值,然后webView主动执行,这样就完成个整个的回调过程。</p>
<p>例子:Demo回调执行语句调试</p>
<figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EasyJS.invokeCallback(<span class="string">"__cb1462414605044"</span>, true, <span class="string">"blabla<span class="variable">%3A</span><span class="variable">%22bla</span>"</span>);</span><br></pre></td></tr></table></figure>
<h2 id="6-存在问题">6.存在问题</h2><p>见如下代码我们分析实现会发现<code>jsObj</code>全局字典方法区分的key是方法名的拼接,且去处了连接符号<code>:</code>,因此产生疑问这样可能还是会出现同一个key对应不同的方法。</p>
<figure class="highlight nimrod"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">(function (){</span><br><span class="line"> <span class="keyword">var</span> <span class="keyword">method</span> = methods[i];</span><br><span class="line"> <span class="keyword">var</span> jsMethod = <span class="keyword">method</span>.replace(new <span class="type">RegExp</span>(<span class="string">":"</span>, <span class="string">"g"</span>), <span class="string">""</span>);</span><br><span class="line"> jsObj[jsMethod] = function (){</span><br><span class="line"> <span class="keyword">return</span> <span class="type">EasyJS</span>.call(obj, <span class="keyword">method</span>, <span class="type">Array</span>.prototype.slice.call(arguments));</span><br><span class="line"> };</span><br><span class="line">})();</span><br></pre></td></tr></table></figure>
<p>鉴于以上的疑问我改了一下Demo工程,<code>MyJSInterface</code>增加一个实现的接口</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>) testWithTwoParamAndParam2: (<span class="built_in">NSString</span>*) param</span><br><span class="line">{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"testWithTwoParamAndParam2 invoked %@"</span>,param);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这样就会与以下方法冲突</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">- (<span class="keyword">void</span>) testWithTwoParam: (<span class="built_in">NSString</span>*) param AndParam2: (<span class="built_in">NSString</span>*) param2{</span><br><span class="line"> <span class="built_in">NSLog</span>(<span class="string">@"test with param: %@ and param2: %@"</span>, param, param2);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Demo改成如下调用</p>
<figure class="highlight 1c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MyJSTest.testWithTwoParamAndParam2(<span class="string">"haha1"</span>, <span class="string">"haha2"</span>);</span><br></pre></td></tr></table></figure>
<p>抛出异常,原因就是js方法全局字典的key<code>testWithTwoParamAndParam2</code>所对应的方法被下一个方法覆盖。</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">*** WebKit discarded an uncaught exception <span class="keyword">in</span> the <span class="string">webView:</span><span class="string">decidePolicyForNavigationAction:</span><span class="string">request:</span><span class="string">frame:</span><span class="string">decisionListener:</span> <span class="string">delegate:</span> <NSInvalidArgumentException> -[NSInvocation <span class="string">setArgument:</span><span class="string">atIndex:</span>]: index (<span class="number">3</span>) out of bounds [-<span class="number">1</span>, <span class="number">2</span>]</span><br></pre></td></tr></table></figure>
<p>解决:</p>
<blockquote>
<ul>
<li>1.可以尽量避免重名问题</li>
<li>2.也可以替换分隔符号”:”用其他特殊字符替换</li>
</ul>
</blockquote>
<p>本文结,本人还在不断学习积累中,如果对文章有疑问或者错误的描述欢迎提出。<br>或者你有hybrid iOS一块比较好的实现也欢迎分享大家一起学习,谢谢!!!</p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/iOS/">iOS</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-JSPatch源码解析-二" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2016/05/02/JSPatch源码解析-二/" class="article-date">
<time datetime="2016-05-02T14:08:17.000Z" itemprop="datePublished">2016-05-02</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/05/02/JSPatch源码解析-二/">JSPatch源码解析(二)</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>上篇文章简单分析了修复部分的代码实现,本文直接开始由调用过程入手。</p>
<h1 id="1-调用过程">1.调用过程</h1><h2 id="1-1_JPForwardInvocation">1.1 JPForwardInvocation</h2><h3 id="1-1-1_入口">1.1.1 入口</h3><p>还是以上篇文章的<code>handleBtn</code>方法作为例子阐述整个的调用过程。</p>
<p>当点击模拟器的<code>Push JPTableViewController</code>按钮时,<code>handleBtn</code>的方法被调用,由上篇文章4.3.2中以下代码我们已经知道<code>selector</code>的实现实际走消息转发的流程。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">IMP msgForwardIMP = _objc_msgForward;</span><br><span class="line">class_replaceMethod(cls, selector, msgForwardIMP, typeDescription);</span><br></pre></td></tr></table></figure>
<p>同样4.3.2中的以下代码我们知道消息转发的实现已经替换为静态方法<code>JPForwardInvocation</code>的具体实现,因此下面我们具体看看这里的实现。</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (class_getMethodImplementation(cls, <span class="keyword">@selector</span>(forwardInvocation:)) != (IMP)JPForwardInvocation) {</span><br><span class="line"> IMP originalForwardImp = class_replaceMethod(cls, <span class="keyword">@selector</span>(forwardInvocation:), (IMP)JPForwardInvocation, <span class="string">"v@:@"</span>);</span><br><span class="line"> class_addMethod(cls, <span class="keyword">@selector</span>(ORIGforwardInvocation:), originalForwardImp, <span class="string">"v@:@"</span>);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<h3 id="1-1-2_实现">1.1.2 实现</h3><p>代码片段一:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">id</span> slf = assignSlf;</span><br><span class="line"><span class="built_in">NSMethodSignature</span> *methodSignature = [invocation methodSignature];</span><br><span class="line"><span class="built_in">NSInteger</span> numberOfArguments = [methodSignature numberOfArguments];</span><br><span class="line"> </span><br><span class="line"><span class="built_in">NSString</span> *selectorName = <span class="built_in">NSStringFromSelector</span>(invocation<span class="variable">.selector</span>);</span><br><span class="line"><span class="built_in">NSString</span> *JPSelectorName = [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"_JP%@"</span>, selectorName];</span><br><span class="line">SEL JPSelector = <span class="built_in">NSSelectorFromString</span>(JPSelectorName);</span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> (!class_respondsToSelector(object_getClass(slf), JPSelector)) {</span><br><span class="line"> JPExcuteORIGForwardInvocation(slf, selector, invocation);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>判断新的selector是否在该类中已经实现,否则就走原始方法的消息转发的流程。<br><br></p>
<p>代码片段二:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSMutableArray</span> *argList = [[<span class="built_in">NSMutableArray</span> alloc] init];</span><br><span class="line"><span class="keyword">if</span> ([slf class] == slf) {</span><br><span class="line"> [argList addObject:[JSValue valueWithObject:@{<span class="string">@"__clsName"</span>: <span class="built_in">NSStringFromClass</span>([slf class])} inContext:_context]];</span><br><span class="line">} <span class="keyword">else</span> <span class="keyword">if</span> ([selectorName isEqualToString:<span class="string">@"dealloc"</span>]) {</span><br><span class="line"> [argList addObject:[JPBoxing boxAssignObj:slf]];</span><br><span class="line"> deallocFlag = <span class="literal">YES</span>;</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> [argList addObject:[JPBoxing boxWeakObj:slf]];</span><br><span class="line">}</span><br><span class="line">...</span><br><span class="line"><span class="keyword">if</span> (_currInvokeSuperClsName) {</span><br><span class="line"> Class cls = <span class="built_in">NSClassFromString</span>(_currInvokeSuperClsName);</span><br><span class="line"> <span class="built_in">NSString</span> *tmpSelectorName = [[selectorName stringByReplacingOccurrencesOfString:<span class="string">@"_JPSUPER_"</span> withString:<span class="string">@"_JP"</span>] stringByReplacingOccurrencesOfString:<span class="string">@"SUPER_"</span> withString:<span class="string">@"_JP"</span>];</span><br><span class="line"> <span class="keyword">if</span> (!_JSOverideMethods[cls][tmpSelectorName]) {</span><br><span class="line"> <span class="built_in">NSString</span> *ORIGSelectorName = [selectorName stringByReplacingOccurrencesOfString:<span class="string">@"SUPER_"</span> withString:<span class="string">@"ORIG"</span>];</span><br><span class="line"> [argList removeObjectAtIndex:<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">id</span> retObj = callSelector(_currInvokeSuperClsName, ORIGSelectorName, [JSValue valueWithObject:argList inContext:_context], [JSValue valueWithObject:@{<span class="string">@"__obj"</span>: slf, <span class="string">@"__realClsName"</span>: <span class="string">@""</span>} inContext:_context], <span class="literal">NO</span>);</span><br><span class="line"> <span class="keyword">id</span> __autoreleasing ret = formatJSToOC([JSValue valueWithObject:retObj inContext:_context]);</span><br><span class="line"> [invocation setReturnValue:&ret];</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>把self与相应的参数都添加到一个集合中。<br><br></p>
<p>代码片段三:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSArray</span> *params = _formatOCToJSList(argList);</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">char</span> *returnType = [methodSignature methodReturnType];</span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="preprocessor">#define JP_FWD_RET_CALL_JS \</span></span><br><span class="line">JSValue *fun = getJSFunctionInObjectHierachy(slf, JPSelectorName); \</span><br><span class="line">JSValue *jsval; \</span><br><span class="line">[_JSMethodForwardCallLock lock]; \</span><br><span class="line">jsval = [fun callWithArguments:params]; \</span><br><span class="line">[_JSMethodForwardCallLock unlock]; \</span><br><span class="line"><span class="keyword">while</span> (![jsval isNull] && ![jsval isUndefined] && [jsval hasProperty:<span class="string">@"__isPerformInOC"</span>]) { \</span><br><span class="line"> <span class="built_in">NSArray</span> *args = <span class="literal">nil</span>; \</span><br><span class="line"> JSValue *cb = jsval[<span class="string">@"cb"</span>]; \</span><br><span class="line"> <span class="keyword">if</span> ([jsval hasProperty:<span class="string">@"sel"</span>]) { \</span><br><span class="line"> <span class="keyword">id</span> callRet = callSelector(![jsval[<span class="string">@"clsName"</span>] isUndefined] ? [jsval[<span class="string">@"clsName"</span>] toString] : <span class="literal">nil</span>, [jsval[<span class="string">@"sel"</span>] toString], jsval[<span class="string">@"args"</span>], ![jsval[<span class="string">@"obj"</span>] isUndefined] ? jsval[<span class="string">@"obj"</span>] : <span class="literal">nil</span>, <span class="literal">NO</span>); \</span><br><span class="line"> args = @[[_context[<span class="string">@"_formatOCToJS"</span>] callWithArguments:callRet ? @[callRet] : _formatOCToJSList(@[_nilObj])]]; \</span><br><span class="line"> } \</span><br><span class="line"> [_JSMethodForwardCallLock lock]; \</span><br><span class="line"> jsval = [cb callWithArguments:args]; \</span><br><span class="line"> [_JSMethodForwardCallLock unlock]; \</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>把包含self与调用的参数转换为js对象,<code>getJSFunctionInObjectHierachy</code>获取对应的js重写的函数,直接调用<code>callWithArgument</code>方法,执行函数。<br><br></p>
<p>以上部分我们发现<code>handleBtn</code>的实现部分实际上是<code>_JPhandleBtn</code>对应的方法的实现,调用的流程基本了解,而此时我们有疑问,Demo中<code>handleBtn</code>具体的替换实现(见代码)是如何执行的呢?</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">var</span> tableViewCtrl = JPTableViewController.<span class="function"><span class="title">alloc</span><span class="params">()</span></span>.<span class="function"><span class="title">init</span><span class="params">()</span></span></span><br><span class="line">self.<span class="function"><span class="title">navigationController</span><span class="params">()</span></span>.<span class="function"><span class="title">pushViewController_animated</span><span class="params">(tableViewCtrl, YES)</span></span></span><br></pre></td></tr></table></figure>
<h2 id="1-2_callSelector">1.2 callSelector</h2><h3 id="1-2-1_入口">1.2.1 入口</h3><p>代码片段一:分析JSPatch.js的代码部分时我们发现会有如下一段代码,给js对象基类 Object 的 prototype 加上 <strong>c 成员,这样所有对象都可以调用到 </strong>c,为什么这么做可以查看原作者<br><a href="https://github.com/bang590/JSPatch/wiki/JSPatch-%E5%AE%9E%E7%8E" target="_blank" rel="external">wiki详解</a></p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">Object</span><span class="class">.defineProperty</span>(<span class="tag">Object</span><span class="class">.prototype</span>, "__<span class="tag">c</span>", <span class="rules">{<span class="rule"><span class="attribute">value</span>:<span class="value"> <span class="function">function</span>(methodName) </span><br><span class="line">{</span><br><span class="line"> ...</span><br><span class="line"></span></span></span>}, <span class="tag">configurable</span><span class="pseudo">:false</span>, <span class="tag">enumerable</span>: <span class="tag">false</span>});</span><br></pre></td></tr></table></figure>
<p>因此我们只需要关注<code>__c</code>方法的具体实现,分析发现它的核心实现是</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">function</span><span class="params">()</span></span>{</span><br><span class="line"><span class="keyword">var</span> args = <span class="keyword">Array</span>.prototype.slice.call(arguments)</span><br><span class="line"><span class="keyword">return</span> _methodFunc(<span class="keyword">self</span>.__obj, <span class="keyword">self</span>.__clsName, methodName, args, <span class="keyword">self</span>.__isSuper)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>查看<code>_methodFunc</code>的代码,最终定位<code>_OC_callI</code>,<code>_OC_callC</code>两个方法</p>
<figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> _methodFunc = function(instance, clsName, methodName, <span class="keyword">args</span>, isSuper, isPerformSelector) {</span><br><span class="line"><span class="keyword">var</span> selectorName = methodName</span><br><span class="line"><span class="keyword">if</span> (!isPerformSelector) {</span><br><span class="line"> methodName = methodName.<span class="keyword">replace</span>(/__/<span class="keyword">g</span>, <span class="string">"-"</span>)</span><br><span class="line"> selectorName = methodName.<span class="keyword">replace</span>(/_/<span class="keyword">g</span>, <span class="string">":"</span>).<span class="keyword">replace</span>(/-/<span class="keyword">g</span>, <span class="string">"_"</span>)</span><br><span class="line"> <span class="keyword">var</span> marchArr = selectorName.<span class="literal">match</span>(/:/<span class="keyword">g</span>)</span><br><span class="line"> <span class="keyword">var</span> numOfArgs = marchArr ? marchArr.length : 0</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">args</span>.length > numOfArgs) {</span><br><span class="line"> selectorName += <span class="string">":"</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> <span class="keyword">ret</span> = instance ? _OC_callI(instance, selectorName, <span class="keyword">args</span>, isSuper):</span><br><span class="line"> _OC_callC(clsName, selectorName, <span class="keyword">args</span>)</span><br><span class="line"><span class="keyword">return</span> _formatOCToJS(<span class="keyword">ret</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>由startEngine可知,<code>_OC_callI</code>,<code>_OC_callC</code>两个方法为注入到context的全局的方法,因此就定位到<code>callSelector</code>。以上分析了<code>callSelector</code>的入口,下面主要分析它的具体实现。</p>
<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">context[<span class="string">@"_OC_callI"</span>] = ^<span class="keyword">id</span>(JSValue *obj, <span class="built_in">NSString</span> *selectorName, JSValue *arguments, <span class="built_in">BOOL</span> isSuper) {</span><br><span class="line"> <span class="keyword">return</span> callSelector(<span class="literal">nil</span>, selectorName, arguments, obj, isSuper);</span><br><span class="line">};</span><br><span class="line">context[<span class="string">@"_OC_callC"</span>] = ^<span class="keyword">id</span>(<span class="built_in">NSString</span> *className, <span class="built_in">NSString</span> *selectorName, JSValue *arguments) {</span><br><span class="line"> <span class="keyword">return</span> callSelector(className, selectorName, arguments, <span class="literal">nil</span>, <span class="literal">NO</span>);</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<h3 id="1-2-2_实现">1.2.2 实现</h3><p>代码片段一:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (instance) {</span><br><span class="line"> instance = formatJSToOC(instance);</span><br><span class="line"> <span class="keyword">if</span> (!instance || instance == _nilObj) <span class="keyword">return</span> @{<span class="string">@"__isNil"</span>: @(<span class="literal">YES</span>)};</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">id</span> argumentsObj = formatJSToOC(arguments);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (instance && [selectorName isEqualToString:<span class="string">@"toJS"</span>]) {</span><br><span class="line"> <span class="keyword">if</span> ([instance isKindOfClass:[<span class="built_in">NSString</span> class]] || [instance isKindOfClass:[<span class="built_in">NSDictionary</span> class]] || [instance isKindOfClass:[<span class="built_in">NSArray</span> class]] || [instance isKindOfClass:[<span class="built_in">NSDate</span> class]]) {</span><br><span class="line"> <span class="keyword">return</span> _unboxOCObjectToJS(instance);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>把js对象与参数转换为OC对象<br><br></p>
<p>代码片段二:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (isSuper) {</span><br><span class="line"><span class="built_in">NSString</span> *superSelectorName = [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"SUPER_%@"</span>, selectorName];</span><br><span class="line">SEL superSelector = <span class="built_in">NSSelectorFromString</span>(superSelectorName);</span><br><span class="line"> </span><br><span class="line">Class superCls;</span><br><span class="line"><span class="keyword">if</span> (clsDeclaration<span class="variable">.length</span>) {</span><br><span class="line"> <span class="built_in">NSDictionary</span> *declarationDict = convertJPDeclarationString(clsDeclaration);</span><br><span class="line"> <span class="built_in">NSString</span> *defineClsName = declarationDict[<span class="string">@"className"</span>];</span><br><span class="line"></span><br><span class="line"> Class defineClass = <span class="built_in">NSClassFromString</span>(defineClsName);</span><br><span class="line"> superCls = defineClass ? [defineClass superclass] : [cls superclass];</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> superCls = [cls superclass];</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line">Method superMethod = class_getInstanceMethod(superCls, selector);</span><br><span class="line">IMP superIMP = method_getImplementation(superMethod);</span><br><span class="line"> </span><br><span class="line">class_addMethod(cls, superSelector, superIMP, method_getTypeEncoding(superMethod));</span><br><span class="line"> </span><br><span class="line"><span class="built_in">NSString</span> *JPSelectorName = [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"_JP%@"</span>, selectorName];</span><br><span class="line">JSValue *overideFunction = _JSOverideMethods[superCls][JPSelectorName];</span><br><span class="line"><span class="keyword">if</span> (overideFunction) {</span><br><span class="line"> overrideMethod(cls, superSelectorName, overideFunction, <span class="literal">NO</span>, <span class="literal">NULL</span>);</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line">selector = superSelector;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p><br><br>判断是否是父类的方法,走父类的方法的实的实现</p>
<p>代码片段三:</p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSInvocation</span> *invocation;</span><br><span class="line"><span class="built_in">NSMethodSignature</span> *methodSignature;</span><br><span class="line"><span class="keyword">if</span> (!_JSMethodSignatureCache) {</span><br><span class="line">_JSMethodSignatureCache = [[<span class="built_in">NSMutableDictionary</span> alloc]init];</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (instance) {</span><br><span class="line"> ...</span><br><span class="line"> invocation= [<span class="built_in">NSInvocation</span> invocationWithMethodSignature:methodSignature];</span><br><span class="line"> [invocation setTarget:cls];</span><br><span class="line">}</span><br><span class="line">[invocation setSelector:selector];</span><br><span class="line">...</span><br><span class="line">[invocation invoke]; </span><br><span class="line">...</span><br><span class="line"><span class="keyword">return</span> returnValue;</span><br></pre></td></tr></table></figure>
<p>封装<code>NSInvocation</code>并执行,返回处理的结果</p>
<h2 id="补充说明">补充说明</h2><p>上一篇博文中预留一个问题:4.3.1 中为什么需要加入参数个数的说明呢?<br>如下代码:</p>
<figure class="highlight"><figcaption><span>*typeDescStr = [@"@@:" mutableCopy];</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">for (int i = 0; i < numberOfArg; i ++) { [typeDescStr appendString:@"@"]; } overrideMethod(currCls, selectorName, jsMethod, !isInstance, [typeDescStr cStringUsingEncoding:NSUTF8StringEncoding]);**</span><br></pre></td></tr></table></figure>
<p>需要根据传递过来的参数的个数声称方法的签名。</p>
<p><strong>JSPatch核心的代码分析的部分已经完成,可以参考我的两篇博文,<br><a href="http://www.jianshu.com/p/6cc18cfc9354" target="_blank" rel="external">JSPatch源码学习(一)</a><br><a href="http://www.jianshu.com/p/3d9a1c53016a" target="_blank" rel="external">JSPatch源码学习(二)</a>部分细节问题未作具体的分析,例如内存,<code>JPBoxing</code>,<code>JPExtension</code>等,有兴趣可以关注我后期的该主题的博文。本人还在不断的学习积累中,有问题欢迎及时指出,谢谢!</strong></p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/JSpatch/">JSpatch</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/iOS/">iOS</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-JSPatch-源码解析-一" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2016/04/10/JSPatch-源码解析-一/" class="article-date">
<time datetime="2016-04-10T14:27:38.000Z" itemprop="datePublished">2016-04-10</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/04/10/JSPatch-源码解析-一/">JSPatch 源码解析(一)</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h1 id="1-前言">1.前言</h1><p>前一段时间在公司做了一个iOS热补丁的模块,就用到了<a href="https://github.com/bang590/JSPatch" target="_blank" rel="external">JSPatch框架</a>,期间有了解过一些关于框架的源码分析的博客:<br><br><a href="http://albert43.net/2015/06/19/JSPatch%E5%AD%A6%E4%B9%A0-%E6%A0%B8%E5%BF%83%E5%92%8C%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/" target="_blank" rel="external">1.JSPatch学习:JSPatch核心和实现原理分析</a></p>
<p><a href="http://www.jianshu.com/p/564bdb35f3f3" target="_blank" rel="external">2.JSPatch defineProtocol部分实现详解</a><br><br>思考再三还是决定自己调试了解一下整体的实现机制。</p>
<h1 id="2-准备工具">2.准备工具</h1><ul>
<li><a href="https://github.com/bang590/JSPatch" target="_blank" rel="external">DemoCode</a> <br></li>
<li><a href="https://github.com/bang590/JSPatch/wiki/JS-%E6%96%AD%E7%82%B9%E8%B0%83%E8%AF%95" target="_blank" rel="external">js调试</a></li>
</ul>
<h1 id="3-项目文件">3.项目文件</h1><h2 id="3-1项目文件图">3.1项目文件图</h2><p><img src="https://dn-arnold.qbox.me/jspatch_1.png" alt=""></p>
<h2 id="3-2具体文件分析">3.2具体文件分析</h2><ul>
<li>JPEngine.m 核心的Native端实现。</li>
<li>JSpatch.js 核心的js端实现。</li>
<li>Extensions 扩展的方法提供给js端调用,内部是OC实现。</li>
<li>Loader 一套热补丁动态更新补丁脚本的客户端实现,需要配合服务端才能实现整个更新框架。(本文不对此作多赘述)</li>
</ul>
<h1 id="4-调试分析">4.调试分析</h1><h2 id="4-1调用代码">4.1调用代码</h2><figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[JPEngine startEngine];</span><br><span class="line"><span class="built_in">NSString</span> *sourcePath = [[<span class="built_in">NSBundle</span> mainBundle] pathForResource:<span class="string">@"demo"</span> ofType:<span class="string">@"js"</span>];</span><br><span class="line"><span class="built_in">NSString</span> *script = [<span class="built_in">NSString</span> stringWithContentsOfFile:sourcePath encoding:<span class="built_in">NSUTF8StringEncoding</span> error:<span class="literal">nil</span>];</span><br><span class="line">[JPEngine evaluateScript:script];</span><br></pre></td></tr></table></figure>
<h2 id="4-2_JPEngine初始化">4.2 JPEngine初始化</h2><figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[JPEngine startEngine];</span><br></pre></td></tr></table></figure>
<p>我们来看看具体的代码实现,</p>
<ul>
<li>代码片段一:</li>
</ul>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">+ (<span class="keyword">void</span>)startEngine</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">if</span> (![JSContext class] || _context) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> JSContext *context = [[JSContext alloc] init];</span><br><span class="line"> </span><br><span class="line"> context[<span class="string">@"_OC_defineClass"</span>] = ^(<span class="built_in">NSString</span> *classDeclaration, JSValue *instanceMethods, JSValue *classMethods) {</span><br><span class="line"> <span class="keyword">return</span> defineClass(classDeclaration, instanceMethods, classMethods);</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> context[<span class="string">@"_OC_defineProtocol"</span>] = ^(<span class="built_in">NSString</span> *protocolDeclaration, JSValue *instProtocol, JSValue *clsProtocol) {</span><br><span class="line"> <span class="keyword">return</span> defineProtocol(protocolDeclaration, instProtocol,clsProtocol);</span><br><span class="line"> };</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/*.... 分块分析,暂时省略以下代码*/</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>_content 为JSContect的实例,根据苹果官方文档 :</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1439607-b5a418ea54fb49c2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<p>我们知道JSContext为js的执行环境。因此例如:<br><code>context[@"_OC_defineProtocol"]=block实现</code>这样的调用就很好的理解为为js的上下文注入了全局的<code>_OC_defineProtocol</code>方法,而具体的实现对应着Native端的block的实现。</p>
<ul>
<li>代码片段二:</li>
</ul>
<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSString</span> *path = [[<span class="built_in">NSBundle</span> bundleForClass:[<span class="keyword">self</span> class]] pathForResource:<span class="string">@"JSPatch"</span> ofType:<span class="string">@"js"</span>];</span><br><span class="line"><span class="built_in">NSAssert</span>(path, <span class="string">@"can't find JSPatch.js"</span>);</span><br><span class="line"><span class="built_in">NSString</span> *jsCore = [[<span class="built_in">NSString</span> alloc] initWithData:[[<span class="built_in">NSFileManager</span> defaultManager] contentsAtPath:path] encoding:<span class="built_in">NSUTF8StringEncoding</span>];</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> ([_context respondsToSelector:<span class="keyword">@selector</span>(evaluateScript:withSourceURL:)]) {</span><br><span class="line"> [_context evaluateScript:jsCore withSourceURL:[<span class="built_in">NSURL</span> URLWithString:<span class="string">@"JSPatch.js"</span>]];</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> [_context evaluateScript:jsCore];</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>加载核心的JSPatch.js代码完成初始化,具体js代码后续具体调用再作分析。</p>
<h2 id="4-3具体修复代码">4.3具体修复代码</h2><p>我们来看看Demo的修复代码:</p>
<figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">defineClass<span class="list">(<span class="quoted">'JPViewController</span>', {</span><br><span class="line"> handleBtn: function<span class="list">(<span class="keyword">sender</span>)</span> {</span><br><span class="line"> var tableViewCtrl = JPTableViewController.alloc<span class="list">()</span>.init<span class="list">()</span></span><br><span class="line"> self.navigationController<span class="list">()</span>.pushViewController_animated<span class="list">(<span class="keyword">tableViewCtrl</span>, YES)</span></span><br><span class="line"> }</span><br><span class="line">})</span></span><br></pre></td></tr></table></figure>
<p>调试看看执行步骤:</p>
<h3 id="4-3-1_首先执行global-defineClass_(js端)">4.3.1 首先执行global.defineClass (js端)</h3><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">global<span class="class">.defineClass</span> = <span class="function"><span class="title">function</span><span class="params">(declaration, properties, instMethods, clsMethods)</span></span></span><br></pre></td></tr></table></figure>
<p>我们断点到<code>var ret = _OC_defineClass(declaration, newInstMethods, newClsMethods)</code>处,查看局部变量表<br><img src="http://upload-images.jianshu.io/upload_images/1439607-caa22246c1bb69aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="">会发现<code>newInstMethods newClsMethods</code>均被赋值,且其中每个实例或类方法的js对象被修改添加参数的个数的说明,只是好奇为什么需要加入参数个数的说明呢???</p>
<h3 id="4-3-2_执行_OC_defineClass_(js端_->_Native端)">4.3.2 执行<code>_OC_defineClass</code> (js端 -> Native端)</h3><figure class="highlight aspectj"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">context[@<span class="string">"_OC_defineClass"</span>] = ^(NSString *classDeclaration, JSValue *instanceMethods, JSValue *classMethods) {</span><br><span class="line"> <span class="function"><span class="keyword">return</span> <span class="title">defineClass</span><span class="params">(classDeclaration, instanceMethods, classMethods)</span></span>;</span><br><span class="line"> };</span><br></pre></td></tr></table></figure>
<p>实际执行的是Native的 <code>static NSDictionary *defineClass(NSString *classDeclaration, JSValue *instanceMethods, JSValue *classMethods)</code> 方法,以下分代码片段解析。</p>
<ul>
<li>代码片段一:</li>
</ul>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">NSScanner</span> *scanner = [<span class="built_in">NSScanner</span> scannerWithString:classDeclaration];</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">NSString</span> *className;</span><br><span class="line"> <span class="built_in">NSString</span> *superClassName;</span><br><span class="line"> <span class="built_in">NSString</span> *protocolNames;</span><br><span class="line"> [scanner scanUpToString:<span class="string">@":"</span> intoString:&className];</span><br><span class="line"> <span class="keyword">if</span> (!scanner<span class="variable">.isAtEnd</span>) {</span><br><span class="line"> scanner<span class="variable">.scanLocation</span> = scanner<span class="variable">.scanLocation</span> + <span class="number">1</span>;</span><br><span class="line"> [scanner scanUpToString:<span class="string">@"<"</span> intoString:&superClassName];</span><br><span class="line"> <span class="keyword">if</span> (!scanner<span class="variable">.isAtEnd</span>) {</span><br><span class="line"> scanner<span class="variable">.scanLocation</span> = scanner<span class="variable">.scanLocation</span> + <span class="number">1</span>;</span><br><span class="line"> [scanner scanUpToString:<span class="string">@">"</span> intoString:&protocolNames];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (!superClassName) superClassName = <span class="string">@"NSObject"</span>;</span><br><span class="line"> className = trim(className);</span><br><span class="line"> superClassName = trim(superClassName);</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">NSArray</span> *protocols = [protocolNames length] ? [protocolNames componentsSeparatedByString:<span class="string">@","</span>] : <span class="literal">nil</span>;</span><br><span class="line"> </span><br><span class="line"> Class cls = <span class="built_in">NSClassFromString</span>(className);</span><br><span class="line"> <span class="keyword">if</span> (!cls) {</span><br><span class="line"> Class superCls = <span class="built_in">NSClassFromString</span>(superClassName);</span><br><span class="line"> <span class="keyword">if</span> (!superCls) {</span><br><span class="line"> <span class="built_in">NSCAssert</span>(<span class="literal">NO</span>, <span class="string">@"can't find the super class %@"</span>, superClassName);</span><br><span class="line"> <span class="keyword">return</span> @{<span class="string">@"cls"</span>: className};</span><br><span class="line"> }</span><br><span class="line"> cls = objc_allocateClassPair(superCls, className<span class="variable">.UTF8String</span>, <span class="number">0</span>);</span><br><span class="line"> objc_registerClassPair(cls);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (protocols<span class="variable">.count</span> > <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="built_in">NSString</span>* protocolName <span class="keyword">in</span> protocols) {</span><br><span class="line"> Protocol *protocol = objc_getProtocol([trim(protocolName) cStringUsingEncoding:<span class="built_in">NSUTF8StringEncoding</span>]);</span><br><span class="line"> class_addProtocol (cls, protocol);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>通过传递的classDeclaration解析相应的className,superClassName,protocols,</p>
<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//1.</span></span><br><span class="line">cls = objc_allocateClassPair(superCls, className<span class="variable">.UTF8String</span>, <span class="number">0</span>);</span><br><span class="line">objc_registerClassPair(cls);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//2.</span></span><br><span class="line"> <span class="keyword">if</span> (protocols<span class="variable">.count</span> > <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="built_in">NSString</span>* protocolName <span class="keyword">in</span> protocols) {</span><br><span class="line"> Protocol *protocol = objc_getProtocol([trim(protocolName) cStringUsingEncoding:<span class="built_in">NSUTF8StringEncoding</span>]);</span><br><span class="line"> class_addProtocol (cls, protocol);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>1.运行期间创建一个新类,并完成注册. 2.遍历协议名,依此初始化并完成对类的协议的添加。</p>
<ul>
<li>代码片段二:</li>
</ul>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">2</span>; i ++) {</span><br><span class="line"> <span class="built_in">BOOL</span> isInstance = i == <span class="number">0</span>;</span><br><span class="line"> JSValue *jsMethods = isInstance ? instanceMethods: classMethods;</span><br><span class="line"> </span><br><span class="line"> Class currCls = isInstance ? cls: objc_getMetaClass(className<span class="variable">.UTF8String</span>);</span><br><span class="line"> <span class="built_in">NSDictionary</span> *methodDict = [jsMethods toDictionary];</span><br><span class="line"> <span class="keyword">for</span> (<span class="built_in">NSString</span> *jsMethodName <span class="keyword">in</span> methodDict<span class="variable">.allKeys</span>) {</span><br><span class="line"> JSValue *jsMethodArr = [jsMethods valueForProperty:jsMethodName];</span><br><span class="line"> <span class="keyword">int</span> numberOfArg = [jsMethodArr[<span class="number">0</span>] toInt32];</span><br><span class="line"> <span class="built_in">NSString</span> *selectorName = convertJPSelectorString(jsMethodName);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> ([selectorName componentsSeparatedByString:<span class="string">@":"</span>]<span class="variable">.count</span> - <span class="number">1</span> < numberOfArg) {</span><br><span class="line"> selectorName = [selectorName stringByAppendingString:<span class="string">@":"</span>];</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> JSValue *jsMethod = jsMethodArr[<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">if</span> (class_respondsToSelector(currCls, <span class="built_in">NSSelectorFromString</span>(selectorName))) {</span><br><span class="line"> overrideMethod(currCls, selectorName, jsMethod, !isInstance, <span class="literal">NULL</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">BOOL</span> overrided = <span class="literal">NO</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="built_in">NSString</span> *protocolName <span class="keyword">in</span> protocols) {</span><br><span class="line"> <span class="keyword">char</span> *types = methodTypesInProtocol(protocolName, selectorName, isInstance, <span class="literal">YES</span>);</span><br><span class="line"> <span class="keyword">if</span> (!types) types = methodTypesInProtocol(protocolName, selectorName, isInstance, <span class="literal">NO</span>);</span><br><span class="line"> <span class="keyword">if</span> (types) {</span><br><span class="line"> overrideMethod(currCls, selectorName, jsMethod, !isInstance, types);</span><br><span class="line"> free(types);</span><br><span class="line"> overrided = <span class="literal">YES</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (!overrided) {</span><br><span class="line"> <span class="keyword">if</span> (![[jsMethodName substringToIndex:<span class="number">1</span>] isEqualToString:<span class="string">@"_"</span>]) {</span><br><span class="line"> <span class="built_in">NSMutableString</span> *typeDescStr = [<span class="string">@"@@:"</span> mutableCopy];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < numberOfArg; i ++) {</span><br><span class="line"> [typeDescStr appendString:<span class="string">@"@"</span>];</span><br><span class="line"> }</span><br><span class="line"> overrideMethod(currCls, selectorName, jsMethod, !isInstance, [typeDescStr cStringUsingEncoding:<span class="built_in">NSUTF8StringEncoding</span>]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<ul>
<li>遍历传递过过来的实例方法,类方法的js实例,然后依次遍历方法字典,完成方法名js命名到native命名的转换。</li>
<li>通过转换后的方法名,用<code>class_respondsToSelector</code>判断是否该类的方法列表中是否已经存在该方法的实现,存在即调用<code>overrideMethod(currCls, selectorName, jsMethod, !isInstance, NULL);</code>覆盖方法实现。</li>
<li><p>如果类的方法列表中不存在该方法的实现,则通过实现的协议的列表查找,依次判断方法是否在协议的实现方法中,如果以上都不是说明是新添加的方法.</p>
</li>
<li><p>代码片段三:</p>
</li>
</ul>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="preprocessor">#pragma clang diagnostic push</span></span><br><span class="line"><span class="preprocessor">#pragma clang diagnostic ignored <span class="title">"-Wundeclared-selector"</span></span></span><br><span class="line"> class_addMethod(cls, <span class="keyword">@selector</span>(getProp:), (IMP)getPropIMP, <span class="string">"@@:@"</span>);</span><br><span class="line"> class_addMethod(cls, <span class="keyword">@selector</span>(setProp:forKey:), (IMP)setPropIMP, <span class="string">"v@:@@"</span>);</span><br><span class="line"><span class="preprocessor">#pragma clang diagnostic pop</span></span><br></pre></td></tr></table></figure>
<p>添加类的<code>getProp:``setProp:forKey:</code>的方法及实现。</p>
<ul>
<li>代码片段四:</li>
</ul>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> overrideMethod(Class cls, <span class="built_in">NSString</span> *selectorName, JSValue *function, <span class="built_in">BOOL</span> isClassMethod, <span class="keyword">const</span> <span class="keyword">char</span> *typeDescription)</span><br><span class="line">{</span><br><span class="line"> SEL selector = <span class="built_in">NSSelectorFromString</span>(selectorName);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (!typeDescription) {</span><br><span class="line"> Method method = class_getInstanceMethod(cls, selector);</span><br><span class="line"> typeDescription = (<span class="keyword">char</span> *)method_getTypeEncoding(method);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> IMP originalImp = class_respondsToSelector(cls, selector) ? class_getMethodImplementation(cls, selector) : <span class="literal">NULL</span>;</span><br><span class="line"> </span><br><span class="line"> IMP msgForwardIMP = _objc_msgForward;</span><br><span class="line"> <span class="preprocessor">#if !defined(__arm64__)</span></span><br><span class="line"> <span class="keyword">if</span> (typeDescription[<span class="number">0</span>] == <span class="string">'{'</span>) {</span><br><span class="line"> <span class="comment">//In some cases that returns struct, we should use the '_stret' API:</span></span><br><span class="line"> <span class="comment">//http://sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html</span></span><br><span class="line"> <span class="comment">//NSMethodSignature knows the detail but has no API to return, we can only get the info from debugDescription.</span></span><br><span class="line"> <span class="built_in">NSMethodSignature</span> *methodSignature = [<span class="built_in">NSMethodSignature</span> signatureWithObjCTypes:typeDescription];</span><br><span class="line"> <span class="keyword">if</span> ([methodSignature<span class="variable">.debugDescription</span> rangeOfString:<span class="string">@"is special struct return? YES"</span>]<span class="variable">.location</span> != <span class="built_in">NSNotFound</span>) {</span><br><span class="line"> msgForwardIMP = (IMP)_objc_msgForward_stret;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="preprocessor">#endif</span></span><br><span class="line"></span><br><span class="line"> class_replaceMethod(cls, selector, msgForwardIMP, typeDescription);</span><br><span class="line"></span><br><span class="line"><span class="preprocessor">#pragma clang diagnostic push</span></span><br><span class="line"><span class="preprocessor">#pragma clang diagnostic ignored <span class="title">"-Wundeclared-selector"</span></span></span><br><span class="line"> <span class="keyword">if</span> (class_getMethodImplementation(cls, <span class="keyword">@selector</span>(forwardInvocation:)) != (IMP)JPForwardInvocation) {</span><br><span class="line"> IMP originalForwardImp = class_replaceMethod(cls, <span class="keyword">@selector</span>(forwardInvocation:), (IMP)JPForwardInvocation, <span class="string">"v@:@"</span>);</span><br><span class="line"> class_addMethod(cls, <span class="keyword">@selector</span>(ORIGforwardInvocation:), originalForwardImp, <span class="string">"v@:@"</span>);</span><br><span class="line"> }</span><br><span class="line"><span class="preprocessor">#pragma clang diagnostic pop</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (class_respondsToSelector(cls, selector)) {</span><br><span class="line"> <span class="built_in">NSString</span> *originalSelectorName = [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"ORIG%@"</span>, selectorName];</span><br><span class="line"> SEL originalSelector = <span class="built_in">NSSelectorFromString</span>(originalSelectorName);</span><br><span class="line"> <span class="keyword">if</span>(!class_respondsToSelector(cls, originalSelector)) {</span><br><span class="line"> class_addMethod(cls, originalSelector, originalImp, typeDescription);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="built_in">NSString</span> *JPSelectorName = [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"_JP%@"</span>, selectorName];</span><br><span class="line"> SEL JPSelector = <span class="built_in">NSSelectorFromString</span>(JPSelectorName);</span><br><span class="line"></span><br><span class="line"> _initJPOverideMethods(cls);</span><br><span class="line"> _JSOverideMethods[cls][JPSelectorName] = function;</span><br><span class="line"> </span><br><span class="line"> class_addMethod(cls, JPSelector, msgForwardIMP, typeDescription);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>核心的替换添加方法实现的方法:</p>
<ul>
<li>把原始的方法的实现替换为<code>_objc_msgForward</code>,即该方法的调用会走消息转发的路径.</li>
<li>类的<code>forwardInvocation:</code>方法的实现被替换为<code>JPForwardInvocation</code>的实现</li>
<li>添加的方法<code>ORIGforwardInvocation</code>指向原始的实现IMP.</li>
<li>添加的方法<code>ORIG+selector</code>指向原始的实现的IMP.</li>
<li>添加<code>_JP+selector</code>指向新的函数的实现.<br><br></li>
</ul>
<hr>
<p><strong>上述代码只是讲解了替换添加方法的实现,而新的实现方法是一个个js的对象,如何关联到Native端的调用?后续会继续更新,分析 调用核心方法JPForwardInvocation,JPExtension等其他部分的实现,对于上述的分析有问题处欢迎及时指出,谢谢!</strong></p>
<p><strong>补充:4.3.1 中为什么需要加入参数个数的说明呢,与方法的签名有关,具体下次更新作分析!!!</strong></p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/JSpatch/">JSpatch</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/iOS/">iOS</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-LintCode-Perfect-Squares" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2016/04/10/LintCode-Perfect-Squares/" class="article-date">
<time datetime="2016-04-10T14:20:54.000Z" itemprop="datePublished">2016-04-10</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/04/10/LintCode-Perfect-Squares/">LintCode Perfect Squares</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, …) which sum to n.</p>
<p>样例<br><br>Given n = 12, return 3 because 12 = 4 + 4 + 4<br>Given n = 13, return 2 because 13 = 4 + 9</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="comment">/**</span><br><span class="line"> * <span class="doctag">@param</span> n a positive integer</span><br><span class="line"> * <span class="doctag">@return</span> an integer</span><br><span class="line"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">numSquares</span><span class="params">(<span class="keyword">int</span> n)</span> </span>{</span><br><span class="line"> <span class="comment">// Write your code here</span></span><br><span class="line"> <span class="keyword">int</span>[] maxSquares = <span class="keyword">new</span> <span class="keyword">int</span>[n + <span class="number">1</span>];</span><br><span class="line"> maxSquares[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> maxSquares[<span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">2</span>;i <= n;i++){</span><br><span class="line"> maxSquares[i] = Integer.MAX_VALUE;</span><br><span class="line"> <span class="keyword">int</span> sqrtNum = (<span class="keyword">int</span>) Math.sqrt(i);</span><br><span class="line"> <span class="keyword">if</span>(sqrtNum * sqrtNum == i){</span><br><span class="line"> maxSquares[i] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">1</span>;j <= sqrtNum;j++){</span><br><span class="line"> <span class="keyword">int</span> minusNum = i - j * j;</span><br><span class="line"> <span class="keyword">if</span>(maxSquares[i] > maxSquares[minusNum] + <span class="number">1</span>){</span><br><span class="line"> maxSquares[i] = maxSquares[minusNum] + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> maxSquares[n];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/LintCode/">LintCode</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/动态规划/">动态规划</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/算法/">算法</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-LintCode-最小调整代价" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2016/04/10/LintCode-最小调整代价/" class="article-date">
<time datetime="2016-04-10T14:20:41.000Z" itemprop="datePublished">2016-04-10</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/04/10/LintCode-最小调整代价/">LintCode 最小调整代价</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>给一个整数数组,调整每个数的大小,使得相邻的两个数的差小于一个给定的整数target,调整每个数的代价为调整前后的差的绝对值,求调整代价之和最小是多少。</p>
<p>样例<br><br>对于数组[1, 4, 2, 3]和target=1,最小的调整方案是调整为[2, 3, 2, 3],调整代价之和是2。返回2。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="comment">/**</span><br><span class="line"> * <span class="doctag">@param</span> A: An integer array.</span><br><span class="line"> * <span class="doctag">@param</span> target: An integer.</span><br><span class="line"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">MinAdjustmentCost</span><span class="params">(ArrayList<Integer> list, <span class="keyword">int</span> target)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">null</span> == list || list.size() <= <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span>[][] minAdjustmentCosts = <span class="keyword">new</span> <span class="keyword">int</span>[list.size()][<span class="number">101</span>];</span><br><span class="line"> <span class="keyword">int</span> result = Integer.MAX_VALUE;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>;i <= <span class="number">100</span>;i ++) <span class="comment">// 最大值100</span></span><br><span class="line"> {</span><br><span class="line"> minAdjustmentCosts[<span class="number">0</span>][i] = Math.abs(i - list.get(<span class="number">0</span>));</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>;i < list.size();i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">0</span>;j <= <span class="number">100</span>;j++)</span><br><span class="line"> {</span><br><span class="line"> minAdjustmentCosts[i][j] = Integer.MAX_VALUE;</span><br><span class="line"> <span class="keyword">int</span> left = Math.max(j - target, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">int</span> right = Math.min(j + target, <span class="number">100</span>);</span><br><span class="line"> <span class="keyword">int</span> diff = Math.abs(j - list.get(i));</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> k = left;k <= right;k++)</span><br><span class="line"> {</span><br><span class="line"> minAdjustmentCosts[i][j] = Math.min(minAdjustmentCosts[i][j], minAdjustmentCosts[i - <span class="number">1</span>][k] + diff); </span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>;i <= <span class="number">100</span>;i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span>(minAdjustmentCosts[list.size() - <span class="number">1</span>][i] < result)</span><br><span class="line"> {</span><br><span class="line"> result = minAdjustmentCosts[list.size() - <span class="number">1</span>][i] ;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/LintCode/">LintCode</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/动态规划/">动态规划</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/算法/">算法</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-LintCode-Paint-House" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2016/04/10/LintCode-Paint-House/" class="article-date">
<time datetime="2016-04-10T14:20:19.000Z" itemprop="datePublished">2016-04-10</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/04/10/LintCode-Paint-House/">LintCode Paint House</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>There are a row of n houses, each house can be painted with one of the three colors: red, blue or green. The cost of painting each house with a certain color is different. You have to paint all the houses such that no two adjacent houses have the same color.</p>
<p>The cost of painting each house with a certain color is represented by a n x 3 cost matrix. For example, costs[0][0] is the cost of painting house 0 with color red; costs[1][2] is the cost of painting house 1 with color green, and so on… Find the minimum cost to paint all houses.</p>
<p>样例<br><br>Given costs = [[14,2,11],[11,14,5],[14,3,10]] return 10</p>
<p>house 0 is blue, house 1 is green, house 2 is blue, 2 + 5 + 3 = 10</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="comment">/**</span><br><span class="line"> * <span class="doctag">@param</span> costs n x 3 cost matrix</span><br><span class="line"> * <span class="doctag">@return</span> an integer, the minimum cost to paint all houses</span><br><span class="line"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">minCost</span><span class="params">(<span class="keyword">int</span>[][] costs)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">null</span> == costs || costs.length <= <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> minRedCost = costs[<span class="number">0</span>][<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">int</span> minBlueCost = costs[<span class="number">0</span>][<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">int</span> minGreenCost = costs[<span class="number">0</span>][<span class="number">2</span>];</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>;i < costs.length;i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">int</span> tempRedCost = minRedCost;</span><br><span class="line"> <span class="keyword">int</span> tempBlueCost = minBlueCost;</span><br><span class="line"> <span class="keyword">int</span> tempGreenCost = minGreenCost;</span><br><span class="line"> minRedCost = Math.min(tempBlueCost + costs[i][<span class="number">0</span>], tempGreenCost + costs[i][<span class="number">0</span>]);</span><br><span class="line"> minBlueCost = Math.min(tempRedCost+ costs[i][<span class="number">1</span>], tempGreenCost + costs[i][<span class="number">1</span>]);</span><br><span class="line"> minGreenCost = Math.min(tempRedCost + costs[i][<span class="number">2</span>], tempBlueCost+ costs[i][<span class="number">2</span>]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> Math.min(Math.min(minRedCost, minBlueCost), minGreenCost);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/LintCode/">LintCode</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/动态规划/">动态规划</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/算法/">算法</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-LintCode-Decode-Ways" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2016/03/27/LintCode-Decode-Ways/" class="article-date">
<time datetime="2016-03-27T14:06:02.000Z" itemprop="datePublished">2016-03-27</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/03/27/LintCode-Decode-Ways/">LintCode Decode Ways</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>A message containing letters from A-Z is being encoded to numbers using the following mapping:</p>
<figure class="highlight lasso"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">'A'</span> <span class="subst">-> </span><span class="number">1</span></span><br><span class="line"><span class="string">'B'</span> <span class="subst">-> </span><span class="number">2</span></span><br><span class="line"><span class="attribute">...</span></span><br><span class="line"><span class="string">'Z'</span> <span class="subst">-> </span><span class="number">26</span></span><br></pre></td></tr></table></figure>
<h4 id="样例">样例</h4><p>Given encoded message 12, it could be decoded as AB (1 2) or L (12).<br>The number of ways decoding 12 is 2.</p>
<p>Given an encoded message containing digits, determine the total number of ways to decode it.</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="comment">/**</span><br><span class="line"> * <span class="doctag">@param</span> s a string, encoded message</span><br><span class="line"> * <span class="doctag">@return</span> an integer, the number of ways decoding</span><br><span class="line"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">numDecodings</span><span class="params">(String s)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">null</span> == s || s.length() <= <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span>(s.length() == <span class="number">1</span> && s.charAt(<span class="number">0</span>) == <span class="string">'0'</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span>[] numDecodings = <span class="keyword">new</span> <span class="keyword">int</span>[s.length() + <span class="number">1</span>];</span><br><span class="line"> numDecodings[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>;i <= s.length();i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span>(s.charAt(i - <span class="number">1</span>) == <span class="string">'0'</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span>(i- <span class="number">2</span> >= <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// 如果出现类似"50"这样的数字直接返回0</span></span><br><span class="line"> <span class="keyword">if</span>(s.charAt(i - <span class="number">2</span>) != <span class="string">'1'</span> && s.charAt(i - <span class="number">2</span>) != <span class="string">'2'</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> numDecodings[i] += numDecodings[i - <span class="number">2</span>];</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> numDecodings[i] += numDecodings[i - <span class="number">1</span>];</span><br><span class="line"> <span class="keyword">if</span>(i - <span class="number">2</span> >= <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span>((s.charAt(i - <span class="number">2</span>) == <span class="string">'1'</span>) || (s.charAt(i - <span class="number">2</span>) == <span class="string">'2'</span> </span><br><span class="line"> && s.charAt(i - <span class="number">1</span>) >= <span class="string">'1'</span> && s.charAt(i - <span class="number">1</span>) <= <span class="string">'6'</span>))</span><br><span class="line"> {</span><br><span class="line"> numDecodings[i] += numDecodings[i - <span class="number">2</span>];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> numDecodings[s.length()];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/LintCode/">LintCode</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/动态规划/">动态规划</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/算法/">算法</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-LintCode乘积最大子序列" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2016/03/27/LintCode乘积最大子序列/" class="article-date">
<time datetime="2016-03-27T14:05:53.000Z" itemprop="datePublished">2016-03-27</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/03/27/LintCode乘积最大子序列/">LintCode乘积最大子序列</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>找出一个序列中乘积最大的连续子序列(至少包含一个数)。</p>
<h4 id="样例">样例</h4><p>比如, 序列 [2,3,-2,4] 中乘积最大的子序列为 [2,3] ,其乘积为6。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="comment">/**</span><br><span class="line"> * <span class="doctag">@param</span> nums: an array of integers</span><br><span class="line"> * <span class="doctag">@return</span>: an integer</span><br><span class="line"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">maxProduct</span><span class="params">(<span class="keyword">int</span>[] nums)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">null</span> == nums || nums.length <= <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> max = nums[<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">int</span> min = nums[<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">int</span> mostMax = max;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>;i < nums.length;i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">int</span> tempMax = max;</span><br><span class="line"> max = Math.max(Math.max(nums[i], tempMax * nums[i]),min * nums[i]);</span><br><span class="line"> min = Math.min(Math.min(nums[i], tempMax * nums[i]),min * nums[i]);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(max > mostMax)</span><br><span class="line"> {</span><br><span class="line"> mostMax = max;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> mostMax;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/LintCode/">LintCode</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/动态规划/">动态规划</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/算法/">算法</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-LintCode-最长上升子序列" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2016/03/20/LintCode-最长上升子序列/" class="article-date">
<time datetime="2016-03-20T14:28:36.000Z" itemprop="datePublished">2016-03-20</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/03/20/LintCode-最长上升子序列/">LintCode 最长上升子序列</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>给定一个整数序列,找到最长上升子序列(LIS),返回LIS的长度。</p>
<h4 id="说明">说明</h4><p>最长上升子序列的定义:</p>
<p>最长上升子序列问题是在一个无序的给定序列中找到一个尽可能长的由低到高排列的子序列,这种子序列不一定是连续的或者唯一的。<br><a href="https://en.wikipedia.org/wiki/Longest_common_subsequence_problem" target="_blank" rel="external">https://en.wikipedia.org/wiki/Longest_common_subsequence_problem</a></p>
<h4 id="样例">样例</h4><p>给出[5,4,1,2,3],这个LIS是[1,2,3],返回 3</p>
<p>给出[4,2,4,5,3,7],这个LIS是[4,4,5,7],返回 4</p>
<h4 id="分析">分析</h4><p>第一种做法(常规做法):<br><br>时间复杂度:O(n^2)</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="comment">/**</span><br><span class="line"> * <span class="doctag">@param</span> nums: The integer array</span><br><span class="line"> * <span class="doctag">@return</span>: The length of LIS (longest increasing subsequence)</span><br><span class="line"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">longestIncreasingSubsequence</span><span class="params">(<span class="keyword">int</span>[] nums)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">null</span> == nums || nums.length <= <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span>[] logestSequences = <span class="keyword">new</span> <span class="keyword">int</span>[nums.length + <span class="number">1</span>];</span><br><span class="line"> logestSequences[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">1</span>;i < nums.length;i++)</span><br><span class="line"> {</span><br><span class="line"> logestSequences[i] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> j = <span class="number">0</span>;j < i;j++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span>(nums[i] >= nums[j] && (logestSequences[j] + <span class="number">1</span> > logestSequences[i])) <span class="comment">// 核心代码</span></span><br><span class="line"> {</span><br><span class="line"> logestSequences[i] = logestSequences[j] + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> longestIncreasingSubsequence = -<span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>;i < nums.length;i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span>(logestSequences[i] > longestIncreasingSubsequence)</span><br><span class="line"> {</span><br><span class="line"> longestIncreasingSubsequence = logestSequences[i];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> longestIncreasingSubsequence;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>第二种做法(贪心策略):<br><br>时间复杂度:O(n*lg(n))</p>
<p>分析:开一个栈,每次取栈顶元素top和读到的元素temp做比较,如果temp > top 则将temp入栈;如果temp < top则二分查找栈中的比temp大的第1个数,并用temp替换它。 最长序列长度即为栈的大小top。<br>这也是很好理解的,对于x和y,如果x < y且Stack[y] < Stack[x],用Stack[x]替换Stack[y],此时的最长序列长度没有改变但序列Q的’’潜力’’增大了。<br></p>
<p>举例:原序列为1,5,8,3,6,7<br>栈为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终栈为1,3,6,7。最长递增子序列为长度4。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="comment">/**</span><br><span class="line"> * <span class="doctag">@param</span> nums: The integer array</span><br><span class="line"> * <span class="doctag">@return</span>: The length of LIS (longest increasing subsequence)</span><br><span class="line"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">longestIncreasingSubsequence</span><span class="params">(<span class="keyword">int</span>[] nums)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">null</span> == nums || nums.length <= <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span>(nums.length == <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">int</span>[] subsequenceStack = <span class="keyword">new</span> <span class="keyword">int</span>[nums.length + <span class="number">1</span>];</span><br><span class="line"> <span class="keyword">int</span> top = <span class="number">0</span>;</span><br><span class="line"> subsequenceStack[<span class="number">0</span>] = -<span class="number">1</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>;i < nums.length;i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span>(nums[i] >= subsequenceStack[top])</span><br><span class="line"> {</span><br><span class="line"> top++;</span><br><span class="line"> subsequenceStack[top] = nums[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">int</span> low = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> high = top;</span><br><span class="line"> <span class="keyword">while</span>(low <= high)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">int</span> middle = (low + high) / <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">if</span>(nums[i] < subsequenceStack[middle])</span><br><span class="line"> {</span><br><span class="line"> high = middle - <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> low = middle + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> subsequenceStack[low] = nums[i];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> top;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/LintCode/">LintCode</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/动态规划/">动态规划</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/算法/">算法</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<article id="post-LintCode-Paint-Fence" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2016/03/20/LintCode-Paint-Fence/" class="article-date">
<time datetime="2016-03-20T14:28:16.000Z" itemprop="datePublished">2016-03-20</time>
</a>
</div>
<div class="article-inner">
<input type="hidden" class="isFancy" />
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2016/03/20/LintCode-Paint-Fence/">LintCode Paint Fence</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>There is a fence with n posts, each post can be painted with one of the k colors.<br>You have to paint all the posts such that no more than two adjacent fence posts have the same color.<br>Return the total number of ways you can paint the fence.</p>
<h5 id="样例:">样例:<br></h5><p>Given n=3, k=2 return 6</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"> post <span class="number">1</span>, post <span class="number">2</span>, post <span class="number">3</span></span><br><span class="line">way1 <span class="number">0</span> <span class="number">0</span> <span class="number">1</span> </span><br><span class="line">way2 <span class="number">0</span> <span class="number">1</span> <span class="number">0</span></span><br><span class="line">way3 <span class="number">0</span> <span class="number">1</span> <span class="number">1</span></span><br><span class="line">way4 <span class="number">1</span> <span class="number">0</span> <span class="number">0</span></span><br><span class="line">way5 <span class="number">1</span> <span class="number">0</span> <span class="number">1</span></span><br><span class="line">way6 <span class="number">1</span> <span class="number">1</span> <span class="number">0</span></span><br></pre></td></tr></table></figure>
<h5 id="分析:">分析:</h5><p>影响第n种的颜色的是第n-1与第n-2种颜色的,因此可能性都为k-1种<br>因此得到如下递推式:<code>maxWays[n] = (k - 1) * (maxWays[n - 1] + maxWays[n - 2]);</code></p>
<figure class="highlight objc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">public class Solution {</span><br><span class="line"> <span class="comment">/**</span><br><span class="line"> * @param n non-negative integer, n posts</span><br><span class="line"> * @param k non-negative integer, k colors</span><br><span class="line"> * @return an integer, the total number of ways</span><br><span class="line"> */</span></span><br><span class="line"> public <span class="keyword">int</span> numWays(<span class="keyword">int</span> n, <span class="keyword">int</span> k) {</span><br><span class="line"> <span class="keyword">if</span>(n == <span class="number">0</span> || k == <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span>(n == <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">return</span> k;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span>[] maxWays = new <span class="keyword">int</span>[n + <span class="number">1</span>];</span><br><span class="line"> maxWays[<span class="number">0</span>] = <span class="number">0</span>;</span><br><span class="line"> maxWays[<span class="number">1</span>] = k;</span><br><span class="line"> maxWays[<span class="number">2</span>] = k * k;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">3</span>;i <= n;i ++)</span><br><span class="line"> {</span><br><span class="line"> maxWays[i] = (k - <span class="number">1</span>) * (maxWays[i - <span class="number">1</span>] + maxWays[i - <span class="number">2</span>]);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> maxWays[n];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/LintCode/">LintCode</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/动态规划/">动态规划</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/算法/">算法</a></li></ul>
</div>
<div class="clearfix"></div>
</div>
</div>
</article>
<nav id="page-nav">
<span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><a class="page-number" href="/page/3/">3</a><a class="page-number" href="/page/4/">4</a><a class="extend next" rel="next" href="/page/2/">Next »</a>
</nav>
</div>
<footer id="footer">
<script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js">
</script>
<div class="outer">
<div id="footer-info">
<div class="footer-left">
© 2016 Arnold
</div>
<span id="busuanzi_container_site_pv" style='display:none'>
本站总访问量<span id="busuanzi_value_site_pv"></span>次
</span>
<span id="busuanzi_container_page_pv" style='display:none'>
本文总阅读量<span id="busuanzi_value_page_pv"></span>次
</span>
<div class="footer-right">
<a href="http://hexo.io/" target="_blank">Hexo</a> Theme <a href="https://github.com/litten/hexo-theme-yilia" target="_blank">Yilia</a> by Litten
</div>
</div>
</div>
</footer>