Jekyll2021-12-14T11:38:46+00:00https://dcell.github.io/feed.xmlDcell BlogiOS/android/webapp developer. Dcell767536477@qq.com手机控制软件[wormhole] 破解2021-06-28T00:00:00+00:002021-06-28T00:00:00+00:00https://dcell.github.io/2021/06/28/%E6%89%8B%E6%9C%BA%E6%8E%A7%E5%88%B6%E8%BD%AF%E4%BB%B6%5Bwormhole%5D%E7%A0%B4%E8%A7%A3<h2 id="前言">前言</h2> <p>平时我们开发过程中,难免会要进行真机的调试。有没有一款应用能让真机和模拟器一样方便使用呢? 我在搜索了一番后,发现一款应用叫 <em>Wormhole</em> ,免费使用3天,后续升级Pro 45元。</p> <h2 id="破解">破解</h2> <h3 id="方式1">方式1</h3> <p>本想通过抓包分析下应用协议,然后进行拦截替换,但是发现协议包都是加密的,而且是双向证书认证,无法进行中间者模式攻击。该方式在尝试后放弃。</p> <h3 id="方式2">方式2</h3> <p>直接进行硬核破解,修改汇编指令集,替换可执行2进制文件</p> <h4 id="第一步">第一步</h4> <p>安装相关程序包 <a href="https://er.run/">Wormhole</a> ,将程序包安装到 Applications</p> <h4 id="第二步">第二步</h4> <p>将 /Applications/Wormhole.app/Contents/MacOS/Wormhole 二进制文件拖到 Hopper</p> <p><img src="https://i.loli.net/2021/08/18/nFEDcZs8w4NS9Kl.png" alt="截屏2021-08-18 下午2.04.02" /></p> <p>选择 Debug -&gt; Select Debugger</p> <p><img src="https://i.loli.net/2021/08/18/aH9szTu6qSro4i7.png" alt="截屏2021-08-18 下午2.08.43" /></p> <p>启动应用后,我们可以通过LLDB 或者 Hopper工具 调试挂载进去。接下来我们分析汇编的一些代码逻辑,我们省略中间过程直接讲结果。</p> <p><img src="https://i.loli.net/2021/08/18/9hmOrnapXdzfAei.png" alt="截屏2021-08-18 下午2.16.20" /></p> <p>搜索关键字 <em>active</em> , 双击打开 CheckActiveResponse函数</p> <p><img src="https://i.loli.net/2021/08/18/nXtNHiwVrqhOekR.png" alt="截屏2021-08-18 下午2.18.21" /></p> <div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="o">*</span><span class="p">(</span><span class="kt">int32_t</span> <span class="o">*</span><span class="p">)(</span><span class="n">rsi</span> <span class="o">+</span> <span class="mh">0x10</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0xc8</span><span class="p">)</span> <span class="c1">//判断返回Code是否是200</span> </code></pre></div></div> <div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">if</span> <span class="p">(</span><span class="n">ChoosePlatformWidget</span><span class="o">::</span><span class="n">UpdateProState</span><span class="p">(</span><span class="n">rdi</span><span class="p">)</span> <span class="o">==</span> <span class="mh">0x0</span><span class="p">)</span> <span class="c1">//判断该用户状态是否 激活状态</span> </code></pre></div></div> <p>所以我们只需要修改下面这个判断,让他永久成立即可。</p> <p><img src="https://i.loli.net/2021/08/18/gWYt9M2xpQDLn4a.png" alt="截屏2021-08-18 下午2.24.02" /></p> <p>找到对应的汇编代码,修改 je =&gt; jne ,然后重新生成二进制文件,替换原先的文件。</p>Dcell767536477@qq.com前言Dark mode on the web2021-03-22T00:00:00+00:002021-03-22T00:00:00+00:00https://dcell.github.io/2021/03/22/Dark%20mode%20on%20the%20web<p>随着App混合开发的流行,一些复杂App往往会加载一些H5相关的网页;自从iOS 13加入暗黑模式以后,android的一些厂商也加入了暗黑模式。如果在暗黑模式中加载了一个正常的H5样式,那么对视觉的冲击是比较大的,所以我们需要H5 Web能随着系统的模式自动切换。</p> <h2 id="toggling-themes">Toggling Themes</h2> <h4 id="using-a-body-class">Using a Body Class</h4> <p>我们通过css样式切换,可以简单的切换一个主题。</p> <iframe height="265" style="width: 100%;" scrolling="no" title="Method 1 - Class Swapping" src="https://codepen.io/adhuham/embed/dyodgPj?height=265&amp;theme-id=light&amp;default-tab=html,result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href="https://codepen.io/adhuham/pen/dyodgPj">Method 1 - Class Swapping</a> by Mohamed Adhuham (<a href="https://codepen.io/adhuham">@adhuham</a>) on <a href="https://codepen.io">CodePen</a>. </iframe> <h4 id="using-separate-stylesheets">Using Separate Stylesheets</h4> <p>一个优秀的前端开发工程师,在做项目开发的时候,会做一套样式管理,合理利用CSS 样式继承。我们假如你的工程已经有了一套样式 ‘Light Theme’,那么我们开发Dark Theme,只需要按照现有的样式开发一套对立的样式。</p> <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span> <span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">&gt;</span> <span class="nt">&lt;head&gt;</span> <span class="c">&lt;!-- Light theme stylesheet --&gt;</span> <span class="nt">&lt;link</span> <span class="na">href=</span><span class="s">"light-theme.css"</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">id=</span><span class="s">"theme-link"</span><span class="nt">&gt;</span> <span class="nt">&lt;/head&gt;</span> <span class="c">&lt;!-- etc. --&gt;</span> <span class="nt">&lt;/html&gt;</span> </code></pre></div></div> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Select the button</span> <span class="kd">const</span> <span class="nx">btn</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">.btn-toggle</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// Select the stylesheet &lt;link&gt;</span> <span class="kd">const</span> <span class="nx">theme</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">"</span><span class="s2">#theme-link</span><span class="dl">"</span><span class="p">);</span> <span class="c1">// Listen for a click on the button</span> <span class="nx">btn</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">"</span><span class="s2">click</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// If the current URL contains "ligh-theme.css"</span> <span class="k">if</span> <span class="p">(</span><span class="nx">theme</span><span class="p">.</span><span class="nx">getAttribute</span><span class="p">(</span><span class="dl">"</span><span class="s2">href</span><span class="dl">"</span><span class="p">)</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">light-theme.css</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// ... then switch it to "dark-theme.css"</span> <span class="nx">theme</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">dark-theme.css</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// Otherwise...</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c1">// ... switch it to "light-theme.css"</span> <span class="nx">theme</span><span class="p">.</span><span class="nx">href</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">light-theme.css</span><span class="dl">"</span><span class="p">;</span> <span class="p">}</span> <span class="p">});</span> </code></pre></div></div> <p><a href="https://codepen.io/adhuham/project/editor/AqjdGV">Demo</a></p> <h4 id="using-custom-properties">Using Custom Properties</h4> <p>我们也可以通过自定义属性来做样式的切换,如果你是多人协作开发,我认为这是个不错的方案。</p> <iframe height="265" style="width: 100%;" scrolling="no" title="KKaPMWM" src="https://codepen.io/dcell/embed/KKaPMWM?height=265&amp;theme-id=light&amp;default-tab=html,result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href="https://codepen.io/dcell/pen/KKaPMWM">KKaPMWM</a> by Dcell (<a href="https://codepen.io/dcell">@dcell</a>) on <a href="https://codepen.io">CodePen</a>. </iframe> <h2 id="dark-mode-at-the-operating-system-level">Dark Mode at the Operating System Level</h2> <p>上面我们讲了些,如何通过手动的方式来切换模式(当然你可以通过URL?mode=dark),那么有没有自动跟随系统的方式呢?</p> <p>CSS提供了一个参数:<strong><code class="language-plaintext highlighter-rouge">prefers-color-scheme</code></strong> <a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/Media_Queries/Using_media_queries#media_features">媒体特性</a>用于检测用户是否有将系统的主题色设置为亮色或者暗色。</p> <div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@media</span> <span class="p">(</span><span class="n">prefers-color-scheme</span><span class="p">:</span> <span class="n">dark</span><span class="p">)</span> <span class="p">{</span> <span class="c">/* Dark theme styles go here */</span> <span class="p">}</span> <span class="k">@media</span> <span class="p">(</span><span class="n">prefers-color-scheme</span><span class="p">:</span> <span class="n">light</span><span class="p">)</span> <span class="p">{</span> <span class="c">/* Light theme styles go here */</span> <span class="p">}</span> </code></pre></div></div> <iframe height="265" style="width: 100%;" scrolling="no" title="Prefers Color Scheme: Demo Use Case" src="https://codepen.io/team/css-tricks/embed/mdVrQXV?height=265&amp;theme-id=light&amp;default-tab=html,result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"> See the Pen <a href="https://codepen.io/team/css-tricks/pen/mdVrQXV">Prefers Color Scheme: Demo Use Case</a> by CSS-Tricks (<a href="https://codepen.io/css-tricks">@css-tricks</a>) on <a href="https://codepen.io">CodePen</a>. </iframe> <p>我们也可以检测当前浏览器是否有暗黑模式</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">prefersDarkScheme</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">matchMedia</span><span class="p">(</span><span class="dl">"</span><span class="s2">(prefers-color-scheme: dark)</span><span class="dl">"</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="nx">prefersDarkScheme</span><span class="p">.</span><span class="nx">matches</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//add dark theme</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c1">//remove dark theme</span> <span class="p">}</span> </code></pre></div></div> <p>这样一看真是非常的方便,但是我们忽略了一点,这个特性只支持 Chrome 76+, Firefox 67+, Chrome Android 76+, Safari 12.5+ (13+ on iOS), and Samsung Internet Browser.</p> <h2 id="总结">总结</h2> <p>现阶段来说,如果要实现暗黑模式还是需要手动管理CSS样式,当然在高级版本可以自动做些切换,做一些体验上的优化。</p>Dcell767536477@qq.com随着App混合开发的流行,一些复杂App往往会加载一些H5相关的网页;自从iOS 13加入暗黑模式以后,android的一些厂商也加入了暗黑模式。如果在暗黑模式中加载了一个正常的H5样式,那么对视觉的冲击是比较大的,所以我们需要H5 Web能随着系统的模式自动切换。objc4 流言终结者2021-03-08T00:00:00+00:002021-03-08T00:00:00+00:00https://dcell.github.io/2021/03/08/objc4%20%E6%B5%81%E8%A8%80%E7%BB%88%E7%BB%93%E8%80%85<blockquote> <p>源码都是基于objc-782,818版本,Swift5 ;老版本可能有不同的地方</p> </blockquote> <h2 id="nsobject-alloc-init-和-nsobject-new-的区别">[[NSObject alloc] init] 和 [NSObject new] 的区别</h2> <p>我采用3种方式来验证</p> <pre><code class="language-objective-c">id a = [ClassA alloc]; id aa = [[ClassA alloc] init]; id aaa = [ClassA new]; </code></pre> <p>第一个,执行的堆栈,发现执行了2次 <strong>callAlloc</strong>,最终执行到<strong>_objc_rootAllocWithZone</strong></p> <pre><code class="language-objective-c">id a = [ClassA alloc]; #0 _objc_rootAllocWithZone #1 callAlloc(objc_class*, bool, bool) #2 _objc_rootAlloc #3 +[NSObject alloc] #4 callAlloc(objc_class*, bool, bool) #5 objc_alloc </code></pre> <p>第二个,执行堆栈,这里就有点奇怪了,感觉编译器做了优化,如果 alloc 后面紧跟 init,那么直接就执行<strong>objc_alloc_init</strong></p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#0 _objc_rootAllocWithZone #1 callAlloc(objc_class*, bool, bool) #2 objc_alloc_init </code></pre></div></div> <p>第三个,执行堆栈,除了入口函数不一样,其他一模一样。</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#0 _objc_rootAllocWithZone #1 callAlloc(objc_class*, bool, bool) #2 objc_opt_new </code></pre></div></div> <p>总结:</p> <ul> <li>[alloc] init ] 和 new ,执行的结果是一样的 , 面试的时候可以理直气壮的说两者是“一模一样的”。</li> <li>id a = [ClassA alloc]; [a init]; 和 id aa = [[ClassA alloc] init]; 结果是一样的,但是堆栈有较大不同</li> </ul> <h2 id="nsobject-alloc-init-中-init-是做初始化的工作么">[[NSObject alloc] init] 中 init 是做初始化的工作么?</h2> <pre><code class="language-objective-c">id _objc_rootInit(id obj) { // In practice, it will be hard to rely on this function. // Many classes do not properly chain -init calls. return obj; } </code></pre> <p>总结:init 没有做任何事情</p> <h2 id="弱引用指针存储是用什么数据结构">弱引用指针存储是用什么数据结构?</h2> <p>摘取了部分源码:</p> <div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">weak_entry_t</span> <span class="o">*</span> <span class="nf">weak_entry_for_referent</span><span class="p">(</span><span class="n">weak_table_t</span> <span class="o">*</span><span class="n">weak_table</span><span class="p">,</span> <span class="n">objc_object</span> <span class="o">*</span><span class="n">referent</span><span class="p">)</span> <span class="p">{</span> <span class="n">ASSERT</span><span class="p">(</span><span class="n">referent</span><span class="p">);</span> <span class="n">weak_entry_t</span> <span class="o">*</span><span class="n">weak_entries</span> <span class="o">=</span> <span class="n">weak_table</span><span class="o">-&gt;</span><span class="n">weak_entries</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">weak_entries</span><span class="p">)</span> <span class="k">return</span> <span class="n">nil</span><span class="p">;</span> <span class="kt">size_t</span> <span class="n">begin</span> <span class="o">=</span> <span class="n">hash_pointer</span><span class="p">(</span><span class="n">referent</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">weak_table</span><span class="o">-&gt;</span><span class="n">mask</span><span class="p">;</span> <span class="kt">size_t</span> <span class="n">index</span> <span class="o">=</span> <span class="n">begin</span><span class="p">;</span> <span class="kt">size_t</span> <span class="n">hash_displacement</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="k">while</span> <span class="p">(</span><span class="n">weak_table</span><span class="o">-&gt;</span><span class="n">weak_entries</span><span class="p">[</span><span class="n">index</span><span class="p">].</span><span class="n">referent</span> <span class="o">!=</span> <span class="n">referent</span><span class="p">)</span> <span class="p">{</span> <span class="n">index</span> <span class="o">=</span> <span class="p">(</span><span class="n">index</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span> <span class="o">&amp;</span> <span class="n">weak_table</span><span class="o">-&gt;</span><span class="n">mask</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">index</span> <span class="o">==</span> <span class="n">begin</span><span class="p">)</span> <span class="n">bad_weak_table</span><span class="p">(</span><span class="n">weak_table</span><span class="o">-&gt;</span><span class="n">weak_entries</span><span class="p">);</span> <span class="n">hash_displacement</span><span class="o">++</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="n">hash_displacement</span> <span class="o">&gt;</span> <span class="n">weak_table</span><span class="o">-&gt;</span><span class="n">max_hash_displacement</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">nil</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="o">&amp;</span><span class="n">weak_table</span><span class="o">-&gt;</span><span class="n">weak_entries</span><span class="p">[</span><span class="n">index</span><span class="p">];</span> <span class="p">}</span> </code></pre></div></div> <p>答案:存储的方式是数组,但是用了Hash算法-开放定位法,所以存储的是Hash表</p> <h2 id="分类重写主类的方法会把函数替换掉么">分类重写主类的方法,会把函数替换掉么?</h2> <p>答:不会,函数替换只是表面现象;主要原因是函数寻找过程,优先寻找到分类的方法,主函数的方法其实还是存在的。</p> <h2 id="classa-load-中执行-classb-alloc在classb-load-执行-classa-alloc-会怎么样">ClassA +load 中执行 [ClassB alloc],在ClassB +load 执行 [ClassA alloc] 会怎么样</h2> <div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Call +load methods (without runtimeLock - re-entrant)</span> <span class="n">call_load_methods</span><span class="p">();</span> </code></pre></div></div> <p>答:不会存在任何问题,1:load方法执行的时候,类的相关信息都已经准备完毕 2:执行load方法,没有添加 runtime lock,所以不会出现死锁现象</p> <h2 id="uiview设置frame是直接设置calayer的frame么为什么calayer有隐式动画而uiview没有">UIView设置Frame,是直接设置CALayer的Frame么,为什么CALayer有隐式动画而UIView没有</h2> <p>答:大部分设置UIView的属性,都会设置到CALayer上,而没有出现动画是因为CAAction,只有在UIView.animation block里面,uiview作为calayer的代理才会返回CAAction , 参考官方文档</p> <h2 id="nsnumber-number1--1-和-nsnumber-number2--0xefffffffffffffff-有什么区别">NSNumber *number1 = @(1); 和 NSNumber *number2 = @(0xEFFFFFFFFFFFFFFF); 有什么区别</h2> <p>总结:TaggedPointer的区别,理论上TaggedPointer可以存储7个字节+ 4bit = 60bit的大小数据</p> <h2 id="arc下-什么情况下会执行对象的autorelease方法">ARC下 什么情况下会执行对象的autoRelease方法</h2> <p>//todo</p> <h2 id="swift中函数调用用什么方式">Swift中函数调用用什么方式</h2> <p>答:静态派发和动态派发,动态派发又分 虚拟表派发 和 消息发送(@objc)</p> <h2 id="swift中结构体或者基础数据中函数是调用是什么方式">Swift中结构体或者基础数据中,函数是调用是什么方式</h2> <p>答:静态派发</p> <h2 id="swift中扩展的函数是什么调用方式">Swift中扩展的函数,是什么调用方式</h2> <p>答:静态派发</p> <h2 id="swift中结构体中使用block会出现循环引用的情况么">Swift中结构体中使用Block,会出现循环引用的情况么?</h2> <p>//todo</p> <h2 id="swift中数组范型可以用协议么为什么">Swift中数组范型可以用协议么?为什么</h2> <p>答:可以,如果数组范型是协议,那么数组存储的是一个特殊的数据结构,witnessTable,具体参考文档</p>Dcell767536477@qq.com源码都是基于objc-782,818版本,Swift5 ;老版本可能有不同的地方iOS模拟B站智能防挡弹幕效果2021-01-30T00:00:00+00:002021-01-30T00:00:00+00:00https://dcell.github.io/2021/01/30/iOS%E6%A8%A1%E6%8B%9FB%E7%AB%99%E6%99%BA%E8%83%BD%E9%98%B2%E6%8C%A1%E5%BC%B9%E5%B9%95%E6%95%88%E6%9E%9C<p>最近晚上一直在学习算法和数据结构,人菜瘾还大,一直被虐;特别是一些常用的算法写的想哭。</p> <p>刷累了就看看B站视频,发现B站有个防人物被弹幕遮挡的功能,感觉挺有意思的。</p> <h2 id="原理">原理</h2> <p>看到这个效果,八九不离十猜到是 <strong>mask</strong> 作用。前端正常播放一个视频,然后在视频上发加一个 空心的 mask,这样弹幕就无法在空心的地方显示,就会有一个类似钻过去的效果。</p> <p>那么就是这个mask是怎么计算出来的呢?看了下前端的网络,B站应该通过一些算法,将视频中人物的描边在服务端计算好,然后前端不断请求修改这个 mask 的 path。</p> <p>优点:</p> <ul> <li>前后端通力配合,降低了前端开发难度</li> </ul> <p>缺点:</p> <ul> <li>如果请求这个 mask,网络异常了或者延迟,那么可能会出现反面效果</li> </ul> <p>思考下:能不能把 mask信息藏到视频帧的扩展信息中呢?🤔</p> <h2 id="在ios中模拟下效果">在iOS中模拟下效果</h2> <p>我在iOS使用系统的人脸追踪CIFaceFeature和CALayer,模拟了下B站的效果。只能防止脸被遮住… 而不是像B站一样人体描边。</p> <p>具体的实现不细写了,参考<a href="https://github.com/Dcell/bilibili-mask">demo</a></p> <p><img src="https://github.com/Dcell/bilibili-mask/blob/main/Jan-30-2021%2016-09-41.gif" alt="" /></p>Dcell767536477@qq.com最近晚上一直在学习算法和数据结构,人菜瘾还大,一直被虐;特别是一些常用的算法写的想哭。UIView 和 CALayer 的协作2021-01-11T00:00:00+00:002021-01-11T00:00:00+00:00https://dcell.github.io/2021/01/11/UIView-%E5%92%8C-CALayer-%E7%9A%84%E5%8D%8F%E4%BD%9C<p>在几年前,我浏览过由猫神翻译的一篇Objc中国的文章。讲的是UIView和CALayer的协作关系,讲的非常好;但是一段时间过去后,发现自己慢慢的又遗忘了,所以打算自己写一下,并且跑一些Demo来加深自己的理解。</p> <h2 id="uiview-和-calayer-的关系">UIView 和 CALayer 的关系</h2> <p>iOS面试特别喜欢问这个问题,一般回答都是UIView负责事件的交互,CALayer负责绘制;再深入一点,UIView是CALayer的代理,我们基于代理里面的函数 <strong>action</strong> 展开讲讲。</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">action</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="kt">CALayer</span><span class="p">,</span> <span class="nv">forKey</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CAAction</span><span class="p">?</span> </code></pre></div></div> <p>CAAction在我印象中没有接触过,查看了下文档是用于响应 layer 属性的变化。</p> <blockquote> <p>An interface that allows objects to respond to actions triggered by a CALayer change.</p> </blockquote> <p>CALayer默认是有隐式动画,隐约的感觉到有一丝丝的关系🤔</p> <h2 id="calayer-隐式动画-和-uiview-的关系">CALayer 隐式动画 和 UIView 的关系</h2> <p>CALayer大部分属性都是有隐性动画的,这也是我认为 iOS 动画开发体验比 android 好。回到上面的议题,UIView 和 CALayer是一一对应的,你修改了UIView 的属性,其实是设置到CALayer上,但是我们发现一个问题:在UIView 上修改属性,并没有动画效果,但是在animation block中又有了动画效果。</p> <p>是不是CAAction搞的鬼,如果返回nil默认没有动画,如果有就执行相关的动画,让我们继续思考🤔</p> <p>继续观察上面提到的<strong>CAAction</strong> ,在 CALayer 找到如下函数</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">action</span><span class="p">(</span><span class="n">forKey</span> <span class="nv">event</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CAAction</span><span class="p">?</span> </code></pre></div></div> <blockquote> <ol> <li>If the layer has a delegate that implements the action(for:forKey:) method, the layer calls that method. The delegate must do one of the following: <ol> <li>Return the action object for the given key.</li> <li>Return the NSNull object if it does not handle the action.</li> </ol> </li> <li> <p>The layer looks in the layer’s actions dictionary for a matching key/action pair.</p> </li> <li> <p>The layer looks in the style dictionary for an actions dictionary for a matching key/action pair.</p> </li> <li>The layer calls the defaultAction(forKey:) class method to look for any class-defined actions.</li> </ol> <p>If any of the above steps returns an instance of NSNull, it is converted to nil before continuing.</p> </blockquote> <p>大概就是通过:代理(UIView) –&gt; layer actions –&gt; style actions –&gt; class-defined actions. 获取对应的action.</p> <p>如果任何一层返回NSNull,则不再向下寻找。</p> <p>验证下:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//自定义Layer</span> <span class="kd">class</span> <span class="kt">DLayer</span><span class="p">:</span> <span class="kt">CALayer</span> <span class="p">{</span> <span class="k">override</span> <span class="kd">func</span> <span class="nf">action</span><span class="p">(</span><span class="k">for</span> <span class="nv">layer</span><span class="p">:</span> <span class="kt">CALayer</span><span class="p">,</span> <span class="n">forKey</span> <span class="nv">event</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">CAAction</span><span class="p">?</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">action</span> <span class="o">=</span> <span class="k">super</span><span class="o">.</span><span class="nf">action</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="n">layer</span><span class="p">,</span> <span class="nv">forKey</span><span class="p">:</span> <span class="n">event</span><span class="p">)</span> <span class="c1">//查看action</span> <span class="k">return</span> <span class="n">action</span> <span class="p">}</span> <span class="p">}</span> <span class="c1">//自定义view 并且自定Layer</span> <span class="kd">class</span> <span class="kt">DView</span><span class="p">:</span> <span class="kt">UIView</span> <span class="p">{</span> <span class="k">override</span> <span class="kd">class</span> <span class="k">var</span> <span class="nv">layerClass</span><span class="p">:</span> <span class="kt">AnyClass</span><span class="p">{</span> <span class="k">return</span> <span class="kt">DLayer</span><span class="o">.</span><span class="k">self</span> <span class="p">}</span> <span class="p">}</span> <span class="c1">//测试代码</span> <span class="k">let</span> <span class="nv">view</span> <span class="o">=</span> <span class="kt">DView</span><span class="p">(</span><span class="nv">frame</span><span class="p">:</span> <span class="kt">CGRect</span><span class="o">.</span><span class="n">zero</span><span class="p">)</span> <span class="k">self</span><span class="o">.</span><span class="n">view</span><span class="o">.</span><span class="nf">addSubview</span><span class="p">(</span><span class="n">view</span><span class="p">)</span> <span class="n">view</span><span class="o">.</span><span class="n">frame</span> <span class="o">=</span> <span class="kt">CGRect</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">width</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">height</span><span class="p">:</span> <span class="mi">10</span><span class="p">)</span> <span class="o">------</span> <span class="mi">1</span> <span class="kt">UIView</span><span class="o">.</span><span class="nf">animate</span><span class="p">(</span><span class="nv">withDuration</span><span class="p">:</span> <span class="mi">5</span><span class="p">)</span> <span class="p">{</span> <span class="n">view</span><span class="o">.</span><span class="n">frame</span> <span class="o">=</span> <span class="kt">CGRect</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">width</span><span class="p">:</span> <span class="mi">20</span><span class="p">,</span> <span class="nv">height</span><span class="p">:</span> <span class="mi">20</span><span class="p">)</span> <span class="o">------</span> <span class="mi">2</span> <span class="p">}</span> </code></pre></div></div> <p>其中1处,打印action 为 **<null>** , 2处打印为 **_UIViewAdditiveAnimationAction** 应该是个实现了CAAction协议的内部类,验证了我们上面的猜想。</null></p> <p>继续打印 CALayer 添加动画的函数</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="kd">func</span> <span class="nf">add</span><span class="p">(</span><span class="n">_</span> <span class="nv">anim</span><span class="p">:</span> <span class="kt">CAAnimation</span><span class="p">,</span> <span class="n">forKey</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span><span class="p">?)</span> <span class="p">{</span> <span class="nf">print</span><span class="p">(</span><span class="n">key</span><span class="o">!</span><span class="p">)</span> <span class="nf">print</span><span class="p">(</span><span class="n">anim</span><span class="o">.</span><span class="n">debugDescription</span><span class="p">)</span> <span class="k">super</span><span class="o">.</span><span class="nf">add</span><span class="p">(</span><span class="n">anim</span><span class="p">,</span> <span class="nv">forKey</span><span class="p">:</span> <span class="n">key</span><span class="p">)</span> <span class="p">}</span> <span class="c1">//</span> <span class="n">position</span> <span class="o">&lt;</span><span class="kt">CABasicAnimation</span><span class="p">:</span><span class="mh">0x600001d9cf20</span><span class="p">;</span> <span class="n">toValue</span> <span class="o">=</span> <span class="kt">NSPoint</span><span class="p">:</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">};</span> <span class="n">additive</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">delegate</span> <span class="o">=</span> <span class="o">&lt;</span><span class="kt">UIViewAnimationState</span><span class="p">:</span> <span class="mh">0x7f9122c084b0</span><span class="o">&gt;</span><span class="p">;</span> <span class="n">fillMode</span> <span class="o">=</span> <span class="n">both</span><span class="p">;</span> <span class="n">timingFunction</span> <span class="o">=</span> <span class="n">easeInEaseOut</span><span class="p">;</span> <span class="n">duration</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> <span class="n">fromValue</span> <span class="o">=</span> <span class="kt">NSPoint</span><span class="p">:</span> <span class="p">{</span><span class="o">-</span><span class="mi">10</span><span class="p">,</span> <span class="o">-</span><span class="mi">10</span><span class="p">};</span> <span class="n">keyPath</span> <span class="o">=</span> <span class="n">position</span><span class="o">&gt;</span> <span class="n">bounds</span><span class="o">.</span><span class="n">origin</span> <span class="o">&lt;</span><span class="kt">CABasicAnimation</span><span class="p">:</span><span class="mh">0x600001d9d2c0</span><span class="p">;</span> <span class="n">toValue</span> <span class="o">=</span> <span class="kt">NSPoint</span><span class="p">:</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">};</span> <span class="n">additive</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">fromValue</span> <span class="o">=</span> <span class="kt">NSPoint</span><span class="p">:</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">};</span> <span class="n">keyPath</span> <span class="o">=</span> <span class="n">bounds</span><span class="o">.</span><span class="n">origin</span><span class="p">;</span> <span class="n">delegate</span> <span class="o">=</span> <span class="o">&lt;</span><span class="kt">UIViewAnimationState</span><span class="p">:</span> <span class="mh">0x7f9122c084b0</span><span class="o">&gt;</span><span class="p">;</span> <span class="n">fillMode</span> <span class="o">=</span> <span class="n">both</span><span class="p">;</span> <span class="n">timingFunction</span> <span class="o">=</span> <span class="n">easeInEaseOut</span><span class="p">;</span> <span class="n">duration</span> <span class="o">=</span> <span class="mi">5</span><span class="o">&gt;</span> <span class="n">bounds</span><span class="o">.</span><span class="n">size</span> <span class="o">&lt;</span><span class="kt">CABasicAnimation</span><span class="p">:</span><span class="mh">0x600001d9d400</span><span class="p">;</span> <span class="n">toValue</span> <span class="o">=</span> <span class="kt">NSSize</span><span class="p">:</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">};</span> <span class="n">additive</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">fromValue</span> <span class="o">=</span> <span class="kt">NSSize</span><span class="p">:</span> <span class="p">{</span><span class="o">-</span><span class="mi">20</span><span class="p">,</span> <span class="o">-</span><span class="mi">20</span><span class="p">};</span> <span class="n">keyPath</span> <span class="o">=</span> <span class="n">bounds</span><span class="o">.</span><span class="n">size</span><span class="p">;</span> <span class="n">delegate</span> <span class="o">=</span> <span class="o">&lt;</span><span class="kt">UIViewAnimationState</span><span class="p">:</span> <span class="mh">0x7f9122c084b0</span><span class="o">&gt;</span><span class="p">;</span> <span class="n">fillMode</span> <span class="o">=</span> <span class="n">both</span><span class="p">;</span> <span class="n">timingFunction</span> <span class="o">=</span> <span class="n">easeInEaseOut</span><span class="p">;</span> <span class="n">duration</span> <span class="o">=</span> <span class="mi">5</span><span class="o">&gt;</span> </code></pre></div></div> <p>是不是感觉非常熟悉,这就是我们平常自定义动画的方式。我们在修改frame时候,默认系统添加了3个animation,分别是 position、bounds.origin、bounds.size。</p> <h2 id="总结">总结</h2> <p>通过上面的思考,我们对 Layer 和 View 关系有了进一步的了解,特别是 CA Animation这一块。</p> <ol> <li>UIView 持有 CALayer, 并且是 CALayer的代理</li> <li>如果是通过UIView修改属性造成Layer属性变化时,Layer会通过4个方式来获取CAAction。</li> <li>获取action后,添加对应CALayer CAAnimation</li> </ol>Dcell767536477@qq.com在几年前,我浏览过由猫神翻译的一篇Objc中国的文章。讲的是UIView和CALayer的协作关系,讲的非常好;但是一段时间过去后,发现自己慢慢的又遗忘了,所以打算自己写一下,并且跑一些Demo来加深自己的理解。创建Xcode自定义模版2020-12-25T00:00:00+00:002020-12-25T00:00:00+00:00https://dcell.github.io/2020/12/25/%E5%88%9B%E5%BB%BAxcode%E8%87%AA%E5%AE%9A%E4%B9%89%E6%A8%A1%E7%89%88<p>最近有个需求要搞一个大基座,要求项目都按照某种方式生成,然后功能模版就和组件一样,一个个勾选就可以了。</p> <h2 id="思考">思考</h2> <p>对于模版的功能,我第一个念头就想到是元编程</p> <ul> <li>按照需求生成一个元模版</li> <li>通过自定义参数生成出基于元模版的一些功能模块</li> <li>编写脚本,提供一套手脚架</li> </ul> <h2 id="xcode模版">Xcode模版</h2> <p>平时我们创建iOS工程的时候,选择的一些模版,包括App、framework、App Extension其实都是一个模版,模版分2类:</p> <ol> <li>Project Templates 工程模版</li> <li>File Templates 文件模版</li> </ol> <p>路径在<code class="language-plaintext highlighter-rouge">/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates</code> (不同的Xcode版本可能有所不同)</p> <p><img src="https://i.loli.net/2020/12/24/deVMpqGXtjIzBbc.png" alt="截屏2020-12-24 下午2.43.07.png" /></p> <h2 id="自定义模版">自定义模版</h2> <p>如果我们要创建一个自定义模版,不能在系统的模版上重新创建,需要在<code class="language-plaintext highlighter-rouge">~/Library/Developer/Xcode/Templates</code>去创建</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># ~/Library/Developer/Xcode/Templates/{模版类别}/{模版名称}.xctemplate</span> <span class="nb">mkdir</span> <span class="nt">-p</span> ~/Library/Developer/Xcode/Templates/ding_qili <span class="nb">cp</span> <span class="nt">-R</span> /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/Project<span class="se">\ </span>Templates/iOS/Application/App.xctemplate ~/Library/Developer/Xcode/Templates/ding_qili/ding_qili.xctemplate </code></pre></div></div> <p>然后重启Xcode,新建一个工程,在选择中多了一个类别和一个模版,因为模版是完全拷贝过来的,创建的自定义项目和默认创建项目是一样的</p> <p><img src="https://i.loli.net/2020/12/24/5mRxtY6gbeLljaq.png" alt="截屏2020-12-24 下午2.56.50.png" /></p> <h2 id="appxctemplate-模版分析">App.xctemplate 模版分析</h2> <ol> <li>TemplateIcon.png: 模版显示的图片</li> </ol> <p><img src="https://i.loli.net/2020/12/24/SRTPo59hElQJU74.png" alt="截屏2020-12-24 下午3.51.02.png" /></p> <ol> <li>TemplateInfo.plist</li> </ol> <ul> <li>Kind :类别好像没发现什么特殊的作用</li> <li>Identifier: 唯一标记,不要和系统的冲突,会出现覆盖现象</li> <li>Ancestors:继承与哪个模版,可以多选</li> <li>Concrete:是否展示出来,如果选择NO,那就变成匿名的模版,只能被别人继承使用</li> <li>Description:描述</li> <li>SortOrder:显示的排序,越小越显示在前面</li> <li>NameOfInitialFileForEditor: 未知好像和SwiftUI相关</li> <li>Options: 可选项</li> <li>…其他</li> </ul> <h2 id="实战创建一个带podfile的模版">实战–创建一个带Podfile的模版</h2> <p>因为模版没有相关的开发文档,只能通过网上的一些信息加上自己的摸索,接下来我通过修复官方的模版,创建一个自带Podfile的模版。</p> <p>我们先看下效果</p> <p><img src="https://i.loli.net/2020/12/25/6JduiG7PEgCqVvW.png" alt="截屏2020-12-25 下午2.50.24.png" /></p> <p><img src="https://i.loli.net/2020/12/25/OQxcqyeHWR6prS9.png" alt="截屏2020-12-25 下午3.33.47.png" /></p> <p><img src="https://i.loli.net/2020/12/25/KPcbnLdpzuytA4r.png" alt="截屏2020-12-25 下午3.34.01.png" /></p> <ul> <li>Options添加一个新的选项</li> <li>类型是Popup,支持2种包的集成方式,Pod和Swift Package,默认是Pod(Swift Package未实现)</li> <li>在Units添加选择类型的实现,这边是Copy 模版下的Podfile文件 到 当前工程下,并且不选中任何的Target</li> </ul> <p><a href="https://github.com/Dcell/my-test/tree/master/ding_qili.xctemplate">demo模版</a></p> <p><a href="https://github.com/Dcell/my-test/tree/master/ding_qili.xctemplate/Xcode 4 Template Documentation.pdf">收集的开发文档</a></p> <h2 id="总结">总结</h2> <p>这边就不在发散开来讲了,模版的需求本来就不多而且没有官方的文档,如果要自定义一个,我建议参考官方的文档摸索着写写,可以立马上手。</p>Dcell767536477@qq.com最近有个需求要搞一个大基座,要求项目都按照某种方式生成,然后功能模版就和组件一样,一个个勾选就可以了。Swift Memory Layout2020-12-21T00:00:00+00:002020-12-21T00:00:00+00:00https://dcell.github.io/2020/12/21/Swift%20Memory%20Layout<h2 id="swift-memory-layout">Swift Memory Layout</h2> <h4 id="unsafe-pointer">Unsafe Pointer</h4> <p>在开始介绍<strong>Memory Layout</strong>之前,先介绍下Swift指针。如图所示,除了黑色Unsafe和Pointer固定外,其他都是可选的,一共有8种不同的类型。</p> <p><img src="https://i.loli.net/2020/12/16/8Sf9GysgvDE1bjT.png" alt="截屏2020-12-16 下午2.10.23.png" /></p> <h4 id="memory--layout">Memory Layout</h4> <p>早在Swift3.0的时候就推出函数<strong>MemoryLayout</strong>计算内存大小。其中分为如下3个参数</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;.</span><span class="n">size</span> <span class="c1">//内存大小</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;.</span><span class="n">stride</span> <span class="c1">//分配的内存大小</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">T</span><span class="o">&gt;.</span><span class="n">alignment</span> <span class="c1">//内存对齐大小</span> </code></pre></div></div> <p>比如:</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Int32</span><span class="o">&gt;.</span><span class="n">size</span> <span class="c1">//4</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Int32</span><span class="o">&gt;.</span><span class="n">stride</span> <span class="c1">//4 </span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Int32</span><span class="o">&gt;.</span><span class="n">alignment</span> <span class="c1">//4</span> </code></pre></div></div> <h3 id="size">size</h3> <p>**MemoryLayout<T>.size** 表示一个数据在内存中占用的大小,比如常见的Int32 4个字节,Int64 8个字节。</T></p> <p>除了计算基础类型外,也可以计算结构体和对象</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Person</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">age</span><span class="p">:</span><span class="kt">Int64</span> <span class="k">let</span> <span class="nv">sex</span><span class="p">:</span><span class="kt">Bool</span> <span class="p">}</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Person</span><span class="o">&gt;.</span><span class="n">size</span> <span class="c1">// 9</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Person</span><span class="o">&gt;.</span><span class="n">stride</span> <span class="c1">//16</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Person</span><span class="o">&gt;.</span><span class="n">alignment</span> <span class="c1">//8</span> </code></pre></div></div> <h3 id="stride--alignment">stride &amp; alignment</h3> <h4 id="什么是内存对齐">什么是内存对齐</h4> <p>CPU 更高效地读写内存,采用了<strong>word size</strong>读取方式,避免造成额外的开销;但是也要求类型的存储地址要与其内存对齐。</p> <p>我们修改下上面的demo如下:为什么修改了下属性的顺序,结构体的size变化了呢?</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Person</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">sex</span><span class="p">:</span><span class="kt">Bool</span> <span class="k">let</span> <span class="nv">age</span><span class="p">:</span><span class="kt">Int64</span> <span class="p">}</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Person</span><span class="o">&gt;.</span><span class="n">size</span> <span class="c1">// 16</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Person</span><span class="o">&gt;.</span><span class="n">stride</span> <span class="c1">// 16</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Person</span><span class="o">&gt;.</span><span class="n">alignment</span> <span class="c1">// 8</span> </code></pre></div></div> <p>首先我们分别获取属性<strong>Bool</strong>和<strong>Int64</strong>的内存模型</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Bool</span><span class="o">&gt;.</span><span class="n">size</span> <span class="c1">//1</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Bool</span><span class="o">&gt;.</span><span class="n">stride</span> <span class="c1">//1 </span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Bool</span><span class="o">&gt;.</span><span class="n">alignment</span> <span class="c1">//1</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Int64</span><span class="o">&gt;.</span><span class="n">size</span> <span class="c1">//8</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Int64</span><span class="o">&gt;.</span><span class="n">stride</span> <span class="c1">//8</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Int64</span><span class="o">&gt;.</span><span class="n">alignment</span> <span class="c1">//8</span> </code></pre></div></div> <ul> <li>首先是Bool类型,内存对齐是1,当前内存对象是1的倍数不需要padding,所以第一个字节是赋值Bool</li> <li>第2个属性是Int64,内存对齐是8,当前内存地址是1,不是8的倍数,需要padding7个字节</li> <li>地址8-16是Int64赋值,所以MemoryLayout<Person>.size = 16</Person></li> <li>MemoryLayout<Person>.alignment = Max( MemoryLayout<Bool>.alignment ,MemoryLayout<Bool>.alignment ) = 8</Bool></Bool></Person></li> <li>Person.alignment 满足8字节的内存对齐,不需要再padding,所以MemoryLayout<Person>.stride = 16</Person></li> </ul> <p><img src="https://i.loli.net/2020/12/23/efQR4EK8pP2xM3V.png" alt="截屏2020-12-23 下午2.58.04.png" /></p> <p>请画出:未交换属性之前的内存图</p> <p><img src="https://i.loli.net/2020/12/23/Lxr1caFZADozbsi.png" alt="截屏2020-12-23 下午3.17.35.png" /></p> <h3 id="实战">实战</h3> <p>在xcode中,去真正内存中看下是否满足我们的猜想</p> <ul> <li> <p>初始化一个结构体 <code class="language-plaintext highlighter-rouge">var person = Person(sex: true, age: 11)</code> ,打印结构体地址 <code class="language-plaintext highlighter-rouge">withUnsafeMutablePointer(to: &amp;self)</code> 或者通过如下方式</p> <p><img src="https://i.loli.net/2020/12/23/72VNIgqaxO3mikG.png" alt="截屏2020-12-23 下午3.52.44.png" /></p> </li> <li> <p>查看该地址在内存的信息</p> <p><img src="https://i.loli.net/2020/12/23/EAXPCBad5kvRF4i.png" alt="截屏2020-12-23 下午3.55.51.png" /></p> </li> </ul> <p>第一个字节是01 表示 <strong>let sex:Bool</strong> ,然后padding7个0x00 字节,最后0x000000000000000B 表示 11,就是我们设置的 <strong>let age:Int64</strong></p> <h3 id="猜想">猜想</h3> <p>从结构体的内存模型来看,好像无法区分是什么类型,如果我定义一个一摸一样的结构体,是不是可以偷天换日呢?</p> <div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Person</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">sex</span><span class="p">:</span><span class="kt">Bool</span> <span class="k">let</span> <span class="nv">age</span><span class="p">:</span><span class="kt">Int64</span> <span class="p">}</span> <span class="kd">struct</span> <span class="kt">Person2</span> <span class="p">{</span> <span class="k">let</span> <span class="nv">sex</span><span class="p">:</span><span class="kt">Bool</span> <span class="k">let</span> <span class="nv">age</span><span class="p">:</span><span class="kt">Int64</span> <span class="p">}</span> <span class="k">var</span> <span class="nv">person</span> <span class="o">=</span> <span class="kt">Person</span><span class="p">(</span><span class="nv">sex</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nv">age</span><span class="p">:</span> <span class="mi">11</span><span class="p">)</span> <span class="k">let</span> <span class="nv">rawPointer</span> <span class="o">=</span> <span class="kt">UnsafeMutableRawPointer</span><span class="o">.</span><span class="nf">allocate</span><span class="p">(</span><span class="nv">byteCount</span><span class="p">:</span><span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Person2</span><span class="o">&gt;.</span><span class="n">stride</span><span class="p">,</span> <span class="nv">alignment</span><span class="p">:</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Person2</span><span class="o">&gt;.</span><span class="n">alignment</span><span class="p">)</span> <span class="k">let</span> <span class="nv">personPointer</span> <span class="o">=</span> <span class="nf">withUnsafeMutablePointer</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">person</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="kt">UnsafeMutableRawPointer</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span><span class="o">.</span><span class="nf">bindMemory</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="kt">Int8</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="nv">capacity</span><span class="p">:</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;.</span><span class="n">stride</span><span class="p">)</span> <span class="p">}</span> <span class="n">rawPointer</span><span class="o">.</span><span class="nf">copyMemory</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="n">personPointer</span><span class="p">,</span> <span class="nv">byteCount</span><span class="p">:</span> <span class="kt">MemoryLayout</span><span class="o">&lt;</span><span class="kt">Person2</span><span class="o">&gt;.</span><span class="n">stride</span><span class="p">)</span> <span class="k">let</span> <span class="nv">person2</span> <span class="o">=</span> <span class="n">rawPointer</span><span class="o">.</span><span class="nf">load</span><span class="p">(</span><span class="nv">as</span><span class="p">:</span> <span class="kt">Person2</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="kt">XCTAssertTrue</span><span class="p">(</span><span class="n">person</span><span class="o">.</span><span class="n">sex</span> <span class="o">==</span> <span class="n">person2</span><span class="o">.</span><span class="n">sex</span> <span class="o">&amp;&amp;</span> <span class="n">person</span><span class="o">.</span><span class="n">age</span> <span class="o">==</span> <span class="n">person2</span><span class="o">.</span><span class="n">age</span><span class="p">)</span> <span class="c1">//pass</span> </code></pre></div></div> <h3 id="总结">总结</h3> <ul> <li> <p>MemoryLayout<T>.alignment = Max( MemoryLayout<T.var1>.alignment ,MemoryLayout<T.varn>.alignment )</T.varn></T.var1></T></p> </li> <li> <p>MemoryLayout<T>.size = MemoryLayout<T.var1>.size + padding + ... + MemoryLayout<T.varn>.size</T.varn></T.var1></T></p> </li> <li> <p>MemoryLayout<T>.stride = MemoryLayout<T>.size + padding</T></T></p> <p>其中padding是通过内存对其来计算的</p> </li> </ul>Dcell767536477@qq.comSwift Memory Layout小程序之美团外卖邀请链接2020-12-14T00:00:00+00:002020-12-14T00:00:00+00:00https://dcell.github.io/2020/12/14/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E4%B9%8B%E7%BE%8E%E5%9B%A2%E5%A4%96%E5%8D%96%E9%82%80%E8%AF%B7%E9%93%BE%E6%8E%A5<p>前不久无意中刷到一个同学说:自己做了个<a href="https://github.com/Dcell/coupons">小程序</a>,里面可以分享各种外卖平台红包,然后用户下单后可以分红。这极大的引起了我的兴趣,赚不赚钱(🐶当然要)无所谓就是自己也想搞一个…</p> <p>大概看了下流程,首先你要在各个外卖平台联盟里注册推广,比如:饿了么,你需要在淘宝联盟注册;但是问题就出在我最常用的外卖平台美团,暂不支持个人账号申请推广,这让我很难受啊… 我得想个办法。</p> <h2 id="第一步">第一步</h2> <p>首先打开美团小程序,在【我的】【邀请有奖页面】</p> <p><img src="https://i.loli.net/2020/12/17/IVK3W7m28qSO6uF.png" alt="IMG_2360.PNG" style="zoom:30%;" /></p> <p><img src="https://i.loli.net/2020/12/17/Wzgif1NVRFsTx3l.png" alt="image.png" style="zoom:33%;" /></p> <p>把邀请发送到自己的Mac 微信【文件传输组手】。</p> <h2 id="第二步">第二步</h2> <p>把Mac微信的聊天记录给导出来,我用的解密db文件的方式,因为微信采用的是<strong>SQLCipher</strong>加密,这里就不展开讲了网上有很多的教程。等我们拿到db数据后,找到对应的聊天信息,基本可以通过肉眼就能确定,刚才分享过来的小程序链接;我截取了一段放出来看看。</p> <div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;weappinfo&gt;</span> <span class="nt">&lt;username&gt;</span><span class="cp">&lt;![CDATA[gh_72a4eb2d4324@app]]&gt;</span><span class="nt">&lt;/username&gt;</span> <span class="nt">&lt;appid&gt;</span><span class="cp">&lt;![CDATA[wx2c348cf579062e56]]&gt;</span><span class="nt">&lt;/appid&gt;</span> <span class="nt">&lt;type&gt;</span>2<span class="nt">&lt;/type&gt;</span> <span class="nt">&lt;version&gt;</span>340<span class="nt">&lt;/version&gt;</span> <span class="nt">&lt;weappiconurl&gt;</span><span class="cp">&lt;![CDATA[http://mmbiz.qpic.cn/mmbiz_png/IXJic6HOb8QT02PwzH5wCUicpuGmIagaUJLxzGRKtoY8PLQqBR1UDHwK5DpsyRJnQ0OHAFGaA8jweXGUh8RsJpCA/640?wx_fmt=png&amp;wxfrom=200]]&gt;</span><span class="nt">&lt;/weappiconurl&gt;</span> <span class="nt">&lt;pagepath&gt;</span><span class="cp">&lt;![CDATA[outer_packages/r2xinvite/coupon/coupon.html?inviteCode=NnOIp-QOs8SiYF1dcSlL5r8phPrCf6qkH7evMyjIoup2NXxNCLYcBbd3bqpv2X2IOn5qfM4c28lrF49RL3kN7qKldLOV5bXOLrtjmuKQXOw_WKb38eCOvByJfrhKaFDEhpJ4NUwgFIKgxUTvIqoimgbBWQ6Ul_shsw1YN_XlAWM]]&gt;</span><span class="nt">&lt;/pagepath&gt;</span> <span class="nt">&lt;shareId&gt;</span><span class="cp">&lt;![CDATA[0_wx2c348cf579062e56_06c58c1a452776ed22d64f6b62cbe83a_1608020809_0]]&gt;</span><span class="nt">&lt;/shareId&gt;</span> <span class="nt">&lt;appservicetype&gt;</span>0<span class="nt">&lt;/appservicetype&gt;</span> <span class="nt">&lt;tradingguaranteeflag&gt;</span>0<span class="nt">&lt;/tradingguaranteeflag&gt;</span> <span class="nt">&lt;brandofficialflag&gt;</span>0<span class="nt">&lt;/brandofficialflag&gt;</span> <span class="nt">&lt;subType&gt;</span>0<span class="nt">&lt;/subType&gt;</span> <span class="nt">&lt;isprivatemessage&gt;</span>0<span class="nt">&lt;/isprivatemessage&gt;</span> <span class="nt">&lt;/weappinfo&gt;</span> </code></pre></div></div> <ul> <li>其中appid是小程序id,这个我查看美团小程序id后已经确认</li> <li>pagepath是小程序的路由地址,其中inviteCode应该是我的邀请码啦</li> </ul> <h2 id="第三步">第三步</h2> <p>开发一个小程序,拿出CV大法直接在github上找个开源的,把美团小程序链接改成<strong>pagepath</strong>(主要:去掉 .html),然后上架。</p> <h2 id="最后">最后</h2> <p>求求你们扫描二维码再下单吧~~~ 😭😭😭</p> <p><img src="https://i.loli.net/2020/12/17/XaZBfECLzHp62cm.jpg" alt="IMG_2359.JPG" style="zoom:50%;" /></p>Dcell767536477@qq.com前不久无意中刷到一个同学说:自己做了个小程序,里面可以分享各种外卖平台红包,然后用户下单后可以分红。这极大的引起了我的兴趣,赚不赚钱(🐶当然要)无所谓就是自己也想搞一个…shell命令中 单引号和双引号的区别2020-12-11T00:00:00+00:002020-12-11T00:00:00+00:00https://dcell.github.io/2020/12/11/shell%E5%91%BD%E4%BB%A4%E4%B8%AD-%E5%8D%95%E5%BC%95%E5%8F%B7%E5%92%8C%E5%8F%8C%E5%BC%95%E5%8F%B7%E7%9A%84%E5%8C%BA%E5%88%AB<p>​ 在没有碰到shell命令单引号和双引号之前,我一直以为shell里面<code class="language-plaintext highlighter-rouge">''</code> 和 <code class="language-plaintext highlighter-rouge">""</code>是和JS里面一样没什么区别的;今天在做android重签名工具的时候,测试在修改应用名称带空格的情况下一直签名失败,如:<code class="language-plaintext highlighter-rouge">测试 空格</code></p> <h3 id="问题1shell命令传入带空格的参数">问题1:shell命令传入带空格的参数</h3> <p>因为shell命名对参数的解析是通过空格的方式解析的</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>如:1 2 测试 ,那么对应的参数获取就是:<span class="nv">$1</span><span class="o">=</span>1<span class="p">;</span> <span class="nv">$2</span><span class="o">=</span>2<span class="p">;</span> <span class="nv">$3</span><span class="o">=</span>测试<span class="p">;</span> </code></pre></div></div> <p>如果你参数带了空格</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>如:1 2 测试 空格,那么对应的参数获取就是:<span class="nv">$1</span><span class="o">=</span>1<span class="p">;</span> <span class="nv">$2</span><span class="o">=</span>2<span class="p">;</span> <span class="nv">$3</span><span class="o">=</span>测试<span class="p">;</span> 其中‘空格’是对应参数 <span class="s1">'$4'</span> </code></pre></div></div> <h3 id="解决方法">解决方法</h3> <p>我们可以采用读取全末尾参数方式</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>如 <span class="nv">APP_NAME</span><span class="o">=</span><span class="sb">`</span><span class="nb">echo</span> <span class="k">${</span><span class="p">@</span>:3<span class="k">}</span><span class="sb">`</span> 表示读取第3个参数直到最后字符 </code></pre></div></div> <hr /> <h3 id="问题2--sed--i-替换带空格的问题">问题2: sed -i 替换带空格的问题</h3> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">rp</span><span class="o">=</span><span class="sb">`</span><span class="nb">echo</span> <span class="k">${</span><span class="p">@</span>:1<span class="k">}</span><span class="sb">`</span> <span class="nv">file</span><span class="o">=</span><span class="nv">$PWD</span>/txt <span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">'.bak'</span> <span class="s1">'s/**/'</span><span class="nv">$rp</span><span class="s1">'/g'</span> <span class="nv">$file</span> </code></pre></div></div> <p>测试空格输入,报错了,当时花了很多时间去google这个问题,当时搜索的思路是 “sed -i 替换文字 参数带空格”,但没什么收获。</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sh shell.sh 测试 空格 error <span class="o">&gt;&gt;&gt;</span> <span class="nb">sed</span>: 1: <span class="s2">"s/**/测试"</span>: unterminated substitute <span class="k">in </span>regular expression </code></pre></div></div> <p>后面换了个思路,尝试更改单引号为双引号。</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s1">'$rp'</span> <span class="o">===&gt;</span> <span class="s1">'$rp'</span> 如果是单引号,则不会执行取值,直接输出<span class="s1">''</span>内所有内容 </code></pre></div></div> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$rp</span><span class="s2">"</span> <span class="o">===&gt;</span> 测试 空格 如果是双引号,输出值内容 </code></pre></div></div> <p><strong>但是!我发现</strong>,如果用2个单引号包裹和一个双引号是一样的效果,没找到什么区别。</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s1">''</span><span class="nv">$rp</span><span class="s1">''</span> <span class="o">===&gt;</span> 测试 空格 </code></pre></div></div> <h3 id="解决问题">解决问题</h3> <p>修改shell如下,成功修改</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span> <span class="nv">rp</span><span class="o">=</span><span class="sb">`</span><span class="nb">echo</span> <span class="k">${</span><span class="p">@</span>:1<span class="k">}</span><span class="sb">`</span> <span class="nv">file</span><span class="o">=</span><span class="nv">$PWD</span>/txt <span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">'.bak'</span> <span class="s2">"s/**/</span><span class="nv">$rp</span><span class="s2">/g"</span> <span class="nv">$file</span> <span class="c">#sed -i '.bak' 's/**/'$rp'/g' $file #为什么这样不行,讲道理 2个单引号和一个双引号效果一样,关于这个疑问,还没有解决</span> </code></pre></div></div> <p>可以查看<a href="https://github.com/Dcell/my-test/tree/master/test-quote">Demo</a></p>Dcell767536477@qq.com​ 在没有碰到shell命令单引号和双引号之前,我一直以为shell里面'' 和 ""是和JS里面一样没什么区别的;今天在做android重签名工具的时候,测试在修改应用名称带空格的情况下一直签名失败,如:测试 空格WWDC20 WKWebView新特性2020-12-09T00:00:00+00:002020-12-09T00:00:00+00:00https://dcell.github.io/2020/12/09/WWDC20-WKWebView%E6%96%B0%E7%89%B9%E6%80%A7<p>WWDC20中,有一个讲座是对于iOS14中WKWebView的新特性,其中一个特性还是比较有意思的,那就是<strong>WKScriptMessageHandlerWithReply</strong> 。</p> <h2 id="wkscriptmessagehandlerwithreply-和-wkscriptmessagehandler-的区别"><strong>WKScriptMessageHandlerWithReply</strong> 和 <strong>WKScriptMessageHandler</strong> 的区别</h2> <p><strong>WKScriptMessageHandlerWithReply</strong>其实是对<strong>WKScriptMessageHandler</strong>的扩展,在<strong>WKScriptMessageHandler</strong>中JS调用<strong>PostMessage</strong>,返回的是undefine;而<strong>WKScriptMessageHandlerWithReply</strong>中调用<strong>PostMessage</strong> 返回的Promise;在Native中,<strong>WKScriptMessageHandlerWithReply</strong>多了一个<strong>replyHandler</strong>回调调</p> <pre><code class="language-objective-c">- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message replyHandler:(void (^)(id _Nullable reply, NSString *_Nullable errorMessage))replyHandler API_AVAILABLE(macos(11.0), ios(14.0)); </code></pre> <p>当Native调用<strong>replyHandler</strong>回调后,在JS端就执行了Promise的resolve函数,从而完成了这个JS调用Native的闭环。</p> <p>从这个现象来看,在iOS14的WKWebView中,系统支持了JS调用Native,并且返回Native执行结果。可以通过新的API来替换原先那些开源的JSBridge方案。现在越发手痒想自己搞一个JSBridge了。</p>Dcell767536477@qq.comWWDC20中,有一个讲座是对于iOS14中WKWebView的新特性,其中一个特性还是比较有意思的,那就是WKScriptMessageHandlerWithReply 。