-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
377 lines (310 loc) · 69.8 KB
/
search.xml
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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Universal Links</title>
<url>/2020/07/14/universal_links/</url>
<content><![CDATA[<h4 id="什么是Universal-Links"><a href="#什么是Universal-Links" class="headerlink" title="什么是Universal Links?"></a>什么是Universal Links?</h4><p><strong>Universal Links</strong>是iOS9推出的一项功能,使你的应用可以通过http或者https协议来启动。如果是已经安装了app,则直接打开,如果没有安装,则打开网页(网页的内容一般是公司主页)。</p>
<p>其实在iOS9之前,从外部唤醒app的方式有<strong>scheme</strong>。但是这种方式需要提前判断系统中是否安装了能够响应此scheme的app,而且这种方式在微信中是禁止的。</p>
<h4 id="Universal-Links-特性"><a href="#Universal-Links-特性" class="headerlink" title="Universal Links 特性"></a>Universal Links 特性</h4><ul>
<li><strong>唯一性</strong>:不像自定义的<strong>scheme</strong>,<strong>Universal Links</strong>不会被其它的app所使用。(你的域名总归不会被其它公司使用吧)</li>
<li><strong>安全性</strong>:当用户第一次打开app的时候,iOS会检查apple-app-site-association文件以确保你的网站允许app以其名义打开网址,这个文件的上传是有权限控制的,所以网站和app的关联是安全的。</li>
<li><strong>灵活性</strong>:当手机中没有安装app的时候,当你点开链接时,也会在safari中打开网页来展示你网站的内容。</li>
<li><strong>安全&灵活</strong>:避免别的app检测你是否安装。</li>
</ul>
<h4 id="如何使用Universal-Links?"><a href="#如何使用Universal-Links?" class="headerlink" title="如何使用Universal Links?"></a>如何使用Universal Links?</h4><ol>
<li>首先得准备一个域名,要求这个域名支持https协议</li>
<li>需要在开发者中心做配置:找到对应的App ID,在Application Services列表里有Associated Domains一条,把它变为Enabled就可以了。</li>
</ol>
<img src="https://s1.ax1x.com/2020/07/15/UwZO6x.png" style="zoom:50%;">
<ol start="3">
<li>Xcode配置:将Associated Domains打开(注意xcode10.0以下是图一所示,xcode10.0以上需要手动添加,如图二所示)。同时填写我们的域名,<strong>必须以applinks:为前缀</strong>,比如你的域名为<em>test.com</em>,那么填写的就是<em>applinks:test.com</em>。app会在第一次启动的时候通过填写的域名来下载apple-app-site-association文件。</li>
</ol>
<p>图一:</p>
<img src="https://s1.ax1x.com/2020/07/15/UwZXX6.png" alt="图一" style="zoom:67%;">
<p>图二:</p>
<img src="https://s1.ax1x.com/2020/07/15/UwZL11.png" alt="图二" style="zoom:67%;">
<ol start="4">
<li>创建apple-app-site-association文件(服务端工作),格式如下</li>
</ol>
<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">"applinks"</span>: {</span><br><span class="line"> <span class="attr">"apps"</span>: [],</span><br><span class="line"> <span class="attr">"details"</span>: [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"appID"</span>: <span class="string">"TEAMIDSHSAUX.com.test.bundle"</span>,</span><br><span class="line"> <span class="attr">"paths"</span>: [ <span class="string">"*"</span> ]</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"appID"</span>: <span class="string">"TEAMIDSHSAUXANOTHER.com.test.bundle"</span>,</span><br><span class="line"> <span class="attr">"paths"</span>: [ <span class="string">"*"</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>
<h6 id="相关参数说明"><a href="#相关参数说明" class="headerlink" title="相关参数说明"></a>相关参数说明</h6><ul>
<li>appID:由<strong>TeamID.BundleID</strong>组成。TeamID可在开发者中心查看,BundleID可在Xcode中查看。</li>
<li>paths:设定一个App的路径支持列表,只有这些指定的路径链接才会被App所处理。(paths是大小写敏感,*是通配符表示任意路径,一般填写这个就可以)</li>
</ul>
<ol start="5">
<li>上传文件到域名所对应的根目录,这是为了苹果能获取到你上传的这个文件。(服务端工作)</li>
</ol>
<h4 id="Universal-Links验证"><a href="#Universal-Links验证" class="headerlink" title="Universal Links验证"></a>Universal Links验证</h4><p>一般通常在<strong>备忘录</strong>中输入一个链接,一般类似</p>
<p><code>https://你的域名/填写的paths</code></p>
<p>如果paths中填写的是通配符 * ,那paths就不用带上。</p>
<p>点击此链接,就会直接跳转到对应的app,这就算成功了。</p>
<img src="https://s1.ax1x.com/2020/07/15/UwebVS.png" alt="备忘录中" style="zoom:50%;">
<p>或者你将要测试的链接放在safari中打开,在出现的网页上下滑,可以看到有<strong>在xx应用中打开</strong>:</p>
<img src="https://s1.ax1x.com/2020/07/15/UweqUg.png" alt="safari中" style="zoom:50%;">
<p>在微信的网页浏览器里也是可以的。虽然微信屏蔽了所有的<strong>scheme</strong>方式跳转到其它app,但是Universal Links是由系统直接处理的,这也就实现了从微信跳转到我们的app。</p>
<h4 id="进入app的处理"><a href="#进入app的处理" class="headerlink" title="进入app的处理"></a>进入app的处理</h4><p>当用户点击某个链接,直接进入到我们的app,会触发下面的api。在这儿我们可以处理相关的逻辑,比如你们的app支持router跳转,根据传过来的参数,就可以跳转到具体的页面。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {</span><br><span class="line"> NSURLComponents *comps = [[NSURLComponents alloc] initWithString:userActivity.webpageURL.absoluteString];</span><br><span class="line"> if ([comps.host isEqualToString:@"你们的域名"]) {</span><br><span class="line"> /// universal links 处理</span><br><span class="line"> for (NSURLQueryItem *item in comps.queryItems) {</span><br><span class="line"> if ([item.name isEqualToString:@"url"]) {</span><br><span class="line"> self.linksRouteUrlString = item.value;</span><br><span class="line"> break;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> if (self.isUniverlinksFromLanch) {</span><br><span class="line"> /// 避免冷启动执行两次 handleURL</span><br><span class="line"> self.isUniverlinksFromLanch = NO;</span><br><span class="line"> self.isNeedHandleUniversalLinks = YES;</span><br><span class="line"> } else {</span><br><span class="line"> // 热启动</span><br><span class="line"> [self p_handleUniversalLinks:nil];</span><br><span class="line"> return YES;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return NO;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>注意这儿的逻辑:</p>
<ol>
<li>建议使用 <strong>NSURLComponents</strong> 类去处理url逻辑。</li>
<li>这儿的query中value中的值,建议<strong>encode</strong>,否则解析出来的可能出错。</li>
<li>这个回调的处理建议是热启动,如果是冷启动,建议放到<code>homeVC</code>的<code>viewDidAppear:</code>函数中,这儿的考虑是时机加载问题,所以冷启动的情况下,只是处理标记位。</li>
<li>既然区分了冷热启动,最好就把冷启动这个方法的实现屏蔽掉,避免执行两遍。</li>
</ol>
<h4 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h4><ol>
<li><strong>必须要求跨域</strong>:即你当前webView的url域名,与你跳转的app域名不一致时,才生效。</li>
<li>当我们的app在手机上第一次运行时,如果会支持<code>Associated Domains</code>功能,那么iOS会自动GET定义的Domain下的<code>apple-app-site-association</code>文件</li>
<li>iOS会先请求<code>https://domain.com/.well-known/apple-app-site-association</code>,如果请求不到,再去请求<code>https://domain.com/apple-app-site-association</code>,所以如果想要避免服务器接收过多GET请求,可以直接把<code>apple-app-site-association</code>文件放在<code>./well-know</code>目录下</li>
<li>服务器上的<code>apple-app-site-association</code>文件更新不会让iOS本地的<code>apple-app-site-association</code>文件同步更新,即iOS只会在app第一次启动时请求一次,以后除非app更新或者重新安装,否则不会在每次打开时去请求<code>apple-app-site-association</code>文件</li>
</ol>
]]></content>
<tags>
<tag>iOS technology</tag>
</tags>
</entry>
<entry>
<title>Snippet</title>
<url>/2015/10/26/Snippet/</url>
<content><![CDATA[<p>Xcode在编码的时候,系统代码块给我们提供了很大的便利。然而,有时候,也给我们带来很大的困扰。</p>
<p>例如,系统的 if 代码块,大括号是紧跟括号之后的。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">if (condition) {</span><br><span class="line"> statements</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>但是现在呢,公司的代码规范是要求另起一行。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">if (condition)</span><br><span class="line">{</span><br><span class="line"> statements</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>那每次都得手动去调,的确是件很影响心情的事。</p>
<p>现在,就教大家怎么去修改系统的代码块样式。</p>
<h4 id="xcode内置代码模板"><a href="#xcode内置代码模板" class="headerlink" title="xcode内置代码模板"></a>xcode内置代码模板</h4><p>Xcode的所有代码模板是用一个plist格式xml文件描述的,这文件存储在Xcode的安装目录:<br><code>/Applications/Xcode.app/Contents/Frameworks/IDEKit.framework/Versions/A/Resources/SystemCodeSnippets.codesnippets</code></p>
<blockquote>
<p>注意:Xcode5.1之前是在这个目录下<br>/Applications/Xcode.app/Contents/PlugIns/IDECodeSnippetLibrary.ideplugin/Contents/Resources/¬<br>SystemCodeSnippets.codesnippets</p>
</blockquote>
<p>用任意文本编辑打开这个文件,如下显示:</p>
<p><img src="http://img.blog.csdn.net/20151026142350298" alt></p>
<p>这个文件是没有权限去修改的。在这里就是所有的Xcode代码块的配置,包括常见的 if , if else , switch,dispatch等相关代码块。</p>
<p>每一个 key 所对应的意义也很好理解。下面教你如何去理解。在此之前,你应该知道Xcode左下角有个花括号的页签,这里面存储了很多代码提示模板,就是以上所配置的。</p>
<p><img src="http://img.blog.csdn.net/20151026143028995" alt="这里写图片描述"></p>
<p>如果是自己拖拽进去的会有 User 标志。</p>
<p><img src="http://img.blog.csdn.net/20151026143656268" alt="这里写图片描述"></p>
<h4 id="自定义代码模块"><a href="#自定义代码模块" class="headerlink" title="自定义代码模块"></a>自定义代码模块</h4><blockquote>
<p>关于拖拽进代码块的操作这里不作叙述,有不会的可以上网查查或者留言回复我再告之。</p>
</blockquote>
<p>自己定义的代码块是在以下目录下:<br><code>~/Library/Developer/Xcode/UserData/CodeSnippets/</code></p>
<blockquote>
<p>注意:只有自己定义过代码块,才会在UserData目录下存在CodeSnippets文件夹,否则是不存在的。</p>
</blockquote>
<p>你可以试试写个</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">randomColor(`self.view.backgroundColor = [UIColorcolorWithRed:arc4random()%255/256.0f green:arc4random()%255/256.0f blue:arc4random()%255/256.0f alpha:1.0f])</span><br></pre></td></tr></table></figure>
<p>的代码块,拖进去,你会发现在CodeSnippets文件夹下多了这么一个文件。</p>
<p><img src="http://img.blog.csdn.net/20151026145134013" alt></p>
<p>这儿的名字是可以更改的,后缀要写成.codesnippet 。</p>
<p>所以我把它更改成如下名字,便于分辨。</p>
<p><img src="http://img.blog.csdn.net/20151026145437453" alt></p>
<p>用任意文本编辑器打开,和Xcode左下角对比,很容易明白各个key所对应的意思。</p>
<p><img src="http://img.blog.csdn.net/20151026150041777" alt> <img src="http://img.blog.csdn.net/20151026150100108" alt></p>
<p>这里要注意的是以下几个 key 。<br><strong>IDECodeSnippetIdentifier</strong> 是唯一标识符,其中 if 模板的唯一标记是D70E6D11-0297-4BAB-88AA-86D5D5CBBC5D,不能重名,重名即会覆盖。</p>
<p>这也是我们更改系统代码块样式的核心思想!!!</p>
<p><strong>IDECodeSnippetVersion</strong> 是版本号,一般系统的都是1,我们自己定义的写成2。保持默认1也没多大问题,只是Xcode有时会自动用标识符替换你的自定义命名,所以用2是比较合适的。</p>
<p>剩下的很重要的一步,如果是自定义的,一定要加以下这个 key, <strong>IDECodeSnippetUserSnippet</strong> ,定义为 true 。如果是自定义的而没有这个字段,Xcode是会崩溃的。</p>
<p><img src="http://img.blog.csdn.net/20151026151504459" alt></p>
<p>现在用 if 代码块对比一下系统的和自定义的区别。</p>
<p>系统的是</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><dict></span><br><span class="line"> <key>IDECodeSnippetVersion</key></span><br><span class="line"> <integer>1</integer></span><br><span class="line"> <key>IDECodeSnippetCompletionPrefix</key></span><br><span class="line"> <string>if</string></span><br><span class="line"> <key>IDECodeSnippetContents</key></span><br><span class="line"> <string>if (<#condition#>) {</span><br><span class="line"> <#statements#></span><br><span class="line">}</string></span><br><span class="line"> <key>IDECodeSnippetIdentifier</key></span><br><span class="line"> <string>D70E6D11-0297-4BAB-88AA-86D5D5CBBC5D</string></span><br><span class="line"> <key>IDECodeSnippetLanguage</key></span><br><span class="line"> <string>Xcode.SourceCodeLanguage.C</string></span><br><span class="line"> <key>IDECodeSnippetSummary</key></span><br><span class="line"> <string>Execute code only when a certain condition is true.</string></span><br><span class="line"> <key>IDECodeSnippetTitle</key></span><br><span class="line"> <string>If Statement</string></span><br><span class="line"> <key>IDECodeSnippetCompletionScopes</key></span><br><span class="line"> <array></span><br><span class="line"> <string>CodeBlock</string></span><br><span class="line"> </array></span><br><span class="line"> </dict></span><br></pre></td></tr></table></figure>
<p>自定义的是</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><dict></span><br><span class="line"> <key>IDECodeSnippetVersion</key></span><br><span class="line"> <integer>2</integer></span><br><span class="line"> <key>IDECodeSnippetUserSnippet</key></span><br><span class="line"> <true/></span><br><span class="line"> <key>IDECodeSnippetCompletionPrefix</key></span><br><span class="line"> <string>if</string></span><br><span class="line"> <key>IDECodeSnippetContents</key></span><br><span class="line"> <string>if (<#condition#>) </span><br><span class="line">{</span><br><span class="line"> <#statements#></span><br><span class="line">}</string></span><br><span class="line"> <key>IDECodeSnippetIdentifier</key></span><br><span class="line"> <string>D70E6D11-0297-4BAB-88AA-86D5D5CBBC5D</string></span><br><span class="line"> <key>IDECodeSnippetLanguage</key></span><br><span class="line"> <string>Xcode.SourceCodeLanguage.C</string></span><br><span class="line"> <key>IDECodeSnippetSummary</key></span><br><span class="line"> <string>Execute code only when a certain condition is true.</string></span><br><span class="line"> <key>IDECodeSnippetTitle</key></span><br><span class="line"> <string>If Statement</string></span><br><span class="line"> <key>IDECodeSnippetCompletionScopes</key></span><br><span class="line"> <array></span><br><span class="line"> <string>CodeBlock</string></span><br><span class="line"> </array></span><br><span class="line"> </dict></span><br></pre></td></tr></table></figure>
<h2 id="修改系统的代码块样式"><a href="#修改系统的代码块样式" class="headerlink" title="修改系统的代码块样式"></a>修改系统的代码块样式</h2><p>1.前往文件夹<br> <code>~/Library/Developer/Xcode/UserData/CodeSnippets</code></p>
<p> 2.创建 <strong>if.codesnippet</strong> 文件</p>
<p>3.复制以下模板:</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="meta"><?xml version="1.0" encoding="UTF-8"?></span></span><br><span class="line"><span class="meta"><!DOCTYPE <span class="meta-keyword">plist</span> <span class="meta-keyword">PUBLIC</span> <span class="meta-string">"-//Apple//DTD PLIST 1.0//EN"</span> <span class="meta-string">"http://www.apple.com/DTDs/PropertyList-1.0.dtd"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">plist</span> <span class="attr">version</span>=<span class="string">"1.0"</span>></span></span><br><span class="line"> <span class="comment"><!--code config here--></span></span><br><span class="line"><span class="tag"></<span class="name">plist</span>></span></span><br></pre></td></tr></table></figure>
<p>4.找到系统的 if 代码块,粘贴 过来。</p>
<p>5.修改 <strong>IDECodeSnippetVersion</strong> 从1变成2。</p>
<p>6.增加 <strong>IDECodeSnippetUserSnippet</strong> 为 true。</p>
<p>7.保存,关闭。退出Xcode ,再打开,输入 if ,看看此时的 if 样式,大功告成!!!!</p>
<p>最后修改的 if 配置文件应是如下:</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="meta"><?xml version="1.0" encoding="UTF-8"?></span></span><br><span class="line"><span class="meta"><!DOCTYPE <span class="meta-keyword">plist</span> <span class="meta-keyword">PUBLIC</span> <span class="meta-string">"-//Apple//DTD PLIST 1.0//EN"</span> <span class="meta-string">"http://www.apple.com/DTDs/PropertyList-1.0.dtd"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">plist</span> <span class="attr">version</span>=<span class="string">"1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dict</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">key</span>></span>IDECodeSnippetVersion<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">integer</span>></span>2<span class="tag"></<span class="name">integer</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">key</span>></span>IDECodeSnippetUserSnippet<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">true</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">key</span>></span>IDECodeSnippetCompletionPrefix<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">string</span>></span>if<span class="tag"></<span class="name">string</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">key</span>></span>IDECodeSnippetContents<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">string</span>></span>if (<span class="symbol">&lt;</span>#condition#<span class="symbol">&gt;</span>) </span><br><span class="line">{</span><br><span class="line"> <span class="symbol">&lt;</span>#statements#<span class="symbol">&gt;</span></span><br><span class="line">}<span class="tag"></<span class="name">string</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">key</span>></span>IDECodeSnippetIdentifier<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">string</span>></span>D70E6D11-0297-4BAB-88AA-86D5D5CBBC5D<span class="tag"></<span class="name">string</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">key</span>></span>IDECodeSnippetLanguage<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">string</span>></span>Xcode.SourceCodeLanguage.C<span class="tag"></<span class="name">string</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">key</span>></span>IDECodeSnippetSummary<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">string</span>></span>Execute code only when a certain condition is true.<span class="tag"></<span class="name">string</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">key</span>></span>IDECodeSnippetTitle<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">string</span>></span>If Statement<span class="tag"></<span class="name">string</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">key</span>></span>IDECodeSnippetCompletionScopes<span class="tag"></<span class="name">key</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">array</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">string</span>></span>CodeBlock<span class="tag"></<span class="name">string</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">array</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dict</span>></span></span><br><span class="line"><span class="tag"></<span class="name">plist</span>></span></span><br></pre></td></tr></table></figure>
<h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>根据 if 的配置逻辑,完全可以再修改 if-else,switch,dispatch_once,dispatch_after等样式。</p>
<p>以下是我修改的一些:</p>
<p><img src="http://img.blog.csdn.net/20151026153629974" alt></p>
<p> 这个CodeSnippets文件夹是可以复制给他人,别人安装在相同的目录下,便也有了相同的功能。很适合一个团队的代码规整。</p>
]]></content>
<tags>
<tag>iOS technology</tag>
</tags>
</entry>
<entry>
<title>ScrollView嵌套scrollView,如何解决联动问题</title>
<url>/2018/01/09/ScrollView%E5%B5%8C%E5%A5%97scrollView%EF%BC%8C%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3%E8%81%94%E5%8A%A8%E9%97%AE%E9%A2%98/</url>
<content><![CDATA[<h4 id="背景介绍"><a href="#背景介绍" class="headerlink" title="背景介绍"></a>背景介绍</h4><p>APP中经常用到的一个场景,一个页面,可以 <strong>竖向</strong> 滑动,中间有个segment控件,支持 <strong>横向</strong> 滑动,横向滑动的scrollView上面又放了一个个可以竖向滑动的scrollView。</p>
<h4 id="github地址"><a href="#github地址" class="headerlink" title="github地址:"></a>github地址:</h4><p><a href="https://github.com/oscarwuer/yhlinkagetableview" target="_blank" rel="noopener">https://github.com/oscarwuer/yhlinkagetableview</a></p>
<h4 id="框架搭建"><a href="#框架搭建" class="headerlink" title="框架搭建"></a>框架搭建</h4><p>整个页面基于一个tableView,从上而下,分别是<code>海报cell</code>,<code>简介cell</code>,<code>sectionHeaderView</code>,<code>容器cell</code>。所以一共两个section,前一个section里有两个cell,后一个section只放一个容器cell。</p>
<p>由于最下面的也有三个tableView,为了区分开来,主页上的我们称为 <strong>mainTableView</strong> ,下面那三个称为 <strong>listTableView</strong> 。</p>
<p><code>sectionHeaderView</code> 上放的是 <strong>HMSegmentControl</strong> 。</p>
<p><code>容器cell</code>是重点,上面放了个 <strong>UIScrollView</strong> ,横向摆放三个tableViewController的view。注意,是控制器的view。你也可以直接放三个tableView在scrollView上,然后你会发现你的cell里N多个<code>if... else...</code></p>
<p>所以整体架构就是这样</p>
<h4 id="原理实现"><a href="#原理实现" class="headerlink" title="原理实现"></a>原理实现</h4><p>在介绍原理之前,有个函数需要认识下</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">/*</span><br><span class="line"> * 是否允许多个手势识别器共同识别,一个控件的手势识别后是否阻断手势识别继续向下传播,默认返回NO;如果为YES,响应者链上层对象触发手势识别后,如果下层对象也添加了手势并成功识别也会继续执行,否则上层对象识别后则不再继续传播</span><br><span class="line"> */</span><br><span class="line">- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer</span><br><span class="line">{</span><br><span class="line"> return YES;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>备注上解释的很清楚,第一个重点,我们需要写一个tableView基类,实现这个方法, <strong>mainTableView</strong> 继承自此基类tableView。</p>
<p><strong>listTableView</strong> 就是普通的tableView即可。现在的效果是,滑动listTableView,mainTableView也会跟着滑动,所以现在需要做的是,当listTableView未滑动到顶的时候,禁止mainTableView的滑动。 <strong>核心就是在一定的时机,设置mainTableView的scrollEnabled为NO和YES</strong> 。此处为第二个重点</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">- (void)mmtdOptionalScrollViewDidScroll:(UIScrollView *)scrollView {</span><br><span class="line"> self.tableView.scrollEnabled = NO;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">- (void)mmtdOptionalScrollViewDidEndDecelerating:(UIScrollView *)scrollView {</span><br><span class="line"> NSUInteger page = scrollView.contentOffset.x/[UIScreen mainScreen].bounds.size.width;</span><br><span class="line"> [self.sectionView.segmentControl setSelectedSegmentIndex:page animated:YES];</span><br><span class="line"> </span><br><span class="line"> self.tableView.scrollEnabled = YES;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>第三个重点,当listTableView滑动到顶时,需要发送 <strong>通知</strong> 告诉mainTableView,我已经到顶了,该你滑动了。其实就是改变两个bool值,因为在mainTableView的 <code>scrollViewDidScroll</code> 代理方法中,需要用来判断。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">- (void)scrollViewDidScroll:(UIScrollView *)scrollView {</span><br><span class="line"> if (scrollView == self.tableView) {</span><br><span class="line"> </span><br><span class="line"> CGFloat offset = 64;</span><br><span class="line"> if (iPhoneX) {</span><br><span class="line"> offset = 88;</span><br><span class="line"> }</span><br><span class="line"> CGFloat bottomCellOffset = [self.tableView rectForSection:1].origin.y - offset;</span><br><span class="line"> bottomCellOffset = floorf(bottomCellOffset);</span><br><span class="line"> </span><br><span class="line"> if (scrollView.contentOffset.y >= bottomCellOffset) {</span><br><span class="line"> scrollView.contentOffset = CGPointMake(0, bottomCellOffset);</span><br><span class="line"> if (self.canScroll) {</span><br><span class="line"> self.canScroll = NO;</span><br><span class="line"> self.containerCell.objectCanScroll = YES;</span><br><span class="line"> }</span><br><span class="line"> }else{</span><br><span class="line"> //子视图没到顶部</span><br><span class="line"> if (!self.canScroll) {</span><br><span class="line"> scrollView.contentOffset = CGPointMake(0, bottomCellOffset);</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>至此,功能大体上就完活了。不过还是有需要优化的地方,也就是第四个重点。当我们在横向滑动来切换segment时,有时候会触发使得纵向也滑动了,很不好的体验。所以需要在<code>容器cell</code>中,横向滑动的时候,禁止纵向滑动</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">- (void)scrollViewDidScroll:(UIScrollView *)scrollView {</span><br><span class="line"> // 为了横向滑动的时候,外层的tableView不动</span><br><span class="line"> if (!self.isSelectIndex) {</span><br><span class="line"> if (scrollView == self.scrollView) {</span><br><span class="line"> if (self.delegate &&</span><br><span class="line"> [self.delegate respondsToSelector:@selector(mmtdOptionalScrollViewDidScroll:)]) {</span><br><span class="line"> [self.delegate mmtdOptionalScrollViewDidScroll:scrollView];</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>另外一个需要优化的地方,重点5,是当数据多的一页滑到中间,这时候切换segment,换到数据少的一页,向下拉动一点,再换回刚刚那个滑到中间的那一页(gif演示中最后一部分)。需要在<code>容器cell</code>里做如下操作,否则会闪一下。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">- (void)setObjectCanScroll:(BOOL)objectCanScroll {</span><br><span class="line"> _objectCanScroll = objectCanScroll;</span><br><span class="line"> </span><br><span class="line"> self.oneVC.vcCanScroll = objectCanScroll;</span><br><span class="line"> self.twoVC.vcCanScroll = objectCanScroll;</span><br><span class="line"> self.threeVC.vcCanScroll = objectCanScroll;</span><br><span class="line"> </span><br><span class="line"> if (!objectCanScroll) {</span><br><span class="line"> [self.oneVC.tableView setContentOffset:CGPointZero animated:NO];</span><br><span class="line"> [self.twoVC.tableView setContentOffset:CGPointZero animated:NO];</span><br><span class="line"> [self.threeVC.tableView setContentOffset:CGPointZero animated:NO];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>最后一点,切记,mainTableView的bounces可以设置为NO,但是,listTableView的bounces一定得是YES,否则偏移量会有那么一丝丝误差使得滑不动。</strong></p>
]]></content>
<tags>
<tag>code</tag>
</tags>
</entry>
<entry>
<title>Mac使用Nginx搭建本地服务器</title>
<url>/2015/11/27/Mac%E4%BD%BF%E7%94%A8Nginx%E6%90%AD%E5%BB%BA%E6%9C%AC%E5%9C%B0%E6%9C%8D%E5%8A%A1%E5%99%A8/</url>
<content><![CDATA[<h4 id="WHAT"><a href="#WHAT" class="headerlink" title="WHAT"></a>WHAT</h4><pre><code>本篇主要是基于Nginx在Mac上搭建自己的服务器。
我相信很多朋友肯定是第一次听到Nginx,关于它具有怎样的传奇,这儿肯定说不完也说不透.
有兴趣的朋友可以自行google或者baidu.</code></pre><h4 id="WHY"><a href="#WHY" class="headerlink" title="WHY"></a>WHY</h4><pre><code>为什么要搭建自己的服务器呢。
好处肯定多多,这儿说一条——模拟数据。
很多时候,我们在前端开发的过程中,API接口没有做好.
当我们要铺界面时,如果等待API的开发完成,无疑是件很耽误工作的事情。
还有一点,即使API完成了,我们开发完项目时,需要自测各种极限的数据。例如</code></pre><ul>
<li>约定好返回的是NSString,如果返回的是NSNumber,你会崩吗?</li>
<li>约定好的一个字段有返回值,突然间返回为null,你会崩吗?</li>
<li>约定好的label上的赋值文本是最多7个字,突然给你77个字,你会变得很丑吗?</li>
</ul>
<p> <strong>有人问了,不是约定好了吗?</strong><br> <strong>话说API也是人开发的,你写的客户端还会crash呢,就不允许人家后台出错啦???</strong><br> <strong>讲道理嘛大兄弟!</strong></p>
<h4 id="HOW"><a href="#HOW" class="headerlink" title="HOW"></a>HOW</h4><pre><code>复杂来说,这是件很难的事情,因为无论是Nginx,还是它所需要的准备工作,都是一个个庞然大物。搞不懂!
你需要安装Nginx,你可以自己独立装,也可以用别的包安装.
后者那推荐的是Homebrew.这也是下面我带大家一起来做的。
如果是前者,不要问我,我装了一下午,然后失败了。
简单来说,跟着我来做,几个步骤,几分钟,带你实现。</code></pre><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">平台:Mac</span><br><span class="line">系统:10.11.1</span><br></pre></td></tr></table></figure>
<p> <strong>1. 安装Homebrew</strong></p>
<p> <strong>2. 安装Nginx</strong> </p>
<p> <strong>3. 启动Nginx</strong> </p>
<p> <strong>4. 配置JSON文件</strong> </p>
<p> <strong>5. 配置Nginx</strong> </p>
<p> <strong>6. 展现成果</strong></p>
<hr>
<p><strong>1.安装Homebrew</strong></p>
<p>打开终端,输入:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"</span><br></pre></td></tr></table></figure>
<p>跟着步骤走。终端都有提示。</p>
<p><strong>2.依赖Homebrew安装Nginx</strong></p>
<p>依旧在终端中</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">brew install nginx</span><br></pre></td></tr></table></figure>
<p>这个等待时间会比较长。如果你仔细看了终端的进程。你会发现你用homebrew安装是多么聪明了。其实在安装nginx之前还要帮你安装很多别的,如果你独立安装,是很费劲的。。。</p>
<p><strong>3.启动Nginx</strong></p>
<p>依旧在终端里,输入</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">nginx</span><br></pre></td></tr></table></figure>
<p>回车就行了。别惊讶,就这么简单。<br>怎样验证Nginx能用了呢。你在浏览器里输入<code>localhost:8080</code>,回车,出现以下画面,说明成功。<br><img src="https://s1.ax1x.com/2020/07/20/UhAKc6.jpg" alt="oscar"></p>
<p>如果是以下画面,转身抬头挺胸三鞠躬,再回来看看那里出错了。<br><img src="https://s1.ax1x.com/2020/07/20/UhAMjK.jpg" alt="这里写图片描述"></p>
<p><strong>4.配置JSON文件</strong></p>
<p>那配置文件是在</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">/usr/local/Cellar/nginx/1.8.0/html/</span><br></pre></td></tr></table></figure>
<p>这个目录下会默认有50x.html和index.html两个文件。你再新加一个json文件,取个名字叫<strong>oscar.json</strong>,里面随便写些字典数组,保存。</p>
<p><strong>5.配置Nginx</strong></p>
<p>安装完之后,默认路径是在</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">/usr/local/etc/</span><br></pre></td></tr></table></figure>
<p>这个文件下会有一个nginx文件夹和一个openssl的文件夹。<br>点开nginx文件夹,里面会有个</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">nginx.conf.default</span><br></pre></td></tr></table></figure>
<p>的文件,记住,不要看错了,这里面的名字很相像。用文本编辑器打开,里面默认是有内容的,可以看看。将以下内容粘贴进去。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">server { </span><br><span class="line"> listen 8080; </span><br><span class="line"> server_name localhost; </span><br><span class="line"> #access_log logs/host.access.log main; </span><br><span class="line"> location ~* { </span><br><span class="line"> add_header Content-Type "application/json";</span><br><span class="line"> root html; </span><br><span class="line"> if (!-f $request_filename) { </span><br><span class="line"> rewrite ^/(.*) /$1.json last;</span><br><span class="line"> } </span><br><span class="line"> index index.php index.html index.htm;</span><br><span class="line"> } </span><br><span class="line">error_page 405 =200 http://$host$request_uri; }</span><br></pre></td></tr></table></figure>
<p>6.展现成果<br> 如果你新加的json文件叫oscar.json,目录是在刚刚说的</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">/usr/local/Cellar/nginx/1.8.0/html/oscar.json</span><br></pre></td></tr></table></figure>
<p>那么你在浏览器里输入</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">localhost:8080/oscar.json</span><br></pre></td></tr></table></figure>
<p>看看你的成果吧!</p>
<p>如果你的页面有你配置的JSON文件,那么恭喜你。别人如果想访问你的资源,保证你两在同一个局域网内,然后把你的ip地址替换localhost,再看看你朋友的电脑的页面吧。</p>
]]></content>
<tags>
<tag>system</tag>
</tags>
</entry>
<entry>
<title>链表集锦</title>
<url>/2019/07/29/%E9%93%BE%E8%A1%A8%E9%9B%86%E9%94%A6/</url>
<content><![CDATA[<p>关于链表节点的定义:</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">Node</span> {</span></span><br><span class="line"> <span class="keyword">int</span> data;</span><br><span class="line"> Node *next;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="1-删除链表节点"><a href="#1-删除链表节点" class="headerlink" title="1. 删除链表节点"></a>1. 删除链表节点</h4><p><strong>题目描述:</strong>给定链表的头指针和一个节点指针,在O(1)时间删除该节点。</p>
<p><strong>分析:</strong>主要思想是用下一个节点数据覆盖要删除的节点,然后再删除下一个节点。但是如果要删除的节点是尾节点时,就行不通了。</p>
<p>代码如下:</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">//O(1)时间删除链表节点,从无头单链表中删除节点</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">deleteRandomNode</span><span class="params">(Node *cur)</span> </span>{</span><br><span class="line"> assert(cur != <span class="literal">NULL</span>)</span><br><span class="line"> assert(cur->next != <span class="literal">NULL</span>)</span><br><span class="line"> Node *pNext = cur->next;</span><br><span class="line"> cur->data = pNext->data;</span><br><span class="line"> cur->next = pNext->next;</span><br><span class="line"> <span class="keyword">delete</span> pNext;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="2-单链表反转"><a href="#2-单链表反转" class="headerlink" title="2. 单链表反转"></a>2. 单链表反转</h4><p><strong>题目描述:</strong>输入一个单向链表,输出逆序反转后的链表</p>
<p><strong>分析:</strong>链表的转置是一个很常见,很基础的数据结构问题,非递归的算法很简单,用三个临时指针<code>pre</code> 、<code>head</code> 、<code>next</code> 在链表上循环一遍即可。递归算法也比较简单,具体如下</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 单链表反转,非递归方法</span></span><br><span class="line"><span class="function">Node * <span class="title">reverseByLoop</span><span class="params">(Node *head)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(head == <span class="literal">NULL</span> || head->next == <span class="literal">NULL</span>) <span class="keyword">return</span> head;</span><br><span class="line"> Node *pre = <span class="literal">NULL</span>;</span><br><span class="line"> Node *next = NUILL;</span><br><span class="line"> <span class="keyword">while</span>(head != <span class="literal">NULL</span>) {</span><br><span class="line"> next = head->next;</span><br><span class="line"></span><br><span class="line"> head-next = pre;</span><br><span class="line"> pre = head;</span><br><span class="line"> head = next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> pre;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 单链表反转,递归方法</span></span><br><span class="line"><span class="function">Node * <span class="title">reverseByRecursion</span><span class="params">(Node *head)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(head == NUL || head->next == <span class="literal">NULL</span>) <span class="keyword">return</span> head;</span><br><span class="line"> Node *newHead = reverseByRecursion(head-next);</span><br><span class="line"> </span><br><span class="line"> head->next-next = head;</span><br><span class="line"> head->next = <span class="literal">NULL</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> newHead;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="3-求链表倒数第k个节点"><a href="#3-求链表倒数第k个节点" class="headerlink" title="3. 求链表倒数第k个节点"></a>3. 求链表倒数第k个节点</h4><p><strong>题目描述:</strong>输入一个单项链表,输出该链表中倒数第k个节点,链表倒数第0个节点为链表的尾节点。</p>
<p><strong>分析:</strong>设置两个指针<code>p1</code> 、 <code>p2</code> ,首先<code>p1</code> 和 <code>p2</code> 都指向head,然后<code>p2</code> 向前走k步,这样<code>p1</code> 和 <code>p2</code> 之间就间隔了k个节点,然后<code>p1</code> 和 <code>p2</code> 同时向前移动,直至p2走到链表末尾。</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 倒数第k个节点</span></span><br><span class="line"><span class="function">Node * <span class="title">theKthNode</span><span class="params">(Node *head,<span class="keyword">int</span> k)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(k<<span class="number">0</span>) <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"> Node *snow,*fast;</span><br><span class="line"> slow = fast = head;</span><br><span class="line"> <span class="keyword">for</span>(i=k;i><span class="number">0</span> && fast != <span class="literal">NULL</span>;i--){</span><br><span class="line"> fast = fast-next;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 考虑链表长度不足k的情况</span></span><br><span class="line"> <span class="keyword">if</span>(i><span class="number">0</span>) <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">while</span>(fast != <span class="literal">NULL</span>) {</span><br><span class="line"> slow = slow -> next;</span><br><span class="line"> fast = fast -> next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> slow;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="4-求链表的中间节点"><a href="#4-求链表的中间节点" class="headerlink" title="4. 求链表的中间节点"></a>4. 求链表的中间节点</h4><p><strong>题目描述:</strong>求链表的中间节点,如果链表的长度为偶数,返回中间两个节点的任意一个,若为奇数,则返回中间节点</p>
<p><strong>分析:</strong>首先想到的是,先计算出链表的长度,然后计算出中间节点所在链表顺序的位置。但是还有一种更搞笑的思路,只需要扫描一遍链表。通过两个指针<code>p1</code> 和 <code>p2</code> ,同时从链表头节点开始,<code>p1</code> 每次移动两步, <code>p2</code> 每次移动一步,当<code>p1</code> 移动到链表尾节点的时候,<code>p2</code>所在的位置就是链表的中间节点</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 求链表的中间节点</span></span><br><span class="line"><span class="function">Node *<span class="title">theMiddleNode</span><span class="params">(Node *head)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(head == <span class="literal">NULL</span>) <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"> </span><br><span class="line"> Node *slow,*fast;</span><br><span class="line"> slow = fast = head;</span><br><span class="line"> <span class="comment">//如果要求在链表长度为偶数的情况下,返回中间两个节点的第一个,可以用下面的循环条件</span></span><br><span class="line"> <span class="comment">// while(fast && fast-next != NULL && fast->next-next != NULL)</span></span><br><span class="line"> <span class="keyword">while</span>(fast != <span class="literal">NULL</span> && fast-next != <span class="literal">NULL</span>) {</span><br><span class="line"> slow = slow->next;</span><br><span class="line"> fast = fast-next->next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> slow;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="5-判断单链表是否存在环"><a href="#5-判断单链表是否存在环" class="headerlink" title="5. 判断单链表是否存在环"></a>5. 判断单链表是否存在环</h4><p><strong>题目描述:</strong>输入一个单向链表,判断该链表是否存在环</p>
<p><strong>分析:</strong>也是通过两个指针,分别从链表的头节点开始出发,一个每次向后移动一步,另一个移动两步,两个指针的移动速度不一样,如果存在环,那么两个指针一定会在环里相遇。</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 判断单链表是否存在环,参数circleNode是环内节点</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">hasCircle</span><span class="params">(Node *head,Node *&circleNode)</span> </span>{</span><br><span class="line"> Node *slow,*fast;</span><br><span class="line"> slow = fast = head;</span><br><span class="line"> <span class="keyword">while</span>(fast != <span class="literal">NULL</span> && fast->next != <span class="literal">NULL</span>) {</span><br><span class="line"> fast = fast->next->next;</span><br><span class="line"> slow = slow->next;</span><br><span class="line"> <span class="keyword">if</span>(fast == slow) {</span><br><span class="line"> circleNode = fast;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="6-找到环的入口点"><a href="#6-找到环的入口点" class="headerlink" title="6. 找到环的入口点"></a>6. 找到环的入口点</h4><p><strong>题目描述:</strong>输入一个单向链表,判断链表是否有环。如果链表存在环,如何找到环的入口点</p>
<p><strong>分析:</strong>由上题可知,按照 <code>p2</code> 每次两步,<code>p1</code> 每次一步的方式走,发现 <code>p2</code> 和 <code>p1</code> 重合,确定了单向链表有环路了。接下来,让<code>p2</code>回到链表的头部,重新走,每次步长不是走2了,而是走1,那么当 p1 和 p2 再次相遇的时候,就是环路的入口了。</p>
<p><strong>为什么?:</strong>假定起点到环入口点的距离为 a,<code>p1</code> 和 <code>p2</code> 的相交点M与环入口点的距离为b,环路的周长为L,当 <code>p1</code> 和 <code>p2</code> 第一次相遇的时候,假定 <code>p1</code> 走了 n 步。那么有:</p>
<p><code>p1</code>走的路径: <code>a+b = n</code>;</p>
<p><code>p2</code>走的路径: <code>a+b+k*L = 2*n</code>; <code>p2</code> 比 <code>p1</code> 多走了k圈环路,总路程是<code>p1</code>的2倍</p>
<p>根据上述公式可以得到 <code>k*L=a+b=n</code>。显然,如果从相遇点M开始,<code>p1</code> 再走 n 步的话,还可以再回到相遇点,同时<code>p2</code>从头开始走的话,经过n步,也会达到相遇点M。</p>
<p>显然在这个步骤当中 <code>p1</code> 和 <code>p2</code> 只有前 a 步走的路径不同,所以当 <code>p1</code> 和 <code>p2</code> 再次重合的时候,必然是在链表的环路入口点上。</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">//找到环的入口点</span></span><br><span class="line"><span class="function">Node* <span class="title">findLoopPort</span><span class="params">(Node *head)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">//如果head为空,或者为单结点,则不存在环</span></span><br><span class="line"> <span class="keyword">if</span>(head == <span class="literal">NULL</span> || head->next == <span class="literal">NULL</span>) <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line"> Node *slow,*fast;</span><br><span class="line"> slow = fast = head;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//先判断是否存在环</span></span><br><span class="line"> <span class="keyword">while</span>(fast != <span class="literal">NULL</span> && fast->next != <span class="literal">NULL</span>) {</span><br><span class="line"> fast = fast->next->next;</span><br><span class="line"> slow = slow->next;</span><br><span class="line"> <span class="keyword">if</span>(fast == slow)</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="comment">//不存在环</span></span><br><span class="line"> <span class="keyword">if</span>(fast != slow) <span class="keyword">return</span> <span class="literal">NULL</span>; </span><br><span class="line"></span><br><span class="line"> <span class="comment">//快指针从头开始走,步长变为1</span></span><br><span class="line"> fast = head; </span><br><span class="line"> </span><br><span class="line"> <span class="comment">//两者相遇即为入口点</span></span><br><span class="line"> <span class="keyword">while</span>(fast != slow) {</span><br><span class="line"> fast = fast->next;</span><br><span class="line"> slow = slow->next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> fast;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="7-判断两个链表是否相交"><a href="#7-判断两个链表是否相交" class="headerlink" title="7. 判断两个链表是否相交"></a>7. 判断两个链表是否相交</h4><p><strong>题目描述:</strong>给出两个单向链表的头指针,比如h1、h2,判断这两个链表是否相交。这里为了简化问题,我们假设两个链表均不带环。</p>
<p><strong>分析:</strong></p>
<ol>
<li><p>直接循环判断第一个链表的每个节点是否存在第二个链表中。很显然 ,不够效率。</p>
</li>
<li><p>针对第一个链表直接构造hash表,然后查询hash表,判断第二个链表的每个节点是否在hash表出现,如果所有的第二个链表的节点都能在hash表中找到,即说明第二个链表与第一个链表有相同的节点。时间复杂度为为线性:O(Length(h1) + Length(h2)),同时为了存储第一个链表的所有节点,空间复杂度为O(Length(h1))。是否还有更好的方法呢,既能够以线性时间复杂度解决问题,又能减少存储空间?</p>
</li>
<li><p>转换为环的问题。把第二个链表接在第一个链表后面,如果得到的链表有环,则说明两个链表相交。如何判断有环的问题上面已经讨论过了,但这里有更简单的方法。因为如果有环,则第二个链表的表头一定也在环上,即第二个链表会构成一个循环链表,我们只需要遍历第二个链表,看是否会回到起始点就可以判断出来。这个方法的时间复杂度是线性的,空间是常熟。</p>
</li>
<li><p>进一步考虑“如果两个没有环的链表相交于某一节点,那么在这个节点之后的所有节点都是两个链表共有的”这个特点,我们可以知道,如果它们相交,则最后一个节点一定是共有的。而我们很容易能得到链表的最后一个节点,所以这成了我们简化解法的一个主要突破口。那么,我们只要判断两个链表的尾指针是否相等。相等,则链表相交;否则,链表不相交。<br>所以,先遍历第一个链表,记住最后一个节点。然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,则相交,否则,不相交。这样我们就得到了一个时间复杂度,它为O((Length(h1) + Length(h2)),而且只用了一个额外的指针来存储最后一个节点。这个方法时间复杂度为线性O(N),空间复杂度为O(1),显然比解法三更胜一筹。</p>
</li>
</ol>
<pre><code>解法四的代码如下:
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">//判断两个链表是否相交</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">isIntersect</span><span class="params">(Node *h1,Node *h2)</span> </span>{</span><br><span class="line"> <span class="comment">//异常判断</span></span><br><span class="line"> <span class="keyword">if</span>(h1 == <span class="literal">NULL</span> || h2 == <span class="literal">NULL</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">while</span>(h1->next != <span class="literal">NULL</span>) {</span><br><span class="line"> h1 = h1->next;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span>(h2->next != <span class="literal">NULL</span>) {</span><br><span class="line"> h2 = h2->next;</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 class="keyword">if</span>(h1 == h2) <span class="keyword">return</span> <span class="literal">true</span>; </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></code></pre><h4 id="8-链表🈶环,如何判断相交"><a href="#8-链表🈶环,如何判断相交" class="headerlink" title="8. 链表🈶环,如何判断相交"></a>8. 链表🈶环,如何判断相交</h4><p><strong>题目描述:</strong>上面的问题都是针对链表无环的,那么如果现在,链表是有环的呢?上面的方法还同样有效么?</p>
<p><strong>分析:</strong>如果有环且两个链表相交,则两个链表都有共同一个环,即环上的任意一个节点都存在于两个链表上。因此,就可以判断一链表上俩指针相遇的那个节点,在不在另一条链表上。</p>
<figure class="highlight c"><table><tr><td class="code"><pre><span class="line"><span class="comment">//判断两个带环链表是否相交</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">isIntersectWithLoop</span><span class="params">(Node *h1,Node *h2)</span> </span>{</span><br><span class="line"> Node *circleNode1,*circleNode2;</span><br><span class="line"> <span class="comment">//判断链表带不带环,并保存环内节点</span></span><br><span class="line"> <span class="keyword">if</span>(!hasCircle(h1,circleNode1)) </span><br><span class="line"> <span class="comment">//不带环,异常退出</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line"> <span class="keyword">if</span>(!hasCircle(h2,circleNode2))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"> Node *temp = circleNode2->next;</span><br><span class="line"> <span class="keyword">while</span>(temp != circleNode2) {</span><br><span class="line"> <span class="keyword">if</span>(temp == circleNode1)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> temp = temp->next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="9-两链表相交的第一个公共节点"><a href="#9-两链表相交的第一个公共节点" class="headerlink" title="9. 两链表相交的第一个公共节点"></a>9. 两链表相交的第一个公共节点</h4><p><strong>题目描述:</strong>如果两个无环单链表相交,怎么求出他们相交的第一个节点呢?</p>
<p><strong>分析:</strong>采用对齐的思想。计算两个链表的长度 L1 , L2,分别用两个指针 <code>p1</code> , <code>p2</code> 指向两个链表的头,然后将较长链表的 <code>p1</code>(假设为 <code>p1</code>)向后移动<code>L2 - L1</code>个节点,然后再同时向后移动<code>p1</code> , <code>p2</code>,直到 <code>p1 = p2</code>。相遇的点就是相交的第一个节点。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">//求两链表相交的第一个公共节点</span><br><span class="line">Node* findIntersectNode(Node *h1,Node *h2) {</span><br><span class="line"> //求链表长度</span><br><span class="line"> int len1 = listLength(h1); </span><br><span class="line"> int len2 = listLength(h2);</span><br><span class="line"> //对齐两个链表</span><br><span class="line"> if(len1 > len2) {</span><br><span class="line"> for(int i=0;i<len1-len2;i++)</span><br><span class="line"> h1=h1->next;</span><br><span class="line"> } else {</span><br><span class="line"> for(int i=0;i<len2-len1;i++)</span><br><span class="line"> h2=h2->next;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> while(h1 != NULL) {</span><br><span class="line"> if(h1 == h2)</span><br><span class="line"> return h1;</span><br><span class="line"> h1 = h1->next;</span><br><span class="line"> h2 = h2->next; </span><br><span class="line"> }</span><br><span class="line"> return NULL;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
]]></content>
<tags>
<tag>code</tag>
</tags>
</entry>
<entry>
<title>InjectionIII</title>
<url>/2020/03/25/Injectionlll/</url>
<content><![CDATA[<h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>随着项目越来越大,每次build的时间也是在变长。当只是修改一个UI上的小问题时,为了看到效果,需要再次build等待很长的时间,无疑是件很糟糕的事情。尤其对比flutter的热重载,这样的开发体验尤其难受。</p>
<p><strong>InjectionIII</strong> 的出现,完美解决了这样的困境。修改完之后,只需要保存,即可看到修改完的效果。</p>
<p>有一点很纳闷,这么好用的工具,为什么之前不但是我,身边的朋友也从来没有提及过,怪哉怪哉!</p>
<p><img src="https://s1.ax1x.com/2020/08/25/d6tSfK.gif" alt="效果图"></p>
<blockquote>
<p>只支持模拟器!只支持模拟器!只支持模拟器!</p>
</blockquote>
<h4 id="一、下载"><a href="#一、下载" class="headerlink" title="一、下载"></a>一、下载</h4><p>在 <code>App Store</code>搜索 <strong>InjectionIII</strong> 并下载</p>
<p><img src="https://s1.ax1x.com/2020/08/25/d6YHlF.png" alt="Injection下载"></p>
<h4 id="二、打开工程"><a href="#二、打开工程" class="headerlink" title="二、打开工程"></a>二、打开工程</h4><p>下载完成后打开此软件,Mac状态点击 <strong>InjectionIII</strong> -> <strong>Open Project</strong> ,选择你的工程文件夹然后确定 <strong>Select Project Directory</strong> 。操作完成后再用Xcode打开工程。</p>
<p><img src="https://s1.ax1x.com/2020/08/25/d6YoWT.png" alt="工程选择"></p>
<p><img src="https://s1.ax1x.com/2020/08/25/d6Y7SU.png" alt="具体选择"></p>
<h4 id="三、配置"><a href="#三、配置" class="headerlink" title="三、配置"></a>三、配置</h4><p>Xcode 打开工程后,在 <code>AppDelegate</code> 的 <code>didFinishLaunchingWithOptions</code> 方法内注入 <strong>InjectionIII</strong></p>
<p>Xcode版本大于等于10.2 如下注入:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">#if DEBUG</span><br><span class="line">Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/iOSInjection.bundle")?.load()</span><br><span class="line">//for tvOS:</span><br><span class="line">Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/tvOSInjection.bundle")?.load()</span><br><span class="line">//Or for macOS:</span><br><span class="line">Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/macOSInjection.bundle")?.load()</span><br><span class="line">#endif</span><br></pre></td></tr></table></figure>
<p>Xcode版本10.1的如下注入:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">#if DEBUG</span><br><span class="line">Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/iOSInjection10.bundle")?.load()</span><br><span class="line">//for tvOS:</span><br><span class="line">Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/tvOSInjection10.bundle")?.load()</span><br><span class="line">//Or for macOS:</span><br><span class="line">Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/macOSInjection10.bundle")?.load()</span><br><span class="line">#endif</span><br></pre></td></tr></table></figure>
<blockquote>
<p>注意,文件路径不能有错,如果你有什么骚操作下载的软件被你移动到其它文件夹,就要用你自己的路径。</p>
</blockquote>
<h4 id="四、看Xcode日志"><a href="#四、看Xcode日志" class="headerlink" title="四、看Xcode日志"></a>四、看Xcode日志</h4><p>配置完成后年,<strong>run</strong>你的项目,注意查看Xcode日志。如果有如下输出,则成功注入了。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">? Injection connected, watching /Users/zhouqiang/Desktop/Project/AnXinCollege/**</span><br></pre></td></tr></table></figure>
<p>比如我修改了 <code>SettingViewController</code> 文件,并保存(<strong>command + S</strong>),日志输出如下,代表成功</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">? *** Compiling /Users/zhouqiang/Desktop/Project/AnXinCollege/AnXinCollege/Modules/Mine/Setting/SettingViewController.swift ***</span><br><span class="line">? Loading .dylib ...</span><br><span class="line">objc[10121]: Class _TtC12AnXinCollege21SettingViewController is implemented in both /Users/zhouqiang/Library/Developer/CoreSimulator/Devices/A12F878C-ACF9-4D62-A82E-4D260EE7E08A/data/Containers/Bundle/Application/ABBB9C03-F6EE-4354-816E-9537D729FC8F/AnXinCollege.app/AnXinCollege (0x10709ed28) and /Users/zhouqiang/Library/Containers/com.johnholdsworth.InjectionIII/Data/eval101.dylib (0x1266d5da0). One of the two will be used. Which one is undefined.</span><br><span class="line">? Loaded .dylib - Ignore any duplicate class warning ^</span><br><span class="line">? Injected 'SettingViewController'</span><br></pre></td></tr></table></figure>
<h4 id="Tips"><a href="#Tips" class="headerlink" title="Tips"></a>Tips</h4><p>一定要保存,只需要保存,快捷键<strong>Command + S</strong> ,看到如上日志输出即可</p>
<p>要想看到修改后的效果,<strong>一定要确保操作执行到你修改的代码</strong>。比如cell的改动,保存后,你需要滑动cell触发你修改的代码。比如页面UI的修改,修改完如果没有其它按钮能触发到,则需要返回上一页再进来,<strong>以此确保执行到你修改的代码</strong></p>
<p><strong>不仅仅看UI上的变化,逻辑上的更改也是可以的</strong></p>
<p><a href="https://github.com/johnno1962/InjectionIII" target="_blank" rel="noopener">参考链接</a></p>
]]></content>
<tags>
<tag>Xcode</tag>
</tags>
</entry>
</search>