<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[NSCookies]]></title><description><![CDATA[提供一些OC与Swift开发的小技巧，让大家可以花几分钟学到一些好玩的东西]]></description><link>http://www.nscookies.com/</link><generator>Ghost 0.7</generator><lastBuildDate>Tue, 30 Jan 2024 19:41:46 GMT</lastBuildDate><atom:link href="http://www.nscookies.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[装个逼：自定义三元运算符]]></title><description><![CDATA[<p>最近刚看完<a href="http://weibo.com/u/2210132365?topnav=1&amp;wvr=6&amp;topsug=1">@喵神</a>翻译的<a href="https://objccn.io/products/functional-swift/">函数式Swift</a>这本书（有兴趣的小伙伴可以去买来看看，记得支持正版哟~），然后就想说说 Swift 中的自定义操作符，这个在函数式编程中也是非常有意义的工具。例如在 <code>Applicative Functor</code> 中的 <code>&lt;*&gt;</code> 和 <code>Monad</code> 中的 <code>&gt;&gt;=</code> 在我们自己实现函数式编程中都有可能需要自定义来实现这两个操作符。另外，除了在函数式编程里，我们平常的编程工作中也有经常遇到需要重载或者自定义操作符的时候，例如实现了 <code>Equatable</code> 协议的情况下就需要重写 <code>==</code> 操作符。</p>

<p>但是这里并不是手把手教你如何自定义操作符，而是来说说在 Swift 2 和 3 中自定义操作符有什么不同吧，至于英文好的小伙伴可以参考<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0077-operator-precedence.md">SE-0077</a>。</p>

<p>在 Swift 2 的情况下，我们通常通过下面的代码来定义自己的操作符:</p>

<pre><code>// Swift 2
infix operator || {  
associativity: left</code></pre>]]></description><link>http://www.nscookies.com/custom-operator/</link><guid isPermaLink="false">9309e9f2-ff68-4dcd-b28e-f4c2a24922ce</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Sat, 01 Oct 2016 20:42:00 GMT</pubDate><content:encoded><![CDATA[<p>最近刚看完<a href="http://weibo.com/u/2210132365?topnav=1&amp;wvr=6&amp;topsug=1">@喵神</a>翻译的<a href="https://objccn.io/products/functional-swift/">函数式Swift</a>这本书（有兴趣的小伙伴可以去买来看看，记得支持正版哟~），然后就想说说 Swift 中的自定义操作符，这个在函数式编程中也是非常有意义的工具。例如在 <code>Applicative Functor</code> 中的 <code>&lt;*&gt;</code> 和 <code>Monad</code> 中的 <code>&gt;&gt;=</code> 在我们自己实现函数式编程中都有可能需要自定义来实现这两个操作符。另外，除了在函数式编程里，我们平常的编程工作中也有经常遇到需要重载或者自定义操作符的时候，例如实现了 <code>Equatable</code> 协议的情况下就需要重写 <code>==</code> 操作符。</p>

<p>但是这里并不是手把手教你如何自定义操作符，而是来说说在 Swift 2 和 3 中自定义操作符有什么不同吧，至于英文好的小伙伴可以参考<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0077-operator-precedence.md">SE-0077</a>。</p>

<p>在 Swift 2 的情况下，我们通常通过下面的代码来定义自己的操作符:</p>

<pre><code>// Swift 2
infix operator || {  
associativity: left  
precedence: 140  
}

func ||&lt;T&gt;(left: T, right: T) -&gt; T {  
    // do something
}
</code></pre>

<p>而在 Swift 3 中我们则通过下面方式来进行替代:</p>

<pre><code>// Swift 3
precedencegroup OrGroup {  
    associativity: left
    higherThan: AdditionPrecedence
    lowerThan: MultiplicationPrecedence
}

infix operator ||: OrGroup

func ||&lt;T&gt;(left: T, right: T) -&gt; T {  
    // do something
}
</code></pre>

<p>整体上来说 Swift 2 和 3 之间是没有特别大的改动，只是将格式方面进行一种更加优雅的进化而已。虽然进行了这样的调整，但是有一点让宝宝不爽的是，已经定义的 <code>group</code> 在 Xcode 中竟然不给自动提示，特别是对于一些标准库中已经定义的 <code>group</code> 竟然也不给提示，这就让宝宝不开心了。目前的解决方案就是通过<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0077-operator-precedence.md">SE-0077</a>这里来查询。</p>

<p>好了，最后我们来简单说说如何实现三元操作符吧，也当是给个自定义操作符的小结。我们都知道 Swift 跟其他大多数语言一样，对于三元操作符只提供了条件操作符 <code>_ ? _ : _</code> ，那么如果我们想自己定义三元操作符怎么办呢？记得有一句话是这么说的，方法总比问题多~</p>

<p>其实我们可以创造一个伪三元操作符，就是用两个二元操作符来结合产生一个三元操作符,例如我们用 <code>_ ??? _ ||| _</code> 来实现一下条件操作符的功能。首先我们先定义一下两个操作符：</p>

<pre><code>// Swift 3
precedencegroup QuesGroup {  
    associativity: left
}

infix operator ???: QuesGroup

precedencegroup OrGroup {  
    associativity: left
    higherThan: QuesGroup
}

infix operator |||: OrGroup  
</code></pre>

<p>为什么这里的 <code>OrGroup</code> 要用 <code>higherThan: QuesGroup</code> 呢？我们设想一下，如果是 <code>QuesGroup</code> 中用 <code>higherThan: OrGroup</code> 的话，那么我们自定义的条件表达式就变成了 <code>(_ ??? _) ||| _</code> 这种形式了，而是不是 <code>_ ??? (_ ||| _)</code> 这种形式。</p>

<p>其次我们来考虑一下条件操作符的流程，它是先查看<code>?</code>前的布尔值，然后再根据布尔值来决定返回哪一个部分，那么我们可以认为后面一部人是需要一个布尔值作为参数，返回数据的方法。并且根据其有短路的功能，我们可以采用 <code>@autoclosure</code> 来实现延迟加载以及短路的功能。并且根据上面的说法我们知道第二个参数和第三个参数其实是一个整体，并且根据前面来返回一个值。具体实现就如下面:</p>

<pre><code>// Swift 3
func |||&lt;T&gt;(left: @autoclosure @escaping () -&gt; T, right: @autoclosure @escaping () -&gt; T) -&gt; (Bool) -&gt; T {  
    return { condition in
        if condition {
            return left()
        } else {
            return right()
        }
    }
}

func ???&lt;T&gt;(condition: @autoclosure @escaping () -&gt; Bool, value: (Bool) -&gt; T) -&gt; T {  
    return value(condition())
}
</code></pre>

<p>代码非常简单，测试代码就更简单了：</p>

<pre><code>let bTrue = true  
let bFalse = false

bTrue ??? "true value" ||| "false value"  
// 输出 true value
bFalse ??? "true value" ||| "false value"  
// 输出 false value
</code></pre>

<p>到这里就搞定了自定义"三元操作符"了~炒鸡简单。那么既然回了三元，那要定义四元，五元甚至更多元都可以采用相同的方式。只要分析好逻辑，并写出来就好了，好吧~就这样了。</p>

<p>宝宝去被窝里蓝瘦，香菇了~在录一个视频吓死你们</p>

<blockquote>
  <p>PS:具体代码可以从<a href="https://github.com/NSCookies">Github</a>上获取。</p>
  
  <p>如有问题或纠正, 可以联系<a href="http://weibo.com/plutoy0504">@叫什么都不如叫Pluto-Y</a>或在<a href="https://github.com/NSCookies">Github</a></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[代表月亮私有你]]></title><description><![CDATA[<p>"你问我爱你有多深, 我爱你有几分, 我的情不移, 我的爱不变, 月亮代表我的心"，既然月亮代表我的新，是不是说明了我的要私有你？哈哈开玩笑，我们来说一个装逼的东西~</p>

<p>我们在写OC的过程中经常有这么一种情况，就是我们需要写一个单例的情况。当然我们都会提供一个类方法类似于<code>+(instancetype)sharedInstance</code>之类的方法，巴特(but)我们的类的开山鼻祖都是<code>NSObject</code>,而这个鼻祖竟然提供了<code>- (instancetype)init</code>让别人来调用。这样宝宝就不开心了，宝宝很委屈，可是宝宝不说，我就是想让别人调用我的类方法来获得单例嘛~(呕吐~)</p>

<p>那么我们有没有办法让<code>init</code>方法私有化呢？或者是让小伙伴不要去调用它呢？答案是木有，因为我们知道OC没有私有化父类方法的做法，并且OC有非常强大的Runtime机制，致使我们没有办法私有化<code>init</code>方法。通常这个时候都会有个但是，要不然还写这篇文章干嘛。</p>

<p>其实虽然没有完全私有<code>init</code>的方法，但是我们可以通过一些手段来私有化它，让我们来一个一个的看看吧。</p>

<p>首先，我们可以用Clang的Attributes中的unavailable来让产生一个编译错误。怎么做呢？简单的要命,</p>]]></description><link>http://www.nscookies.com/private-init/</link><guid isPermaLink="false">20a63349-7a7e-4204-9a01-b3d9c369ae1b</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Sun, 25 Sep 2016 21:42:37 GMT</pubDate><content:encoded><![CDATA[<p>"你问我爱你有多深, 我爱你有几分, 我的情不移, 我的爱不变, 月亮代表我的心"，既然月亮代表我的新，是不是说明了我的要私有你？哈哈开玩笑，我们来说一个装逼的东西~</p>

<p>我们在写OC的过程中经常有这么一种情况，就是我们需要写一个单例的情况。当然我们都会提供一个类方法类似于<code>+(instancetype)sharedInstance</code>之类的方法，巴特(but)我们的类的开山鼻祖都是<code>NSObject</code>,而这个鼻祖竟然提供了<code>- (instancetype)init</code>让别人来调用。这样宝宝就不开心了，宝宝很委屈，可是宝宝不说，我就是想让别人调用我的类方法来获得单例嘛~(呕吐~)</p>

<p>那么我们有没有办法让<code>init</code>方法私有化呢？或者是让小伙伴不要去调用它呢？答案是木有，因为我们知道OC没有私有化父类方法的做法，并且OC有非常强大的Runtime机制，致使我们没有办法私有化<code>init</code>方法。通常这个时候都会有个但是，要不然还写这篇文章干嘛。</p>

<p>其实虽然没有完全私有<code>init</code>的方法，但是我们可以通过一些手段来私有化它，让我们来一个一个的看看吧。</p>

<p>首先，我们可以用Clang的Attributes中的unavailable来让产生一个编译错误。怎么做呢？简单的要命,只要添加如下的声明即可:</p>

<pre><code class="language-objectivec">- (instancetype)init __attribute__((unavailable("Disabled. Use +sharedInstance instead")));
</code></pre>

<p>那么会产生什么效果呢？</p>

<p><img src="http://www.nscookies.com/content/images/2016/09/attribute-unavailable.png" alt="attribute-unavailable.png"></p>

<p>个人比较喜欢第一种，特别是在写一些开源的情况下，能在其他小伙伴调用的时候就直接给出编译错误。而且也能给出有用的信息提示小伙伴调用其他的方法进行替代。</p>

<p>第二种，通过使用<code>NS_UNAVAILABLE</code>来声明，具体使用方法与第一种类似:</p>

<pre><code class="language-objectivec">- (instancetype)init NS_UNAVAILABLE;
</code></pre>

<p>效果如下：</p>

<p><img src="http://www.nscookies.com/content/images/2016/09/NS_UNAVAILABLE.png" alt="NS_UNAVAILABLE.png"></p>

<p>而这种方式也是产生了一个编译错误，只是这种方式没有办法给出更加具体的提示信息，而细心的小伙伴可以发现，第二种方式的提示信息和第一种方式很像，为什么呢？我们来看看关于<code>NS_UNAVAILABLE</code>的定义:</p>

<pre><code class="language-objectivec">#if !defined(NS_UNAVAILABLE)
#define NS_UNAVAILABLE UNAVAILABLE_ATTRIBUTE
#endif
</code></pre>

<p>我们可以从名字<code>UNAVAILABLE_ATTRIBUTE</code>看的出来，它应该也是用到了<code>__attribute__</code>只是应该提示信息是空字符串，应该等价于下面这两种情况，至少从展现的形式上来看应该是等价的。</p>

<pre><code class="language-objectivec">- (instancetype)init __attribute__((unavailable));
- (instancetype)init __attribute__((unavailable));
</code></pre>

<p>第三种是在<code>init</code>方法中调用<code>doesNotRecognizeSelector</code>来搞定，也非常简单，大概如下</p>

<pre><code class="language-objectivec">- (instancetype)init {
    [super doesNotRecognizeSelector:_cmd];
    return nil;
}
</code></pre>

<p>这种方法则会在调用的时候抛出一个类似于"unrecognized selector"信息的错误。这种形式其实没有直接指明真正的原因，可能会让用户丈二和尚摸不着头脑的赶脚，可能分分钟给你差评，所以这种方法可能会被以欺君罪分分钟被分尸~</p>

<p>第四种则是在<code>init</code>中通过断言或者异常的形式来让用户在运行的时候crash来提示用户，这个点很像第三点，但是通过断言或者异常我们可以通过给出相对应的错误信息提示用户来用其他方式来替代的方法，代码可以如下:</p>

<pre><code>// 通过断言
- (instancetype)init {
    NSAssert(false,@"unavailable, use sharedInstance instead");
    return nil;
}

// 或者通过异常
- (instancetype)init {
    [NSException raise:NSGenericException
                format:@"Disabled. Use +[%@ %@] instead",
     NSStringFromClass([self class]),
     NSStringFromSelector(@selector(sharedInstance))];
    return nil;
}
</code></pre>

<p>这种方式虽然能提示正确的信息，可是用户必须要到运行的时候才能看到错误的信息，所以个人感觉还是偏迟了一点。</p>

<p>小小的总结一下，虽然提供了四种方式，但是本来还是喜欢第一种方式，毕竟第一种方式能在编译的过程中就能获得有用的信息，让其他的小伙伴即使使用正确的方式。好啦，就酱紫啦，小伙伴可以自己去动手尝试一下了~</p>

<blockquote>
  <p>PS:具体代码可以从<a href="https://github.com/NSCookies">Github</a>上获取。</p>
  
  <p>如有问题或纠正, 可以联系<a href="http://weibo.com/plutoy0504">@叫什么都不如叫Pluto-Y</a>或在<a href="https://github.com/NSCookies">Github</a></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[UIButton是一个类簇(Class Clusters)？]]></title><description><![CDATA[<p><a href="https://developer.apple.com/library/content/documentation/General/Conceptual/CocoaEncyclopedia/ClassClusters/ClassClusters.html#//apple_ref/doc/uid/TP40010810-CH4-SW1">类簇</a>用通俗一点讲就是一个<code>public</code>的抽象类加上一些<code>private</code>的私有类构成的，它是对一些实现细节进行隐藏，而对外公开的行为进行统一的一种设计。相信大家在平常工作中多少有注意到一些蛛丝马迹。例如我们常用的<code>NSNumber</code>, <code>NSArray</code>, <code>NSDictionary</code>以及<code>NSString</code>等，而这些都是总所周知。</p>

<p>然而就<code>UIButton</code>是不是类簇，本王就纠结了。To be or not to be, that's a question. 这时候就应该装逼了，搬出莎士比亚这句话。 顺带就带着这个来说说类簇的问题。关于为什么纠结呢？因为很多地方包括书籍都提到<code>UIButton</code>是类簇，而我再<a href="http://stackoverflow.com/questions/5045672/create-uibutton-subclass">Stack Overflow</a>却找到这样一段话:</p>

<blockquote>
  <p>UIButton is not a class cluster at all. A</p></blockquote>]]></description><link>http://www.nscookies.com/class-clusters/</link><guid isPermaLink="false">02baa22a-c6ed-412b-892b-fcd9b7d82f04</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Sun, 18 Sep 2016 13:45:00 GMT</pubDate><content:encoded><![CDATA[<p><a href="https://developer.apple.com/library/content/documentation/General/Conceptual/CocoaEncyclopedia/ClassClusters/ClassClusters.html#//apple_ref/doc/uid/TP40010810-CH4-SW1">类簇</a>用通俗一点讲就是一个<code>public</code>的抽象类加上一些<code>private</code>的私有类构成的，它是对一些实现细节进行隐藏，而对外公开的行为进行统一的一种设计。相信大家在平常工作中多少有注意到一些蛛丝马迹。例如我们常用的<code>NSNumber</code>, <code>NSArray</code>, <code>NSDictionary</code>以及<code>NSString</code>等，而这些都是总所周知。</p>

<p>然而就<code>UIButton</code>是不是类簇，本王就纠结了。To be or not to be, that's a question. 这时候就应该装逼了，搬出莎士比亚这句话。 顺带就带着这个来说说类簇的问题。关于为什么纠结呢？因为很多地方包括书籍都提到<code>UIButton</code>是类簇，而我再<a href="http://stackoverflow.com/questions/5045672/create-uibutton-subclass">Stack Overflow</a>却找到这样一段话:</p>

<blockquote>
  <p>UIButton is not a class cluster at all. A class cluster is represented by a public abstract class, that means no instance variables, with a bunch of private concrete subclasses that provide the implementation of the abstract methods of the abstract class. UIButton on the other hand is a concrete class, none of its methods is abstract, and it has instance variables to store the value you pass through its arguments. The only problematic part is that +buttonWithType can instantiate subclasses instead of UIButton directly, thus it can be seen as a factory method, not a class-cluster...</p>
</blockquote>

<p>然后我就懵逼了，根据类簇大体的概念我们知道至少说如果<code>UIButton</code>是一个抽象类的话，那么应该还存在一些<code>private</code>的私有类来实现具体的细节。那么我们的任务就是就是找到这些私有类，之前看了<a href="http://weibo.com/u/1364395395?topnav=1&amp;wvr=6&amp;topsug=1">@我就叫Sunny怎么了 </a>的一篇文章<a href="http://blog.sunnyxx.com/2014/12/18/class-cluster/">从NSArray看类簇</a>后发现至少<code>UIButton</code>中没办法用这种方法，于是就想说用LLDB断点一下，用了下面这条命令：</p>

<pre><code>breakpoint set -F '+[UIButton buttonWithType:]'  
</code></pre>

<p>得到了这样一段汇编：</p>

<pre><code>UIKit`+[UIButton buttonWithType:]:  
-&gt;  0x1063251e5 &lt;+0&gt;:    pushq  %rbp
..... // 省略
    0x106325297 &lt;+178&gt;:  movq   0x972c02(%rip), %rdi      ; (void *)0x0000000106cbabf8: UIButton
    0x10632529e &lt;+185&gt;:  movq   0x93cf6b(%rip), %rsi      ; "alloc"
    0x1063252a5 &lt;+192&gt;:  movq   0x9c0f34(%rip), %rbx      ; (void *)0x0000000105650800: objc_msgSend
    0x1063252ac &lt;+199&gt;:  callq  *%rbx
    0x1063252ae &lt;+201&gt;:  movq   0x93ce2b(%rip), %rsi      ; "initWithFrame:"
..... // 省略
    0x10632531c &lt;+311&gt;:  movq   0x97427d(%rip), %rdi      ; (void *)0x0000000106cbad10: UIPopoverButton
    0x106325323 &lt;+318&gt;:  movq   0x93cee6(%rip), %rsi      ; "alloc"
    0x10632532a &lt;+325&gt;:  movq   0x9c0eaf(%rip), %rbx      ; (void *)0x0000000105650800: objc_msgSend
    0x106325331 &lt;+332&gt;:  callq  *%rbx
    0x106325333 &lt;+334&gt;:  movq   0x9557a6(%rip), %rsi      ; "initWithFrame:buttonType:"
    0x10632533a &lt;+341&gt;:  movq   -0x38(%rbp), %rcx
..... // 省略
    0x1063253c5 &lt;+480&gt;:  movq   0x9741c4(%rip), %rdi      ; (void *)0x0000000106cbac48: UIRoundedRectButton
    0x1063253cc &lt;+487&gt;:  jmp    0x106325553               ; &lt;+878&gt;
    0x1063253d1 &lt;+492&gt;:  movb   %r14b, -0x81(%rbp)
    0x1063253d8 &lt;+499&gt;:  movq   0x972ac1(%rip), %rdi      ; (void *)0x0000000106cbabf8: UIButton
    0x1063253df &lt;+506&gt;:  jmp    0x106325553               ; &lt;+878&gt;
    0x1063253e4 &lt;+511&gt;:  movb   %r14b, -0x81(%rbp)
    0x1063253eb &lt;+518&gt;:  movq   %rbx, -0x80(%rbp)
    0x1063253ef &lt;+522&gt;:  movq   0x973262(%rip), %rdi      ; (void *)0x0000000106cb45a0: UINavigationButton
    0x1063253f6 &lt;+529&gt;:  movq   0x93ce13(%rip), %rsi      ; "alloc"
    0x1063253fd &lt;+536&gt;:  movq   0x9c0ddc(%rip), %rbx      ; (void *)0x0000000105650800: objc_msgSend
    0x106325404 &lt;+543&gt;:  callq  *%rbx
    0x106325406 &lt;+545&gt;:  movq   0x9556cb(%rip), %rsi      ; "initWithTitle:style:"
    0x10632540d &lt;+552&gt;:  xorl   %edx, %edx
    0x10632540f &lt;+554&gt;:  xorl   %ecx, %ecx
    0x106325411 &lt;+556&gt;:  jmp    0x106325475               ; &lt;+656&gt;
    0x106325413 &lt;+558&gt;:  movb   %r14b, -0x81(%rbp)
    0x10632541a &lt;+565&gt;:  movq   %rbx, -0x80(%rbp)
    0x10632541e &lt;+569&gt;:  movq   0x973233(%rip), %rdi      ; (void *)0x0000000106cb45a0: UINavigationButton
    0x106325425 &lt;+576&gt;:  movq   0x93cde4(%rip), %rsi      ; "alloc"
    0x10632542c &lt;+583&gt;:  movq   0x9c0dad(%rip), %rbx      ; (void *)0x0000000105650800: objc_msgSend
    0x106325433 &lt;+590&gt;:  callq  *%rbx
    0x106325435 &lt;+592&gt;:  movq   0x95569c(%rip), %rsi      ; "initWithTitle:style:"
    0x10632543c &lt;+599&gt;:  xorl   %edx, %edx
    0x10632543e &lt;+601&gt;:  movl   $0x1, %ecx
    0x106325443 &lt;+606&gt;:  jmp    0x106325475               ; &lt;+656&gt;
    0x106325445 &lt;+608&gt;:  movb   %r14b, -0x81(%rbp)
    0x10632544c &lt;+615&gt;:  movq   %rbx, -0x80(%rbp)
    0x106325450 &lt;+619&gt;:  movq   0x973201(%rip), %rdi      ; (void *)0x0000000106cb45a0: UINavigationButton
    0x106325457 &lt;+626&gt;:  movq   0x93cdb2(%rip), %rsi      ; "alloc"
    0x10632545e &lt;+633&gt;:  movq   0x9c0d7b(%rip), %rbx      ; (void *)0x0000000105650800: objc_msgSend
    0x106325465 &lt;+640&gt;:  callq  *%rbx
    0x106325467 &lt;+642&gt;:  movq   0x95566a(%rip), %rsi      ; "initWithTitle:style:"
..... // 省略
    0x10632549e &lt;+697&gt;:  movq   0x9740f3(%rip), %rdi      ; (void *)0x0000000106cbacc0: UITexturedButton
    0x1063254a5 &lt;+704&gt;:  jmp    0x106325553               ; &lt;+878&gt;
    0x1063254aa &lt;+709&gt;:  movb   %r14b, -0x81(%rbp)
    0x1063254b1 &lt;+716&gt;:  movq   %rbx, -0x80(%rbp)
    0x1063254b5 &lt;+720&gt;:  movq   0x9740d4(%rip), %rdi      ; (void *)0x0000000106cbac48: UIRoundedRectButton
    0x1063254bc &lt;+727&gt;:  movq   0x93cd4d(%rip), %rsi      ; "alloc"
    0x1063254c3 &lt;+734&gt;:  movq   0x9c0d16(%rip), %r14      ; (void *)0x0000000105650800: objc_msgSend
..... // 省略
    0x106325538 &lt;+851&gt;:  movq   0x974069(%rip), %rdi      ; (void *)0x0000000106cbad60: _UIPlacardButton
    0x10632553f &lt;+858&gt;:  jmp    0x106325553               ; &lt;+878&gt;
    0x106325541 &lt;+860&gt;:  movb   %r14b, -0x81(%rbp)
    0x106325548 &lt;+867&gt;:  movq   %rbx, -0x80(%rbp)
    0x10632554c &lt;+871&gt;:  movq   0x97405d(%rip), %rdi      ; (void *)0x0000000106cbadb0: _UIShortPlacardButton
    0x106325553 &lt;+878&gt;:  movq   0x93ccb6(%rip), %rsi      ; "alloc"
    0x10632555a &lt;+885&gt;:  movq   0x9c0c7f(%rip), %rbx      ; (void *)0x0000000105650800: objc_msgSend
    0x106325561 &lt;+892&gt;:  callq  *%rbx
    0x106325563 &lt;+894&gt;:  movq   0x93cb76(%rip), %rsi      ; "initWithFrame:"
..... // 省略
</code></pre>

<p>看到这里小伙伴不要害怕，吃口粑粑冷静一下。我不是要大家看每一句话是什么意思，大家可以注意一下每一行最后又<code>Button</code>关键字的地方，会发现一堆我们平时没见过的一些<code>Button</code>,如<code>UIPopoverButton</code>, <code>UIRoundedRectButton</code>, <code>UINavigationButton</code>, <code>UITexturedButton</code>, <code>_UIPlacardButton</code>以及<code>_UIShortPlacardButton</code>等。至少我们发现了我们所说的<code>private</code>的私有类，那么我们就可以知道其实这里的<code>UIButton</code>是一个类簇。</p>

<p>那么我们也用官网的图来说说类簇的用处：</p>

<p><img src="https://developer.apple.com/library/content/documentation/General/Conceptual/CocoaEncyclopedia/Art/cluster3.gif" alt="NSNumber"></p>

<p>可以从上图看到虽然官方的实现细节根据不同的类型有不同的实现，但是我们所需要记得以及交互的接口都可以通过<code>NSNumer</code>来进行交互。而如果我们自己在设计类簇的时候也可能通过这种方式来进行隐藏一些细节的实现。</p>

<p>但是在设计自己的类簇的过程中需要注意一下几点：</p>

<ul>
<li>首先要定义好抽象基类</li>
<li>其次需要指明子类需要重写的方法</li>
<li>最后提供一个比较好的文档说明方面其他人读写</li>
</ul>

<p>最后就到这吧，小伙伴各回各家，各找各妈吧~</p>

<blockquote>
  <p>PS:具体代码可以从<a href="https://github.com/NSCookies">Github</a>上获取。</p>
  
  <p>如有问题或纠正, 可以联系<a href="http://weibo.com/plutoy0504">@叫什么都不如叫Pluto-Y</a>或在<a href="https://github.com/NSCookies">Github</a></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[对象关联(Associated Object)]]></title><description><![CDATA[<p>经常看片的小伙伴肯定听一群坏蛋说过这一句话，我们还会回来的~~咳咳，不好意思，说错了，是我们是绑在一条绳子上的蚂蚱。而这个跟我们今天说的主题有点像，因为我们是要将一个对象绑定到另一个对象上~</p>

<p>那么不废话，新来看看今天的素材都有哪些吧：</p>

<pre><code>// &lt;objc/runtime.h&gt;
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)  
id objc_getAssociatedObject(id object, const void *key)  
void objc_removeAssociatedObjects(id object)  
</code></pre>

<p>首先可以看到最开始的<code>&lt;objc/runtime.h&gt;</code>知道如果要用要建立关联的话，首先要引入这个头文件。其次，</p>]]></description><link>http://www.nscookies.com/associated-object/</link><guid isPermaLink="false">702a0702-5bbe-42ca-b863-7234656faf4a</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Fri, 09 Sep 2016 00:49:22 GMT</pubDate><content:encoded><![CDATA[<p>经常看片的小伙伴肯定听一群坏蛋说过这一句话，我们还会回来的~~咳咳，不好意思，说错了，是我们是绑在一条绳子上的蚂蚱。而这个跟我们今天说的主题有点像，因为我们是要将一个对象绑定到另一个对象上~</p>

<p>那么不废话，新来看看今天的素材都有哪些吧：</p>

<pre><code>// &lt;objc/runtime.h&gt;
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)  
id objc_getAssociatedObject(id object, const void *key)  
void objc_removeAssociatedObjects(id object)  
</code></pre>

<p>首先可以看到最开始的<code>&lt;objc/runtime.h&gt;</code>知道如果要用要建立关联的话，首先要引入这个头文件。其次，在iOS4以及OS X 10.6以后提供了这三个API，而这三个API有点类似NSDictionary(或者Dictionary)的key:value模式，只不过这里需要指定需要绑定哪个对象上。</p>

<p>这样说好像有点乏味，我们拿一个开源库里面最牛逼库之一的<a href="https://github.com/AFNetworking/AFNetworking">AFN</a>里面的代码来看下:</p>

<pre><code>@interface UIImageView (_AFNetworking)
@property (readwrite, nonatomic, strong, setter = af_setActiveImageDownloadReceipt:) AFImageDownloadReceipt *af_activeImageDownloadReceipt;
@end

@implementation UIImageView (_AFNetworking)

- (AFImageDownloadReceipt *)af_activeImageDownloadReceipt {
    return (AFImageDownloadReceipt *)objc_getAssociatedObject(self, @selector(af_activeImageDownloadReceipt));
}

- (void)af_setActiveImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt {
    objc_setAssociatedObject(self, @selector(af_activeImageDownloadReceipt), imageDownloadReceipt, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end
</code></pre>

<p>可以看得出，这里使用了<code>Associated Object</code>为UIImageView的Category添加了一个新的属性。其实这个也是<code>Associated Object</code>最常用的场景，就是为Category添加属性。总所周知，在Objective-C中的Category是没有办法添加新的属性，这个可以说是一个非常麻烦的地方，因为我们经常为一个已经存在的类中希望添加一个属性来完善这个类新添加的功能。</p>

<p>But，苹果不如我们意啊，竟然不给我们权限为Category添加属性，然后程序员是伟大的，老是能找到方法搞小动作。</p>

<p>其实整体用法上面就可以看到了，这里最后说一句，关于其中的key，通常的做法有两种，一种是通过属性的getter方法的@selector作为key（比如上面的方式），还有一种方式通过一个静态变量的地址作为key，比如下面的方式：</p>

<pre><code>static char kAssociatedObjectKey;  
objc_getAssociatedObject(self, &amp;kAssociatedObjectKey);  
</code></pre>

<p>好了，到此为止吧。好像这节没什么代码~别打我哟</p>

<blockquote>
  <p>PS:具体代码可以从<a href="https://github.com/NSCookies">Github</a>上获取。</p>
  
  <p>如有问题或纠正, 可以联系<a href="http://weibo.com/plutoy0504">@叫什么都不如叫Pluto-Y</a>或在<a href="https://github.com/NSCookies">Github</a></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[枚举（Enum）]]></title><description><![CDATA[<p>枚举估计是大家工作中经常打交道的一种类型，那么在OC和Swift中我们经常如何用它的呢？综合多方面的使用，我们大体可以将枚举分为两种方式，一种是普通的枚举，还有一种不普通的枚举(我屮艸芔茻，你这不是废话吗~)，诶，第二种应该说是枚举中的各种可能同时存在，如果换成代码的语言就是枚举中的各个值有可能进行位操作运算的。那么在代码里面分别如何表示呢？</p>

<p>OC中将这两种分别用<code>NS_ENUM</code>与<code>NS_OPTIONS</code>来区分，而Swift中则用普通的enum以及<code>OptionSetType</code>来搞定。那么该如何使用以及在使用过程中有什么需要注意的呢？且听下回分解(分解你大爷啊！)</p>

<p>咳咳，首先我们先来看看<code>NS_ENUM</code>以及<code>NS_OPTIONS</code>是个什么东东吧~</p>

<pre><code>#if (__cplusplus &amp;&amp; __cplusplus &gt;= 201103L      \
        &amp;&amp; (__has_extension(cxx_strong_enums)   \
        || __has_feature(objc_</code></pre>]]></description><link>http://www.nscookies.com/enum/</link><guid isPermaLink="false">24b1c0cc-201b-4742-987b-808233477879</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Mon, 05 Sep 2016 10:37:43 GMT</pubDate><content:encoded><![CDATA[<p>枚举估计是大家工作中经常打交道的一种类型，那么在OC和Swift中我们经常如何用它的呢？综合多方面的使用，我们大体可以将枚举分为两种方式，一种是普通的枚举，还有一种不普通的枚举(我屮艸芔茻，你这不是废话吗~)，诶，第二种应该说是枚举中的各种可能同时存在，如果换成代码的语言就是枚举中的各个值有可能进行位操作运算的。那么在代码里面分别如何表示呢？</p>

<p>OC中将这两种分别用<code>NS_ENUM</code>与<code>NS_OPTIONS</code>来区分，而Swift中则用普通的enum以及<code>OptionSetType</code>来搞定。那么该如何使用以及在使用过程中有什么需要注意的呢？且听下回分解(分解你大爷啊！)</p>

<p>咳咳，首先我们先来看看<code>NS_ENUM</code>以及<code>NS_OPTIONS</code>是个什么东东吧~</p>

<pre><code>#if (__cplusplus &amp;&amp; __cplusplus &gt;= 201103L      \
        &amp;&amp; (__has_extension(cxx_strong_enums)   \
        || __has_feature(objc_fixed_enum))      \
    ) ||                                        \
    (!__cplusplus &amp;&amp; __has_feature(objc_fixed_enum))
        #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
    #if (__cplusplus)
        #define NS_OPTIONS(_type, _name) _type _name; enum : _type
    #else
        #define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type
    #endif
#else
    #define NS_ENUM(_type, _name) _type _name; enum
    #define NS_OPTIONS(_type, _name) _type _name; enum
#endif
</code></pre>

<p>其实呢，他就是一个宏定义。那么我们来一个一个的分析每个宏定义是如何生效的。假设我们写下下面这段话:</p>

<pre><code>typedef NS_ENUM(NSUInteger, MyNSEnum) {  
    MyNSEnumValue1,
    MyNSEnumValue2,
    MyNSEnumValue3,
    MyNSEnumValue4
};
</code></pre>

<p>那么通过上面的宏定义应该就会变成下面这段话:</p>

<pre><code>// 情况一：如果满足上面一堆的条件
typedef enum MyNSEnum : NSUInteger;  
enum MyNSEnum : NSUInteger {  
    //具体内容
};
// 情况二：如果不满足上面一堆的条件
typedef NSUInteger MyNSEnum;  
enum {  
    //具体内容
};
</code></pre>

<p>可以看出来<code>NS_ENUM</code>是做到向下兼容了，在一些低版本的，不兼容一些特性的情况下则用情况二的方式来定义枚举，而在新版本的情况下，则采用情况一来定义。当然对于上面一些条件，有兴趣的小伙伴可以自己查查资料了解一下。</p>

<p>估计还有一部分小伙伴习惯直接用enum之类的来定义枚举，但是还是建议大家尽量用<code>NS_ENUM</code>以及<code>NS_OPTIONS</code>，毕竟苹果提供了这个新的特性，而且它也支持了向下兼容。并且现在大家的都习惯性用这个，如果写这个也方面大家读懂以及直观看懂是<code>NS_ENUM</code>还是<code>NS_OPTIONS</code>。而且这也是目前许多网站和数据推荐的做法，也是一个共识。（路人甲：我就不用，我就不用，你特么来打我啊~）额，你老大，你了不起咯。</p>

<p>那么除了这个好处以外还有没有什么其他的好处呢？在switch块中，我们如果用到的是<code>NS_ENUM</code>的话，尽量不要用<code>default:</code>，这样的话如果没有写全所有的枚举类型的话，会出现warning，这样也方便了我们如果在使用<code>枚举+switch</code>的过程中，如果添加了一个新的枚举的值，那么同时也会出现warning。就像下图:</p>

<p><img src="http://www.nscookies.com/content/images/2016/09/enum-warning.png" alt="enum-warning.png"></p>

<p>然后普通的枚举就尽量用<code>NS_ENUM</code>。对于那些可以同时存在的，比如一些配置项，一些控制某些事件的开关之类的枚举我们就尽量用<code>NS_OPTIONS</code>，例如我们在用<code>NSCalendarUnit</code>，还有动画里面的<code>UIViewAnimationOptions</code>以及<code>Autoresizing</code>里面用到的<code>UIViewAutoresizing</code>都是<code>NS_OPTIONS</code>类型，而这种类型我们经常都会用位操作符来进行控制。</p>

<p>我们从UIKit中看下<code>UIViewAutoresizing</code>吧，代码如下:</p>

<pre><code>typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {  
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 &lt;&lt; 0,
    UIViewAutoresizingFlexibleWidth        = 1 &lt;&lt; 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 &lt;&lt; 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 &lt;&lt; 3,
    UIViewAutoresizingFlexibleHeight       = 1 &lt;&lt; 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 &lt;&lt; 5
};
</code></pre>

<p>而通常<code>NS_OPTIONS</code>都是这样定义的，即通过所需的类型在二进制中的某一个位置的不为0来进行操作，这样也方便了我们通过或(或者与)来进行控制那些属性是需要的，那些属性不需要的来进行不同的操作。</p>

<p>看完OC中的做法，那么我们来看看Swift中是怎么操作的呢？估计普通的枚举大家都用到烂了，就是enum了，那我就不多说废话了，对于<code>NS_OPTIONS</code>在Swift中的替代方案是什么呢？</p>

<p>如果用过Swift1.2的小伙伴估计想到了以前用的Xcode snippet，通常都是这个:</p>

<pre><code>struct &lt;# Options #&gt; : RawOptionSetType, BooleanType {  
    let rawValue: UInt
    init(nilLiteral: ()) { self.value = 0 }
    init(_ value: UInt = 0) { self.value = value }
    init(rawValue value: UInt) { self.value = value }
    var boolValue: Bool { return value != 0 }
    var rawValue: UInt { return value }
    static var allZeros: &lt;# Options #&gt; { return self(0) }

    static var None: &lt;# Options #&gt;         { return self(0b0000) }
    static var &lt;# Option #&gt;: &lt;# Options #&gt;     { return self(0b0001) }
    // ...
}
</code></pre>

<p>看到这里，估计小伙伴可以体会到在1.2的时候那酸爽真是够了，如果自己手写一行一行的敲，估计好浪费时间啊。所以之前都是通过用Xcode snippet来进行操作的。而在2.2中我们有事如何写的呢？我们来尝试一下：</p>

<pre><code>struct NSCOptions : OptionSetType {  
    let rawValue: UInt
    static let None = NSCOptions(rawValue: 0)
    static let Value1 = NSCOptions(rawValue: 0b0001)
    static let Value2 = NSCOptions(rawValue: 0b0010)
    static let Value3 = NSCOptions(rawValue: 0b0100)
    static let Value4 = NSCOptions(rawValue: 0b1000)
}
</code></pre>

<p>发现没相对Swift1.2简洁了不是一点两点，是不是很高兴。好哒，妹子上来亲我一口吧~（pia~流氓）。咳咳，言归正传，是不是比之前方便很多，那么我们来看下<code>OptionSetType</code>是什么鬼？</p>

<p><img src="http://www.nscookies.com/content/images/2016/09/OptionSetType.png" alt="OptionSetType.png"></p>

<p>可以看得出来左边这个就是我们经常enum中用来创建的构造方法，右边这个从最上面说起，上面的<code>Equatable</code>是用来判等用的，也就是我们经常用的<code>!=</code>和<code>==</code>的协议，而右边这个则是我们之前说过的字面量里面的数组字面量(关于字面量一节小伙伴们可以看看<a href="http://www.nscookies.com/enum/">字面量(Literal)</a>)，也就是我们可以用<code>[]</code>来表示一组Options的集合，而不是通过<code>|</code>来表达，这样用起来也更加人性化。而<code>SetAlgebraType</code>则提供了集合的操作，比如并集，交集，异或，插入等等。这里就不对他们进行深究了，放过他们吧。俗话说得好，冤冤相报何时了~</p>

<p>好了，最终关于枚举我们就说到这里吧，剩下的小伙伴们可以自己去玩玩。不过别玩出火哟~</p>

<blockquote>
  <p>PS:具体代码可以从<a href="https://github.com/NSCookies">Github</a>上获取。</p>
  
  <p>如有问题或纠正, 可以联系<a href="http://weibo.com/plutoy0504">@叫什么都不如叫Pluto-Y</a>或在<a href="https://github.com/NSCookies">Github</a></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[链式编程(Method chaining)]]></title><description><![CDATA[<p>所谓的链式编程呢，就是像链子一样一环扣一环，在编程中的体现呢，就是一个方法接着一个方法的调用。如果你还不懂那就想想<a href="https://github.com/SnapKit/Masonry">Masonry</a>，<code>make.width.greaterThanOrEqualTo(@200);</code>类似这种代码。这样就懂了吧，我的小乖乖~</p>

<p>记得以前学Java的时候最喜欢这种不断点方法调用了，以前一直在想如果在OC中要实现链式编程的话会是怎么样子的呢？嗯~大概应该是这样的吧:</p>

<pre><code>// str是一个NSString
[[str substringFromIndex:10] substringToIndex:12];
</code></pre>

<p>想到这个我真的是日了🐶了，要让我不断用空格来调用，OMG，算了吧。知道后来Block的出现，我看到了曙光，因为我看到了Block竟然可以用<code>()</code>来调用Block，终于又看到了我可爱的<code>()</code>了。</p>

<p>但是接下来我还是一步一步的来看看如何做。那么我们假设我们现在要在OC中实现链式编程，那么最直接的做法就是在方法调用后直接返回对应的对象即可，那么假设我们有一个<code>NSCStudent</code>对象，他有<code>study</code>和<code>play</code>的方法，如果按照OC的方法来进行链式的话，应该就是这样<code>[[student study] play]</code></p>]]></description><link>http://www.nscookies.com/method-chaining/</link><guid isPermaLink="false">acaa5dcd-9e03-4464-bbd7-6ea4fd47fadc</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Tue, 30 Aug 2016 19:54:00 GMT</pubDate><content:encoded><![CDATA[<p>所谓的链式编程呢，就是像链子一样一环扣一环，在编程中的体现呢，就是一个方法接着一个方法的调用。如果你还不懂那就想想<a href="https://github.com/SnapKit/Masonry">Masonry</a>，<code>make.width.greaterThanOrEqualTo(@200);</code>类似这种代码。这样就懂了吧，我的小乖乖~</p>

<p>记得以前学Java的时候最喜欢这种不断点方法调用了，以前一直在想如果在OC中要实现链式编程的话会是怎么样子的呢？嗯~大概应该是这样的吧:</p>

<pre><code>// str是一个NSString
[[str substringFromIndex:10] substringToIndex:12];
</code></pre>

<p>想到这个我真的是日了🐶了，要让我不断用空格来调用，OMG，算了吧。知道后来Block的出现，我看到了曙光，因为我看到了Block竟然可以用<code>()</code>来调用Block，终于又看到了我可爱的<code>()</code>了。</p>

<p>但是接下来我还是一步一步的来看看如何做。那么我们假设我们现在要在OC中实现链式编程，那么最直接的做法就是在方法调用后直接返回对应的对象即可，那么假设我们有一个<code>NSCStudent</code>对象，他有<code>study</code>和<code>play</code>的方法，如果按照OC的方法来进行链式的话，应该就是这样<code>[[student study] play]</code>, 那么<code>NSCStudent</code>的方法实现大概应该是这样：</p>

<pre><code>@implementation NSCStudent

- (instancetype)study {
    NSLog(@"The student study");
    return self;
}

- (instancetype)play {
    NSLog(@"The student play");
    return self;
}

@end
</code></pre>

<p>这样我们就可以用<code>[[student study] play]</code>来调用了。（路人甲：“不对啊，我试过，如果是这样实现也可以用student.study.play来调用啊”）保安，保安，有人来打乱，拖出去乱棍打死~。 好吧，我承认如果是这种实现可以，那么如果带参数的情况下呢，那你是不是日了🐶了。</p>

<p>其实这种方式也没有什么，就是'['和']'特别多，有的时候都分不清楚哪一层是哪一层了，这个是我我就怀念起了Java里面通过'.'来调用方法了。那么接下来我们想想如何像<code>Mansory</code>通过'.'和'()'来实现一些带参数的链式编程呢？</p>

<p>其实上面也提到了，既然Block可以通过'()'来调用，并且我们也能通过'.'来调用不带参数的方法，那么我们通过写一个不带参数的方法，并且返回Block，且把原来的参数放到Block中即可。我们把上面的<code>NSCStudent</code>中添加一个方法<code>playWith</code>并且返回Block，那么代码应该如下：</p>

<pre><code>- (NSCStudent * (^)(NSString *))playWith {
    return ^id(NSString *name) {
        NSLog(@"The student play with %@", name);
        return self;
    };
}
</code></pre>

<p>接下来我们来分析一下如果我写了<code>student.study.playWith</code>的话会出现什么，当然首先肯定是<code>study</code>还是跟原来一样，那么调用<code>playWith</code>之后将返回一个Block，并且Block需要一个<code>NSString</code>的参数。那么按照之前的理论就是可以通过'()'来调用Block的话，我们可以这样写<code>student.study.playWith(@"Girl")</code>，并且从代码中我们可以看得出来调用Block的结果是返回了一个<code>NSCStudent</code>的Block。那么酱紫的话，劳资是不是有可以继续点来调用<code>NSCStudent</code>的方法了。</p>

<p>那么我就可以<code>student.study.playWith(@"Girl1").playWith(@"Girl2").playWith(@"Girl3").playWith(@"Girl4").playWith(@"Girl5")....</code>，我去，那么不是我可以群*了，咳咳，说错了，是我就可以无限调用函数了，其实就是这么简单。</p>

<p>那么如果在Swift中要实现链式编程的话，就更特么简单了。毕竟Swift就是通过'.'和'()'来调用的，只要在方法后返回自身就好了。那么我们来稍微看下代码吧:</p>

<pre><code>struct NSCStudent {  
    func study() -&gt; NSCStudent {
        print("The student study")
        return self
    }

    func playWith(name: String) -&gt; NSCStudent {
        print("The student play with \(name)")
        return self
    }
}

let student = NSCStudent()  
student.study().playWith("Girl")  
</code></pre>

<p>好了，就酱紫吧，各位小老婆们伺候我就寝吧~</p>

<blockquote>
  <p>PS:具体代码可以从<a href="https://github.com/NSCookies">Github</a>上获取。</p>
  
  <p>如有问题或纠正, 可以联系<a href="http://weibo.com/plutoy0504">@叫什么都不如叫Pluto-Y</a>或在<a href="https://github.com/NSCookies">Github</a></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[#pragma与 // MARK:]]></title><description><![CDATA[<p>我去，就这两个东西还要讲？是OC或Swift开发人员都知道是怎么回事好吗?不就是用来标记和分组代码的吗？难道还有别的装逼技能？</p>

<p>当然，其实问大部分人说这两个是什么作用，或者是除了这两个还知道什么的情况下。很多人都只知道这两个是用来组织代码的。然而这样说也对也不对，确实我们常用的情况是都是用这两个来组织代码的，但是如果看过很多开源的人肯定看过一下的形式的代码：</p>

<pre><code>#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
.... //一些代码
#pragma clang diagnostic pop
</code></pre>

<p>那么这些代码是拿来干嘛的呢？且听我慢慢道来。</p>

<p>确实在我们日常工作中，经常用到<code>#pragma mark</code>或者<code>// MARK:</code>来组织代码，并且大家都知道组织代码有两种形式: 一种是用来分组，一种是用来标注。具体如何展示大家也当我废话，看下下面的代码吧：</p>

<pre><code>// Objective-C 代码
- (void)viewDidLoad {
    [super viewDidLoad];
    [self initAll];
}

#pragma</code></pre>]]></description><link>http://www.nscookies.com/pargma-mark/</link><guid isPermaLink="false">fcf0bfbb-5f8d-4901-9987-e8ff6699e159</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Fri, 26 Aug 2016 11:45:32 GMT</pubDate><content:encoded><![CDATA[<p>我去，就这两个东西还要讲？是OC或Swift开发人员都知道是怎么回事好吗?不就是用来标记和分组代码的吗？难道还有别的装逼技能？</p>

<p>当然，其实问大部分人说这两个是什么作用，或者是除了这两个还知道什么的情况下。很多人都只知道这两个是用来组织代码的。然而这样说也对也不对，确实我们常用的情况是都是用这两个来组织代码的，但是如果看过很多开源的人肯定看过一下的形式的代码：</p>

<pre><code>#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
.... //一些代码
#pragma clang diagnostic pop
</code></pre>

<p>那么这些代码是拿来干嘛的呢？且听我慢慢道来。</p>

<p>确实在我们日常工作中，经常用到<code>#pragma mark</code>或者<code>// MARK:</code>来组织代码，并且大家都知道组织代码有两种形式: 一种是用来分组，一种是用来标注。具体如何展示大家也当我废话，看下下面的代码吧：</p>

<pre><code>// Objective-C 代码
- (void)viewDidLoad {
    [super viewDidLoad];
    [self initAll];
}

#pragma mark Custom functions
- (void)initAll {
    ...
}

#pragma mark - UITableViewDataSources
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 10;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    return [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"CELL"];
}

#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"点击了%li行", indexPath.row);
}
</code></pre>

<p>下面是<code>Swift</code>代码：</p>

<pre><code>// Swift 代码
override func viewDidLoad() {  
    super.viewDidLoad()
    initAll()
}

// MARK: Custom function
private func initAll() {  
    ...
}

// MARK: - UITableViewDataSource
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {  
    return 10
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -&gt; UITableViewCell {  
    return UITableViewCell(style: .Default, reuseIdentifier: "CELL")
}

// MARK: - UITableViewDelegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {  
    print("点击了\(indexPath.row)行")
}
</code></pre>

<p>那么效果呢，大家也都知道，就是下图这样：</p>

<p><img src="http://www.nscookies.com/content/images/2016/08/pragma.png" alt="pragma.png"></p>

<p>其实主要就是两点，一个就是后面没有跟'-'的情况，这种情况就不会出现分组，只是当单纯的有注释，另一种就是带'-'的情况，那么这种情况就会添加一条水平分割线，也就是出现了分组。那么在我们写代码的过程中最好将代码相关性比较强的都分到同一个组别当中去。这样针对以后维护的人员对代码的也能跟快读懂代码。这也就是我们常说的要做注释，增加代码的可读性。</p>

<p>关于这个的用法就不多说了，大家去试试，并且大部分都知道这个特性。那么我们围绕着这个说说其他的特性吧，首先我们先来说说一个可以装逼的神器，就是用它来局部的控制某个警告的开启与关闭，也就是我们文章开头说的那个：</p>

<pre><code>#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
// 一段代码
#pragma clang diagnostic pop
</code></pre>

<p>估计很多小伙伴经常在很多开源代码里面可以看到这段代码，这个代码呢，其实就用来关闭警告的，这样在你有些写一些代码的时候需要忽略某些警告的时候特别有用。具体用法我们就用上面这个来试试吧。</p>

<pre><code>#pragma mark - UITableViewDataSources
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
    int unusedInt;
#pragma clang diagnostic pop
    return 10;
}
</code></pre>

<p>通过下面这段代码忽略没有使用<code>unsedInt</code>这个属性的警告了。那么问题来了，就有人问了：“我怎么知道后面那个"-Wunsed-variable"这个字符串应该是什么呢”？想知道吗？你猜啊，就不告诉你就不告诉你，就不告诉你！(pia~一脱鞋过来，这是三天不打上房揭瓦！)</p>

<p>好吧，其实有两种手段，一种就是网上有人整理了一个网站<a href="http://fuckingclangwarnings.com/">Which Clang Warning Is Generating This Message?</a>，里面包含了许多警告抑制的字符串，大家可以参考一下。另外一个方案呢，就是通过Xcode来查找，具体步骤如下图:</p>

<p><img src="http://www.nscookies.com/content/images/2016/08/find-warning-string.png" alt="find-warning-string.png"></p>

<p>先选择<code>Report Navigator</code>，然后选择最后一个日志，选择所有信息，然后去日志中查看你需要的警告信息，并且在哪里警告它都会罗列出来。通过这种手段，我们就可以装逼添加<code>#pragma clang diagnostic ignored</code>的信息了。</p>

<p>当然对于上面这种特殊的为使用的警告，还可以用下面这种形式来关闭警告：</p>

<pre><code>// Objective-C 代码
NSString *unsedString;  
#pragma unused(unsedString)
</code></pre>

<p>好了，学了这一招就可以在自己代码里面装逼了，那么我们在说说OC中还有哪些好用的功能呢？竟然说到警告，那么我们可以在说如何自己手动的产生警告吧，有两种方式，具体可以看下面的代码:</p>

<pre><code>#pragma message "This is a warning"
#warning "This is another warning"
</code></pre>

<p>具体效果如何呢？看看下面这张图:</p>

<p><img src="http://www.nscookies.com/content/images/2016/08/warnings.png" alt="warnings.png"></p>

<p>是不是觉得很神奇，其实在我们日常工作中可以通过这种代码为暂时还没有打算好填写内容的地方打上一个warnings,然后这样我们在我们写代码的过程中就可以注意到这些warning，这样之后我们就可以来填坑了。毕竟我是一个有代码洁癖的人，对于自己代码中有很多警告是完全不能够忍受的，希望大家也能养成这个习惯。  其实除了警告以外，还有一个<code>#error</code>可以用，大家可以尝试一下。</p>

<p>下面我们来说说在<code>Swift</code>中除了<code>MARK</code>以外我们还可以用什么呢：其实还有两个我们可以用的，在大家写Swift过程中可以用到<code>// TODO:</code>与<code>// FIXME:</code>相当于OC中只有警告来指定，我更喜欢Swift中的这种用法，因为它的目的性更明确，其用法与<code>MARK</code>一致，具体用法和效果就看下面吧:</p>

<p><img src="http://www.nscookies.com/content/images/2016/08/swift-TODO-FIXME.png" alt="swift-TODO-FIXME.png"></p>

<p>具体内容就到此为止吧，深入的地方就等你们告诉我了哟。感谢大家的阅读，退下吧，朕要洗白白去了。</p>

<blockquote>
  <p>PS:具体代码可以从<a href="https://github.com/NSCookies">Github</a>上获取。</p>
  
  <p>如有问题或纠正, 可以联系<a href="http://weibo.com/plutoy0504">@叫什么都不如叫Pluto-Y</a>或在<a href="https://github.com/NSCookies">Github</a></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[字面量(Literal)]]></title><description><![CDATA[<p>所谓的字面量就是其本身就涵盖自己本身的值以及能包含自己的类型的值，比如我们常见的有数字，字符串以及布尔值，就像下面这些:</p>

<pre><code>// Objective-C
NSString *aString = @"Hello";  
NSNumber *aNumber = @5;  
BOOL aBool = YES;  
NSArray *arr = @[@"Hello", @"World"];  
NSDictionary *dic = @{@"key1": @"value1", @"key2": @"value2"};

// Swift
let aString = "World"  
let aNumber = 6  
let aBool = false  
let arr = ["Hello", "World"]  
let dic = ["key1": "value1", "key2": "value2"]  
</code></pre>

<p>字面量在我们工作中几乎是无处不在，然而其实<code>Objective-C</code>与<code>Swift</code>中这两个的区别还是挺大的。</p>]]></description><link>http://www.nscookies.com/literal/</link><guid isPermaLink="false">79561300-cbf3-48c7-8a1f-9c1ea0e4a83e</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Sun, 21 Aug 2016 17:48:00 GMT</pubDate><content:encoded><![CDATA[<p>所谓的字面量就是其本身就涵盖自己本身的值以及能包含自己的类型的值，比如我们常见的有数字，字符串以及布尔值，就像下面这些:</p>

<pre><code>// Objective-C
NSString *aString = @"Hello";  
NSNumber *aNumber = @5;  
BOOL aBool = YES;  
NSArray *arr = @[@"Hello", @"World"];  
NSDictionary *dic = @{@"key1": @"value1", @"key2": @"value2"};

// Swift
let aString = "World"  
let aNumber = 6  
let aBool = false  
let arr = ["Hello", "World"]  
let dic = ["key1": "value1", "key2": "value2"]  
</code></pre>

<p>字面量在我们工作中几乎是无处不在，然而其实<code>Objective-C</code>与<code>Swift</code>中这两个的区别还是挺大的。<code>Objective-C</code>几乎不可以自定义字面量转换成类型，毕竟<code>OC</code>中的字面量转换成类型是在<code>Clang</code>中进行支持的。而在<code>Swift</code>中是通过一些列的<code>字面量转换协议</code>(<code>Literal Convertible Protocols</code>)来进行字面量转换成具体类型就好了。所以<code>Swift</code>中我们可以通过自定义某一个字面量转换成具体类型。</p>

<p>那么<code>Objective-C</code>是如何实现这些，我们一次来说吧，就说说这些常见的字面量。</p>

<blockquote>
  <p>这里我们通过<code>clang -rewrite-objc NSCLiteralController.m</code>来进行看看<code>Clang</code>都为我们做了什么</p>
</blockquote>

<p>我们来看看结果吧:</p>

<pre><code>#ifndef __NSCONSTANTSTRINGIMPL
struct __NSConstantStringImpl {  
  int *isa;
  int flags;
  char *str;
#if _WIN64
  long long length;
#else
  long length;
#endif
};
#ifdef CF_EXPORT_CONSTANT_STRING
extern "C" __declspec(dllexport) int __CFConstantStringClassReference[];  
#else
__OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[];  
#endif
#define __NSCONSTANTSTRINGIMPL

static __NSConstantStringImpl __NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_1 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"Hello",5};  
static __NSConstantStringImpl __NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_2 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"Hello",5};  
static __NSConstantStringImpl __NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_3 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"World",5};  
static __NSConstantStringImpl __NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_4 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"key1",4};  
static __NSConstantStringImpl __NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_5 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"value1",6};  
static __NSConstantStringImpl __NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_6 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"key2",4};  
static __NSConstantStringImpl __NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_7 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"value2",6};


// 真实代码通过转换后的c++源码
NSString *aString = (NSString *)&amp;__NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_1;  
NSNumber *aNumber = ((NSNumber *(*)(Class, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 5);  
BOOL aBool = ((bool)1);  
NSArray *arr = ((NSArray *(*)(Class, SEL, const ObjectType *, NSUInteger))(void *)objc_msgSend)(objc_getClass("NSArray"), sel_registerName("arrayWithObjects:count:"), (const id *)__NSContainer_literal(2U, (NSString *)&amp;__NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_2, (NSString *)&amp;__NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_3).arr, 2U);  
NSDictionary *dic = ((NSDictionary *(*)(Class, SEL, const ObjectType *, const id *, NSUInteger))(void *)objc_msgSend)(objc_getClass("NSDictionary"), sel_registerName("dictionaryWithObjects:forKeys:count:"), (const id *)__NSContainer_literal(2U, (NSString *)&amp;__NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_5, (NSString *)&amp;__NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_7).arr, (const id *)__NSContainer_literal(2U, (NSString *)&amp;__NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_4, (NSString *)&amp;__NSConstantStringImpl__var_folders_fm_xbb226bx49n28m4sg6sbf_hr0000gn_T_NSCLiteralController_a32604_mi_6).arr, 2U);  
</code></pre>

<p>或许有小伙伴看到上面一堆看不懂的东东直呼，尼玛，太长了，不看。然而请稍安勿躁，难道我还会骗你吗？（我去，上次你说去看那美女洗澡，结果是80岁的老太太。）咳咳，开玩笑。不要急我们一点一点分析，先从具体的运行代码开始分析。</p>

<p>我们可以看到字符串的字面量被转换成了<code>struct __NSConstantStringImpl</code>的类型,也就是字符串的字面量转换成了一个结构常量，然后通过类型转换成了NSString类型。而<code>NSNumber</code>则是通过<code>[NSNumber numberWithInt:]</code>的函数将其转换成了<code>NSNumber</code>类型,而如果小伙伴试试其他的数字，比如float或者double等数字的话，会发现<code>clang</code>会替我们自动选择最适合的构造函数进行转换，不过目前有点纠结的一点就是<code>long double</code>类型是没有办法通过<code>@</code>来进行转换， 并且如果之前看过<a href="http://www.nscookies.com/tagged-pointer/">Tagged Pointer</a>一节的小伙伴可以尝试一下，会发现其实这里的<code>NSNumber</code>其实是一个<code>Tagged Pointer</code>，感兴趣的小伙伴可以自己去看下。</p>

<p>而至于数组和字典则是在调用其自己的构造函数，<code>[NSArray arrayWithObjects:count:]</code>和<code>[NSDictionary dictionaryWithObjects:forKeys:count:]</code>,然后其中的拿一些一大长串的都是字符串字面量的结构体的常量名称。跟上面说到的字符串的字面量一样。</p>

<p>而这里比较特别的是一个布尔类型，其实OC中的BOOL类型只是通过typedef转换了一下，其实其本质就是一个<code>signed char</code>， 而其中的<code>YES</code>和<code>NO</code>就是通过一些宏定义定义的，具体的代码可以查看<code>objc/objc.h</code>中查看，下面我贴出这些代码：</p>

<pre><code>/// Type to represent a boolean value.
#if (TARGET_OS_IPHONE &amp;&amp; __LP64__)  ||  TARGET_OS_WATCH
#define OBJC_BOOL_IS_BOOL 1
typedef bool BOOL;  
#else
#define OBJC_BOOL_IS_CHAR 1
typedef signed char BOOL;  
// BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C" 
// even if -funsigned-char is used.
#endif

#if __has_feature(objc_bool)
#define YES __objc_yes
#define NO  __objc_no
#else
#define YES ((BOOL)1)
#define NO  ((BOOL)0)
#endif
</code></pre>

<p>当然初次之外，我们还可以用的比较多的是所谓的<code>Boxed Expressions</code>,那么具体是什么样子的呢？其实很简单就是'<strong>@</strong>'加上个括号，<code>@( &lt;expression&gt; )</code>的形式，其实这种形式非常常见，可以用在字符串，算术表达式，枚举以及struct中。</p>

<pre><code>typedef NS_ENUM(NSInteger, NSCLietralEnum) {  
    NSCLietralEnum1,
    NSCLietralEnum2,
    NSCLietralEnum3,
    NSCLietralEnum4
};

typedef struct __attribute__((objc_boxable)) _NSCPosition {  
    float x, y;
} NSCPosition;

// Boxed Expressions
NSNumber *piOverTwo = @(M_PI / 2);  
NSNumber *favoriteColor = @(NSCLietralEnum1);  
NSValue *value = @(self.view.frame);  
NSCPosition pos;  
NSValue *value2 = @(pos);  
</code></pre>

<p>看到上面代码我们可以注意到其实需要一个struct，需要其通过<code>__attribute__((objc_boxable))</code>加以修饰，否则就不可以通过<code>@()</code>进行字面量的转换。当然还有关于数组和字典中下标的内容这里就不说了，其实也就是通过判断其是读还是写，将其转换成对应的<code>msg_send()</code>的方法调用。这里就不说了，有兴趣的小伙伴可以自己做一些试验。</p>

<p>最后说说我们所谓的"自定义下标"，其实所谓的自定义下标就是通过实现两个方法即可。 这里我们用一个字典来实现这两个方法，如果想实现自己的方法的话，可以例如通过数据库去获取信息啊之类的。看下面代码：</p>

<pre><code>@interface NSCSubscriptableObject : NSObject

//  Object subscripting
- (id)objectForKeyedSubscript:(id &lt;NSCopying&gt;)key;
- (void)setObject:(id)obj forKeyedSubscript:(id &lt;NSCopying&gt;)key;

@end

@implementation NSCSubscriptableObject {
    NSMutableDictionary     *_dictionary;
}

- (id)init {
    self = [super init];
    if (self) {
        _dictionary = [NSMutableDictionary dictionary];
    }
    return self;
}

- (id)objectForKeyedSubscript:(id &lt;NSCopying&gt;)key {
    return _dictionary[key];
}

- (void)setObject:(id)obj forKeyedSubscript:(id &lt;NSCopying&gt;)key {
    _dictionary[key] = obj;
}

@end

NSCSubscriptableObject *subsObj = [[NSCSubscriptableObject alloc] init];

subsObj[@"string"] = @"Value 1";  
subsObj[@"number"] = @2;  
subsObj[@"array"] = @[@"Arr1", @"Arr2", @"Arr3"];

NSLog(@"String: %@", subsObj[@"string"]);  
NSLog(@"Number: %@", subsObj[@"number"]);  
NSLog(@"Array: %@", subsObj[@"array"]);  
</code></pre>

<p>好了好了，关于<code>Objective C</code>的字面量内容说的有点多了。主要是连得内容也多，如果有兴趣的小伙伴可以看<a href="http://clang.llvm.org/docs/ObjectiveCLiterals.html">Objective-C Literals</a>来看更多详细的内容。</p>

<p>具体的代码也可以从<a href="https://github.com/NSCookies">Github</a>下载，如果对Swift的字面量没兴趣的小伙伴就滚吧(靠，你说什么，找死啊~)。咳咳，说错了。 请各位大神留下来看一眼吧，求求你们了。</p>

<p>其实<code>Swift</code>的非常简单，只要完成其指定协议即可。而至于<code>Swift</code>中都挺提供了哪些<code>字面量转换协议</code>，大家可以通过下面这些来参考一下：</p>

<ul>
<li>ArrayLiteralConvertible</li>
<li>BooleanLiteralConvertible</li>
<li>DictionaryLiteralConvertible</li>
<li>ExtendedGraphemeClusterLiteralConvertible</li>
<li>FloatLiteralConvertible</li>
<li>NilLiteralConvertible</li>
<li>IntegerLiteralConvertible</li>
<li>StringLiteralConvertible</li>
<li>StringInterpolationConvertible</li>
<li>UnicodeScalarLiteralConvertible</li>
</ul>

<p>只要某一个对象实现了具体的转换协议，我们就可以通过对应的字面量进行转换了，下面我们来看一个例子：</p>

<pre><code>// Swift 字面量转换协议
class NSCPerson : StringLiteralConvertible {  
    let name: String

    init(withName name: String) {
        self.name = name
    }

    required convenience init(stringLiteral value: String) {
        self.init(withName:value)
    }

    required convenience init(unicodeScalarLiteral value: String) {
        self.init(withName:value)
    }

    required convenience init(extendedGraphemeClusterLiteral value: String) {
        self.init(withName:value)
    }

}

let person: NSCPerson = "NSCookies"  
print("person.name:\(person.name)")

// 输出
// person.name:NSCookies
</code></pre>

<p>是不是非常简单，简单到就是实现一个接口而已。来小伙伴可以给我鼓鼓掌，我会更爱你们哟~（pia~一脱鞋过来）呜呜~，不要这么暴力，年轻人还是斯文一点比较好。</p>

<p>好吧，言归正传。当我们需要自定义字面量转换成具体类型的时候，可以根据上面的协议中挑选需要的字面量协议类型即可，剩下的就交给<code>Swift</code>自带的机制吧。</p>

<p>可以看到这个例子中用字符串的字面量来创建了<code>NSCPerson</code>对象。我特别喜欢<code>Swift</code>这个特性， 因为这个特性可以给我们带来许多的方便，例如我们可以用在创建<code>NSURL</code>时通过字符串来创建，我们可以通过一个ID去创建一个对象，创建的同时可以通过本地缓存或者数据缓存来获得等等等，非常有用。小伙伴要好好利用这个特性。</p>

<blockquote>
  <p>PS:具体代码可以从<a href="https://github.com/NSCookies">Github</a>上获取。</p>
  
  <p>如有问题或纠正, 可以联系<a href="http://weibo.com/plutoy0504">@叫什么都不如叫Pluto-Y</a>或在<a href="https://github.com/NSCookies">Github</a></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[isa-swizzling?什么鬼？]]></title><description><![CDATA[<p>刚看到这个名字估计很多人有点熟悉，<code>Method Swizzling</code>对不对，不熟悉也没关系，去看看之前的一篇文章<a href="http://www.nscookies.com/method-swizzling/">黑魔法之Method Swizzling</a>吧。不过也可以根据名称猜测出来所谓的<code>isa-swizzling</code>就是讲<code>isa</code>进行替换了的技术。如果有小伙伴问我什么是<code>isa</code>的话，不好意思，保安，有人来捣乱，拖出去~(开玩笑，关于什么是<code>isa</code>的话，大家可以去看看网络上很多关于OC的对象模型的文章)。</p>

<p>那么，既然是<code>isa</code>替换，那主角当然就是<code>isa</code>啦。那么这个技术出现在什么场景呢？其实这个技术在官方文档中关于<a href="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOImplementation.html">KVO的文档中</a>有提到过, 里面说到了，KVO是通过<code>isa-swizzling</code>来实现的。</p>

<blockquote>
  <p>Automatic key-value observing is implemented using a technique called</p></blockquote>]]></description><link>http://www.nscookies.com/isa-swizzling/</link><guid isPermaLink="false">92944730-1470-4b85-a9e7-3abab0c9d386</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Mon, 15 Aug 2016 20:31:17 GMT</pubDate><content:encoded><![CDATA[<p>刚看到这个名字估计很多人有点熟悉，<code>Method Swizzling</code>对不对，不熟悉也没关系，去看看之前的一篇文章<a href="http://www.nscookies.com/method-swizzling/">黑魔法之Method Swizzling</a>吧。不过也可以根据名称猜测出来所谓的<code>isa-swizzling</code>就是讲<code>isa</code>进行替换了的技术。如果有小伙伴问我什么是<code>isa</code>的话，不好意思，保安，有人来捣乱，拖出去~(开玩笑，关于什么是<code>isa</code>的话，大家可以去看看网络上很多关于OC的对象模型的文章)。</p>

<p>那么，既然是<code>isa</code>替换，那主角当然就是<code>isa</code>啦。那么这个技术出现在什么场景呢？其实这个技术在官方文档中关于<a href="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOImplementation.html">KVO的文档中</a>有提到过, 里面说到了，KVO是通过<code>isa-swizzling</code>来实现的。</p>

<blockquote>
  <p>Automatic key-value observing is implemented using a technique called isa-swizzling.</p>
  
  <p>The isa pointer, as the name suggests, points to the object's class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, among other data.</p>
  
  <p>When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.</p>
  
  <p>You should never rely on the isa pointer to determine class membership. Instead, you should use the class method to determine the class of an object instance.</p>
</blockquote>

<p>最后一段中也说到了，永远不要用用<code>isa</code>来判断一个类的继承关系，而是应该用<code>class</code>方法来判断类的实例。</p>

<p>看着好像很厉害的技术耶~(双眼bling~bling~发光)。那么为了验证这个技术真正的实现了，也就是<code>isa</code>替换了，我们写了下面一段代码来验证。大家一起来观摩一下：</p>

<pre><code>// 这里用来说明printInfo方法都做了说明
@implementation Person

- (void)printInfo {
    NSLog(@"isa:%@, supper class:%@", NSStringFromClass(object_getClass(self)), class_getSuperclass(object_getClass(self)));
    NSLog(@"self:%@, [self superclass]:%@", self, [self superclass]);
    NSLog(@"age setter function pointer:%p", class_getMethodImplementation(object_getClass(self), @selector(setAge:)));
    NSLog(@"name setter function pointer:%p", class_getMethodImplementation(object_getClass(self), @selector(setName:)));
    NSLog(@"printInfo function pointer:%p", class_getMethodImplementation(object_getClass(self), @selector(printInfo)));

}

@end

// 既然是KVO嘛，那我们就来模拟一个虚假的KVO的情况，并且打印对应的信息
Person *person = [[Person alloc] init];  
NSLog(@"Before add observer--------------------------------------------------------------------------");  
[person printInfo];
[person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:&amp;PrivateKVOContext];
NSLog(@"After add observer--------------------------------------------------------------------------");  
[person printInfo];
[person removeObserver:self forKeyPath:@"age"];
NSLog(@"After remove observer--------------------------------------------------------------------------");  
[person printInfo];
</code></pre>

<blockquote>
  <p>根据之前一篇<a href="http://www.nscookies.com/runtime-objectmodel/">历史冤案之isKindOfClass与isMemberOfClass</a>中提到，object_getClass其实打印的就是isa指针。</p>
</blockquote>

<p>其中第一个Person的实现中，我们打印了<code>isa</code>, <code>supperclass</code>, 两个方法的<code>setter</code>的函数指针以及<code>printInfo</code>的函数指针。而运行代码中分别在添加Observer前，添加Observer以及删除Observer后分别打印出该类的信息。那么输出的结果是什么呢？</p>

<pre><code>Before add observer--------------------------------------------------------------------------  
isa:Person, supper class:NSObject  
self:&lt;Person: 0x7ffe5aeeb340&gt;, [self superclass]:NSObject  
age setter function pointer:0x10fe03c40  
name setter function pointer:0x10fe03be0  
printInfo function pointer:0x10fe03a30  
After add observer--------------------------------------------------------------------------  
isa:NSKVONotifying_Person, supper class:Person  
self:&lt;Person: 0x7ffe5aeeb340&gt;, [self superclass]:NSObject  
age setter function pointer:0x10ff04c7f  
name setter function pointer:0x10fe03be0  
printInfo function pointer:0x10fe03a30  
After remove observer--------------------------------------------------------------------------  
isa:Person, supper class:NSObject  
self:&lt;Person: 0x7ffe5aeeb340&gt;, [self superclass]:NSObject  
age setter function pointer:0x10fe03c40  
name setter function pointer:0x10fe03be0  
printInfo function pointer:0x10fe03a30  
</code></pre>

<p>我们可以分别对比一下添加Observer前以及添加Observer之后(同样也可以比较添加Observer之后与删除Observer之后)，我们发现<code>isa</code>, 通过<code>class_getSuperclass</code>, 以及<code>age</code>的<code>setter</code>方法都发生了变化。而且我们发现其添加Observer之后的<code>class_getSuperclass</code>得到的结果竟然是<code>Person</code>, <code>isa</code>竟然是<code>NSKVONotifying_Person</code>。 What the hell?这是在逗我？</p>

<p>不对，看看文章的标题，其实在添加KVO之后，<code>isa</code>已经替换成了<code>NSKVONotifying_Person</code>,而根据<code>class_getSuperclass</code>得到的结果竟然是<code>Person</code>, 然后<code>age</code>是使我们KVO需要观察的属性，它的<code>setter</code>函数指针变了。而我们也知道，所谓的OC的消息机制是通过<code>isa</code>去查找实现的，那么我们现在可以进行大胆的猜想：</p>

<p>其实KVO的实现可能是：</p>

<ul>
<li>添加Observer
<ul><li>通过runtime偷偷实现了一个子类，并且以<code>NSKVONotifying_</code>+类名来命名</li>
<li>将之前那个对象的isa指针指向了这个子类。</li>
<li>重写了观察的对象setter方法，并且在重写的中添加了<code>willChangeValueForKey:</code>以及<code>didChangeValueForKey:</code></li></ul></li>
<li>移除Observer
<ul><li>只是简单的将其的isa指向原来的类对象中</li></ul></li>
</ul>

<p>然后我们在分析一下， 在真正调用的<code>setAge:</code>的情况下, 根据消息机制我们知道它先通过<code>isa</code>找到对应对象的类， 也就是现在<code>NSKVONotifying_Person</code>, 然后再去找<code>setAge:</code>，由于<code>NSKVONotifying_Person</code>这个对象重写了这个方法， 那么就会直接取当前的实现， 也就是带有<code>willChangeValueForKey:</code>以及<code>didChangeValueForKey:</code>， 那么自然就实现了对KVO的实现了。</p>

<p>最后在看看一幅图吧，稍微总结一下<code>isa-swizzling</code>是搞什么东东：</p>

<p><img src="http://www.nscookies.com/content/images/2016/08/isa-swizzling.png" alt="35AFA96E-1740-4433-A780-6577E922EE51.png"></p>

<p>最后就酱紫了，退下吧，朕要翻牌子了~哇哈哈</p>

<blockquote>
  <p>PS:具体代码可以从<a href="https://github.com/NSCookies">Github</a>上获取。</p>
  
  <p>如有问题或纠正, 可以联系<a href="http://weibo.com/plutoy0504">@叫什么都不如叫Pluto-Y</a>或在<a href="https://github.com/NSCookies">Github</a></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[黑魔法之Method Swizzling]]></title><description><![CDATA[<blockquote>
  <p>背景：一手抓住女神，一首抓住楼主</p>
  
  <p>“移形换影大法”</p>
  
  <p>几秒钟后...</p>
  
  <p>“讨厌，死相，这什么武功嘛，好厉害的呢，好厉害的呢！”</p>
  
  <p>“pia~，赶紧说正题”（一脱鞋过来！）</p>
</blockquote>

<p>咳咳，其实今天要说的这个，估计很多童鞋看到这个名字就知道是什么了。对了，这个就是大家最最熟悉的黑魔法之一。(其实也就是前面开头中用到的那个移形换影大法，人家没有不正经，只是举了个例子而已啦~嗲~)我们常说OC的动态性是OC的一大特点之一，其中非常好的一个例子就是这个<code>Method Swizzling</code>，用得好它能做出非常恶心的功能————如果Xcode很多插件。但是如果滥用的话，它也会造成非常大的麻烦————最明显的就是很多程序中莫名其妙的bug。</p>

<p>但是作为最为流行的黑魔法我们要知道，如果有童鞋做安全，逆向或者hack别人App，那估计这个是基础的基础了吧。并且很多开源库中都用了这个“魔法”，所以知道这个还是非常有必要的。</p>

<p>我们知道OC底层的实现中，对象的实现方法都存在类中，存在一个叫做<code>objc_method_list</code>，而这里真正存储的是一个关于<code>函数名</code>、<code>参数列表</code>以及<code>函数实现的指针</code>的<code></code></p>]]></description><link>http://www.nscookies.com/method-swizzling/</link><guid isPermaLink="false">d387e0e8-9a57-4ec4-a2be-b5655124a0b9</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Wed, 10 Aug 2016 16:34:00 GMT</pubDate><content:encoded><![CDATA[<blockquote>
  <p>背景：一手抓住女神，一首抓住楼主</p>
  
  <p>“移形换影大法”</p>
  
  <p>几秒钟后...</p>
  
  <p>“讨厌，死相，这什么武功嘛，好厉害的呢，好厉害的呢！”</p>
  
  <p>“pia~，赶紧说正题”（一脱鞋过来！）</p>
</blockquote>

<p>咳咳，其实今天要说的这个，估计很多童鞋看到这个名字就知道是什么了。对了，这个就是大家最最熟悉的黑魔法之一。(其实也就是前面开头中用到的那个移形换影大法，人家没有不正经，只是举了个例子而已啦~嗲~)我们常说OC的动态性是OC的一大特点之一，其中非常好的一个例子就是这个<code>Method Swizzling</code>，用得好它能做出非常恶心的功能————如果Xcode很多插件。但是如果滥用的话，它也会造成非常大的麻烦————最明显的就是很多程序中莫名其妙的bug。</p>

<p>但是作为最为流行的黑魔法我们要知道，如果有童鞋做安全，逆向或者hack别人App，那估计这个是基础的基础了吧。并且很多开源库中都用了这个“魔法”，所以知道这个还是非常有必要的。</p>

<p>我们知道OC底层的实现中，对象的实现方法都存在类中，存在一个叫做<code>objc_method_list</code>，而这里真正存储的是一个关于<code>函数名</code>、<code>参数列表</code>以及<code>函数实现的指针</code>的<code>dispatch table</code>。而其实真正的<code>Method Swizzling</code>的实现就是一个替换函数指针的过程。</p>

<p>怎么个替换方法呢？其实也非常简单， 看下下面两幅图就知道里面是怎么实现的了。
在没有交换前，<code>Class</code>中的两个方法对应的函数指针都是正常的，如下图：</p>

<p><img src="http://www.nscookies.com/content/images/2016/08/method-swizzling-before.jpg" alt="0E754822-3186-48F6-ADCB-5C6ED52CFED2.png"></p>

<p>而在交换后，其实就是将<code>Selector A</code>指向了<code>Implementation B</code>而<code>Selector B</code>则指向了<code>Implementation A</code>,如下图：
<img src="http://www.nscookies.com/content/images/2016/08/method-swizzling-after.jpg" alt="FF8DC868-46ED-4A12-B4F9-66ED789C2D3A.png"></p>

<p>那么在交换后调用<code>Selector A</code>的情况下，其实跳转到了<code>Implementation B</code>的指针所指向的代码段。自然就形成了方法A与B的替换了。</p>

<p>那么具体代码是如何实现的呢？其实实现的方式有特别多种，不过我最喜欢下面这一种：</p>

<pre><code class="language-objectivec">+ (void)swizzleWithOriginalSelector:(SEL)originalSelector swizzledSelector:(SEL) swizzledSelector isClassMethod:(BOOL)isClassMethod
{
    Class cls = [self class];

    Method originalMethod;
    Method swizzledMethod;

    if (isClassMethod) {
        originalMethod = class_getClassMethod(cls, originalSelector);
        swizzledMethod = class_getClassMethod(cls, swizzledSelector);
    } else {
        originalMethod = class_getInstanceMethod(cls, originalSelector);
        swizzledMethod = class_getInstanceMethod(cls, swizzledSelector);
    }

    if (!originalMethod) {
        NSLog(@"Error: originalMethod is nil, did you spell it incorrectly? %@", originalMethod);
        return;
    }

    method_exchangeImplementations(originalMethod, swizzledMethod);
}
</code></pre>

<p>既然知道了这个，那么我们来验证一下上面所说的指针的替换吧，看下下面的代码：</p>

<pre><code class="language-objectivec">MethodSwizzlingDemo *methodSizzling = [MethodSwizzlingDemo new];  
NSLog(@"Before swizzle method");  
NSLog(@"methodOrigin pointer :%p", class_getMethodImplementation([MethodSwizzlingDemo class], @selector(methodOrigin)));  
NSLog(@"methodSizzled pointer :%p", class_getMethodImplementation([MethodSwizzlingDemo class], @selector(methodSizzled)));  
[methodSizzling methodOrigin];
[methodSizzling methodSizzled];

NSLog(@"------------------------------------------------------------------------------------");  
[MethodSwizzlingDemo swizzleWithOriginalSelector:@selector(methodOrigin) swizzledSelector:@selector(methodSizzled) isClassMethod:NO];
NSLog(@"After swizzle method");  
NSLog(@"methodOrigin pointer :%p", class_getMethodImplementation([MethodSwizzlingDemo class], @selector(methodOrigin)));  
NSLog(@"methodSizzled pointer :%p", class_getMethodImplementation([MethodSwizzlingDemo class], @selector(methodSizzled)));  
[methodSizzling methodOrigin];
[methodSizzling methodSizzled];
</code></pre>

<p>上面代码中前面一半为未替换前的关于个方法信息以及调用后的结果，而后面一半则为执行<code>Method Swizzling</code>之后个方法信息以及调用后的结果，其后台输出如下：</p>

<blockquote>
  <p>Before swizzle method</p>
  
  <p>methodOrigin pointer :0x104ad46c0</p>
  
  <p>methodSizzled pointer :0x104ad4700</p>
  
  <p>-[MethodSwizzlingDemo methodOrigin]</p>
  
  <p>-[MethodSwizzlingDemo methodSizzled]</p>
  
  <hr>
  
  <p>After swizzle method</p>
  
  <p>methodOrigin pointer :0x104ad4700</p>
  
  <p>methodSizzled pointer :0x104ad46c0</p>
  
  <p>-[MethodSwizzlingDemo methodSizzled]</p>
  
  <p>-[MethodSwizzlingDemo methodOrigin]</p>
</blockquote>

<p>可以看出其中在为调用前，<code>methodOrigin</code>的指针为<code>0x104ad46c0</code>，而<code>methodSizzled</code>的指针为<code>0x104ad4700</code>,而在<code>Method Swizzling</code>之后这两个方法的指针调换了，从而实现了函数的“移形换影大法”。从后面的函数调用也可以看得出来。从而也证明了上面的图。</p>

<blockquote>
  <p>"死鬼， 这下你明白了没？没明白晚上我们慢慢聊~嗲~"</p>
</blockquote>

<p><code>Method Swizzling</code>是一个非常强而有力的魔法，在非常多的开源以及hack代码中都有用到，我记得我第一次接触到这个是在了解如何写自己的Xcode plugin， 那时候觉得能写一个Xcode plugin是一个非常酷的事情。（喂偏题了！）好吧，关于这个下次有空再说。</p>

<p>虽然说<code>Method Swizzling</code>是一个非常强大的黑魔法，但是还是建议能不用的情况下就少用，毕竟如果在多人都在替换之后，整体的函数指针都会特别乱，特别是在用第三方库的情况下，很多库都替换来替换去，最终导致程序经常出现莫名其妙的错误，但是自己分析情况下又很难判断出是否替换过，寻找起来不是特别的方便。</p>

<blockquote>
  <p>PS:具体代码可以从<a href="https://github.com/NSCookies">Github</a>上获取。</p>
  
  <p>如有问题或纠正, 可以联系<a href="http://weibo.com/plutoy0504">@叫什么都不如叫Pluto-Y</a>或在<a href="https://github.com/NSCookies">Github</a></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[历史冤案之isKindOfClass与isMemberOfClass]]></title><description><![CDATA[<p>开封有个包青天，铁面无私辩忠奸。话说在那峨眉山上有个尼姑庵(诶，怎么串戏了)。咳咳，不好意思，忘记吃药了。进入今天的主题吧，为什么说历史冤案呢？其实也说不上怨，跟窦娥比起来，它还好得很呢。</p>

<p>其实这次跟大家讲的是很多人都知道的OC的对象模型，也就是下面这张图，大家先有个印象先：</p>

<p><img src="http://www.nscookies.com/content/images/2016/08/----.png" alt="对象模型.png"></p>

<p>然后我们看看它的“冤”在哪，先来看一段代码吧。</p>

<pre><code class="language-objectivec">BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];  
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];  
BOOL res3 = [(id)[Pluto class] isKindOfClass:[Pluto class]];  
BOOL res4 = [(id)[Pluto class] isMemberOfClass:</code></pre>]]></description><link>http://www.nscookies.com/runtime-objectmodel/</link><guid isPermaLink="false">37a5d29c-15bc-42bd-b5ad-65dc705d1f12</guid><category><![CDATA[runtime]]></category><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Sun, 07 Aug 2016 20:18:09 GMT</pubDate><content:encoded><![CDATA[<p>开封有个包青天，铁面无私辩忠奸。话说在那峨眉山上有个尼姑庵(诶，怎么串戏了)。咳咳，不好意思，忘记吃药了。进入今天的主题吧，为什么说历史冤案呢？其实也说不上怨，跟窦娥比起来，它还好得很呢。</p>

<p>其实这次跟大家讲的是很多人都知道的OC的对象模型，也就是下面这张图，大家先有个印象先：</p>

<p><img src="http://www.nscookies.com/content/images/2016/08/----.png" alt="对象模型.png"></p>

<p>然后我们看看它的“冤”在哪，先来看一段代码吧。</p>

<pre><code class="language-objectivec">BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];  
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];  
BOOL res3 = [(id)[Pluto class] isKindOfClass:[Pluto class]];  
BOOL res4 = [(id)[Pluto class] isMemberOfClass:[Pluto class]];  
</code></pre>

<blockquote>
  <p>这段代码来自<a href="http://weibo.com/u/1364395395?is_all=1">@sunny大神</a>的博客里的文章——<a href="http://blog.sunnyxx.com/2014/11/06/runtime-nuts/">神经病院objc runtime入院考试</a>。（并对其中做了一些修改）里面还有其他的三道题，其中第二道和第三道题的原因有异曲同工之妙。</p>
</blockquote>

<p>而很多人都会认为，这明显是四个<code>YES</code>,因为我们不是平常说<code>isKindOfClass</code>就是用判断前面是不是后面那个的子类或者就是那个类，而<code>isMemberOfClass</code>就是用来判断前面那个是不是就是后面那个类型的。非也非也，正确的说话应该前面那个"实例"是否是后面那个类型的（针对<code>isKindOfClass</code>和<code>isMemberOfClass</code>），或者是任何前面那个"实例"是否是后面那个类型或者是那个子类型。
下面来看看苹果官网是如何描述的：</p>

<blockquote>
  <p>isKindOfClass: Returns a Boolean value that indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class.</p>
  
  <p>isMemberOfClass: Returns a Boolean value that indicates whether the receiver is an instance of a given class.</p>
</blockquote>

<p>为什么这里的实例加了引号呢，其实是这样的，我们常说的实例都是指左边那一列，而其实这里提到的实例不当是指左边这一列，还包含中间这一列。我们可以这样说我们通常认为的实例是<code>Class</code>的实例，而苹果官网中的文档还有另一重意思就是<code>Class</code>其实是<code>Meta Class</code>的实例。</p>

<p>不好意思这里有点绕，我们举一个实际的🌰。就说说前面这个板栗吧，这个板栗是一个具体的实例，而他的<code>Class</code>则应该是板栗这个类别，而板栗有是具有某一特性的植物，而这里指的某一特性的植物则是板栗这个<code>Class</code>的<code>Meta Class</code>，大家好好去体会一下。</p>

<p>接下来说说<code>NSObject class</code>和<code>object_getClass</code>之间的区别，咋一看很多人觉得这个不是应该是同一个东西吗，其实非也非也，且听我慢慢说来。其实<code>NSObject class</code>这个方法获得的永远是中间这个<code>Class</code>的级别，而<code>object_getClass</code>通常都是用来获得isa指向的那个，不行我们用下面这段代码来试试：</p>

<pre><code class="language-objectivec">BOOL res5 = [NSObject class] == [[NSObject class] class];  
BOOL res6 = [NSObject class] == [[[NSObject class] class] class];  
BOOL res7 = [NSObject class] == object_getClass([NSObject class]);  
BOOL res8 = object_getClass([NSObject class]) == object_getClass(object_getClass([NSObject class]));  
BOOL res9 = [NSObject class] == class_getSuperclass(object_getClass([NSObject class]));  
</code></pre>

<p>其中5和6其实不管调用多少次的<code>class</code>方法都会返回<code>YES</code>，而5和7说明了<code>class</code>方法和<code>object_getClass</code>方法是不一样的，而根据文章开头的图片以及前面说到<code>object_getClass</code>是isa指向的那个可以得出8和9都是<code>YES</code>。</p>

<p>好了，到这里要是都理解的话，我们最后在来看看苹果官方中关于<code>isKindOfClass</code>方法和<code>isMemberOfClass</code>的<a href="https://opensource.apple.com/source/objc4/objc4-532.2/runtime/NSObject.mm">源码</a>:</p>

<pre><code class="language-objectivec">+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = class_getSuperclass(tcls)) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = class_getSuperclass(tcls)) {
        if (tcls == cls) return YES;
    }
    return NO;
}
</code></pre>

<p>看到这里很多童鞋要说"Holy Shit", 然而本宝宝不喜欢说粗话的小盆友， 来人啊， 把说出话的人拖出去斩了（不用这么狠吧！）。好吧，没错，苹果官方关于这两个方法都提供了实例方法和类方法。而我们最开始的Demo调用的都是类方法，再加上我们前面说的<code>object_getClass</code>的含义以及第一副关于对象模型的图片得知，res1的路径为<code>Root Class-&gt;Root Meta Class -&gt; Root Class</code>，所以返回的是<code>YES</code>。同理可证res2, res3, res4为<code>NO</code>。</p>

<blockquote>
  <p>上面老是说因为所以让我想起以前正面题目的<code>∴</code>,<code>∵</code>还有同理可证什么的，看来我还是一个爱学习的好孩子嘛(偷笑)。</p>
  
  <p>PS:具体代码可以从<a href="https://github.com/NSCookies">Github</a>上获取。</p>
  
  <p>如有问题或纠正, 可以联系<a href="http://weibo.com/plutoy0504">@叫什么都不如叫Pluto-Y</a>或在<a href="https://github.com/NSCookies">Github</a></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[Tagged Pointer]]></title><description><![CDATA[<p><a href="https://en.wikipedia.org/wiki/Tagged_pointer">Tagged Pointer</a> What the hell is this?冷静！冷静！不要激动，从名字来上看应该是一个“打标记"的指针？其实想想好像以前见过这个名字, 对以前一定看过这个。于是乎我有开始搜索大脑以及程序员的资源库——Google。</p>

<p>soga，原来是这个回事。好了，这节就到这里了。(赶紧说，别废话!)既然你诚心诚意的发问了,为了防止世界被破坏,为了维护世界的和平...&amp;%<em>%&amp;</em>^&amp;&amp;^$。谁用拖鞋扔我！</p>

<p>好吧，且听我静静装逼，不对，且听我慢慢说来。所谓的<code>Tagged Pointer</code>其实就是一个带着"真实"的数据信息的指针。说到底他也就是一个伪指针。为什么这么说呢？</p>

<p>我们来先看两个例子：</p>

<pre><code class="language-objectivec">NSNumber *num1 = [NSNumber numberWithInt:10];  
NSNumber</code></pre>]]></description><link>http://www.nscookies.com/tagged-pointer/</link><guid isPermaLink="false">3cf25d40-4466-4133-ba4e-691e7aef32b3</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Sun, 31 Jul 2016 16:33:28 GMT</pubDate><content:encoded><![CDATA[<p><a href="https://en.wikipedia.org/wiki/Tagged_pointer">Tagged Pointer</a> What the hell is this?冷静！冷静！不要激动，从名字来上看应该是一个“打标记"的指针？其实想想好像以前见过这个名字, 对以前一定看过这个。于是乎我有开始搜索大脑以及程序员的资源库——Google。</p>

<p>soga，原来是这个回事。好了，这节就到这里了。(赶紧说，别废话!)既然你诚心诚意的发问了,为了防止世界被破坏,为了维护世界的和平...&amp;%<em>%&amp;</em>^&amp;&amp;^$。谁用拖鞋扔我！</p>

<p>好吧，且听我静静装逼，不对，且听我慢慢说来。所谓的<code>Tagged Pointer</code>其实就是一个带着"真实"的数据信息的指针。说到底他也就是一个伪指针。为什么这么说呢？</p>

<p>我们来先看两个例子：</p>

<pre><code class="language-objectivec">NSNumber *num1 = [NSNumber numberWithInt:10];  
NSNumber *num2 = [NSNumber numberWithInt:10];  
NSLog(@"pointer of num 1: %p", num1);  
NSLog(@"pointer of num 2: %p", num2);  
NSNumber *num3 = [NSNumber numberWithInt:0xfffff];  
NSNumber *num4 = [NSNumber numberWithInt:0xfffff];  
NSLog(@"pointer of num 3: %p", num3);  
NSLog(@"pointer of num 4: %p", num4);

// 其输出结果为:
2016-07-31 23:32:08.066 OCCodes[3773:205937] pointer of num 1: 0xb0000000000000a2  
2016-07-31 23:32:08.066 OCCodes[3773:205937] pointer of num 2: 0xb0000000000000a2  
2016-07-31 23:32:08.066 OCCodes[3773:205937] pointer of num 3: 0xb000000000fffff2  
2016-07-31 23:32:08.066 OCCodes[3773:205937] pointer of num 4: 0xb000000000fffff2  
</code></pre>

<p>非常奇怪，正常我们创建两个实例的时候，它应该前后两个实例地址的指针应该是不一样的，可是例子中的两组实例中，没组的实例的指针都是一样的。而且很奇怪的是指针的地址都是这种<code>0xb....2</code>形式，并且2旁边的数值正好是<code>NSNumber</code>存的值。难道这些都是巧合吗？</p>

<p>答案是：并不是的。其实这个正是<code>Tagged Pointer</code>的真实面目，表面上看他是一个指针，可是实际上他并不是真正的指针，而是包含了<code>NSNumber</code>具体信息的伪指针。但是这种"指针"只能存储数值较小的情况下，例如如下的例子则不能保存成<code>Tagged Pointer</code>的形式：</p>

<pre><code class="language-objectivec">NSNumber *num5 = [NSNumber numberWithDouble:M_PI];  
NSNumber *num6 = [NSNumber numberWithDouble:M_PI];  
NSLog(@"pointer of num 5: %p", num5);  
NSLog(@"pointer of num 6: %p", num6);

// 其输出结果为:
2016-07-31 23:39:34.375 OCCodes[3822:210131] pointer of num 5: 0x7fe70140e030  
2016-07-31 23:39:34.375 OCCodes[3822:210131] pointer of num 6: 0x7fe70170aab0  
</code></pre>

<p>可以看出当如果存储的数值所需的字节数较大的情况下，则以正确的指针来进行保存，而只有当存储的数值所需的位数较少时，则以<code>Tagged Pointer</code>的形式进行存储。那么，有人就要问了。为什么不都以真实的指针来进行存储呢？你猜！(瞬间一双拖鞋就过来了！)</p>

<p>好吧，其实是在iPhone 5s引入的过程中其实也引入了64位的处理器，而在引入64位处理器的过程中，指针所占的位数也相应的变为了64位(而在以前的32位处理器的时候指针都是以32位的形式存储)。而如果我需要创建一个<code>NSNumber</code>的实例，则我需要一个8个字节(64位)的指针以及在堆中分配一定空间来进行存储(大概是在8字节左右)。也就是说我需要16个字节才能存储一个<code>NSNumber</code>的对象。而对于一个4个字节的存储空间来说以及可以存储20+亿的整数了。</p>

<p>这个时候<code>Tagged Pointer</code>就闪亮登场了。它的目的就在于，当处于64位处理器的系统中将所有信息都存在栈的指针中，并通过一定的的特殊标记位来说明该指针为一个<code>Tagged Pointer</code>，剩余的一些指针的位数则用来存储<code>NSNumber</code>的具体数值(如上方的<code>0xb000000000fffff2</code>)。</p>

<p>其实这么做除了能节省CPU的空间以外，还能提高处理<code>NSNumber</code>的速度，因为他不需要再从堆中申请和释放空间，同时在读取和存储上的速度也加快了。</p>

<blockquote>
  <p>根据WWDC2013的<a href="https://developer.apple.com/videos/play/wwdc2013/404/">Session 404 Advanced in Objective-C</a>中提到了，在内存上读取的速度快了3倍，创建时的速度比以前快乐106倍。</p>
</blockquote>

<p>同时除了<code>NSNumber</code>能以这种这种形式存储外，<code>NSDate</code>也是做了同样的优化。而以上所说的这些<code>Tagged Pointer</code>是针对64位处理器来说的，在32位系统的情况下，这些还是以指针+堆中的对象进行存储的。在64位处理器并且其存储的实际数值没法用<code>Tagged Pointer</code>保存的情况下，也是与32位系统中一样的存储方式。</p>

<p>由于<code>Tagged Pointer</code>是一个伪指针，所以在使用过程中有两点需要注意的：
* 对于<code>NSObject</code>中的<code>isa</code>指针其实是没有的，毕竟它不是一个真正的对象
* 对于在MRC下，其retainCount也是不正确的，它采用了一个非常大的整数来进行返回，这样也能保证其能在内存中长时间的存在</p>

<p>总的来说，<code>Tagged Pointer</code>的出现是为了在64位系统中提升速度的。而<code>Tagged Pointer</code>则是通过一定的标志位以及将真实信息存入在指针中。所以<code>Tagged Point</code>并不是一个真正意义上的指针，而是包含了真实信息的指针。就是这样啦。</p>

<p>顺带献上Swift代码，请各位大王笑纳:</p>

<pre><code class="language-swift">// MARK: Tagged Pointer
let num1 = NSNumber(long: 0xffff)  
let num2 = NSNumber(long: 0xffff)  
let pointFormatStr1 = NSString(format: "tagged point :%p", num1)  
let pointFormatStr2 = NSString(format: "tagged point :%p", num2)


let num3 = NSNumber(float: Float(M_PI))  
let num4 = NSNumber(float: Float(M_PI))  
let pointFormatStr3 = NSString(format: "tagged point :%p", num3)  
let pointFormatStr4 = NSString(format: "tagged point :%p", num4)

let date1 = NSDate(timeIntervalSince1970: 10)  
let date2 = NSDate(timeIntervalSince1970: 10)  
let pointFormatStr5 = NSString(format: "tagged point :%p", date1)  
let pointFormatStr6 = NSString(format: "tagged point :%p", date2)

let date3 = NSDate(timeIntervalSinceNow: 10)  
let date4 = NSDate(timeIntervalSinceNow: 10)  
let pointFormatStr7 = NSString(format: "tagged point :%p", date3)  
let pointFormatStr8 = NSString(format: "tagged point :%p", date4)
</code></pre>

<blockquote>
  <p>PS:具体代码可以从<a href="https://github.com/NSCookies">Github</a>上获取。</p>
  
  <p>如有问题或纠正, 可以联系<a href="http://weibo.com/plutoy0504">@叫什么都不如叫Pluto-Y</a>或在<a href="https://github.com/NSCookies">Github</a></p>
</blockquote>]]></content:encoded></item><item><title><![CDATA[可变参数(Variadic Arguments)]]></title><description><![CDATA[<p>在OC中最常用的函数之一应该就是NSLog这个输出函数了，作为有好奇心的新青年，我“手贱”的点进了NSLog的声明里看到了下面的声明：</p>

<pre><code>FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);  
</code></pre>

<p>这个代码然我瞬间感觉好熟悉，想起了之前Java中的<a href="https://en.wikipedia.org/wiki/Variadic_function">可变参数函数</a>。可是再仔细一看就发现，尼玛后面的可变参数竟然是没有参数名，而且也没有类型之类的信息，瞬间我就懵逼了。</p>

<p>于是我就找了我的大哥--谷歌，经过一番查找之后发现其实也不是特别难。乍一看虽然跟Java中将参数默认识别为一个具体类型的数据完全不同，但是其实还是换汤不换药，其实只是我们将Java中帮我们做的那一份我们自己实现了而已。</p>

<p>其实说是OC的可变参数的实现不如说是C的可变参数的实现，因为OC是C的超集，而且在许多方面来说OC并没有完全的摆脱C的影子。好了不废话了，看招：</p>

<pre><code class="language-objectivec">// 可变参数的实现
-(NSNumber *)sumAllValues:(NSNumber *)initValue, ... {
    va_list args; // 1.
    va_start(args, initValue); // 2.
    NSNumber *arg</code></pre>]]></description><link>http://www.nscookies.com/variadic-arguments/</link><guid isPermaLink="false">6df9cf21-1ca5-4c87-bb3c-f990e2c74da9</guid><dc:creator><![CDATA[Pluto Y]]></dc:creator><pubDate>Tue, 26 Jul 2016 19:10:36 GMT</pubDate><content:encoded><![CDATA[<p>在OC中最常用的函数之一应该就是NSLog这个输出函数了，作为有好奇心的新青年，我“手贱”的点进了NSLog的声明里看到了下面的声明：</p>

<pre><code>FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);  
</code></pre>

<p>这个代码然我瞬间感觉好熟悉，想起了之前Java中的<a href="https://en.wikipedia.org/wiki/Variadic_function">可变参数函数</a>。可是再仔细一看就发现，尼玛后面的可变参数竟然是没有参数名，而且也没有类型之类的信息，瞬间我就懵逼了。</p>

<p>于是我就找了我的大哥--谷歌，经过一番查找之后发现其实也不是特别难。乍一看虽然跟Java中将参数默认识别为一个具体类型的数据完全不同，但是其实还是换汤不换药，其实只是我们将Java中帮我们做的那一份我们自己实现了而已。</p>

<p>其实说是OC的可变参数的实现不如说是C的可变参数的实现，因为OC是C的超集，而且在许多方面来说OC并没有完全的摆脱C的影子。好了不废话了，看招：</p>

<pre><code class="language-objectivec">// 可变参数的实现
-(NSNumber *)sumAllValues:(NSNumber *)initValue, ... {
    va_list args; // 1.
    va_start(args, initValue); // 2.
    NSNumber *arg = va_arg(args, NSNumber *); // 3.
    NSNumber *result = initValue;
    float sum = [result floatValue];
    while (arg != nil) {
        sum += [arg floatValue];
        arg = va_arg(args, NSNumber *); // 4.
    }
    va_end(args); // 5.
    result = @(sum);
    return result;
}
</code></pre>

<p>可以看出其实我们只是将<code>Java</code>帮我们做的事情让我们自己做而已。也不是特别难嘛。
这里稍微给大家讲解一下：</p>

<ul>
<li>1.声明了一个变量用来装可变参数,其类型是在C中定义的va_list, 其本质上来说是一个指针</li>
<li>2.调用C函数va_start, 其第一个参数是将可变参数的第一个参数的指针传递给该参数，第二个参数指明了可变参数是跟在哪个参数之后的</li>
<li>3、4.都是用来去多参数的下一个参数的C函数，其第一个参数传递的是1中声明的变量，第二个参数是讲多参数的数据转换成什么类型，其实这个好像是<code>forin</code>的形式</li>
<li>5.在遍历完可变参数之后记得要调用va_end来表明结束，并且释放该变量</li>
</ul>

<p>其实这个过程很想是在写原生<code>sqlite3</code>的时候，获取数据之后进行遍历数据的情况。需要去某个地方(<code>va_start</code>第二个参数之后的位置)"获取"数据，然后遍历数据，最后结束的时候还要关闭“数据库”(释放变量)的感觉。是不是很简单？</p>

<p>那么接下来我们再看看<code>Swift</code>是如何实现的，更简单的就来了~</p>

<pre><code class="language-swift">func sumAllValues(nums: Float...) -&gt; Float {  
    var sum: Float = 0.0
    for num in nums {
        sum += num
    }
    return sum
}

sumAllValues(5.0, 4.0, 5.0, 3.3)  
</code></pre>

<p>有没有觉得很像是一个语法糖一样，只是将可变参数转换成一个数据，于是我又一次“手贱”(怪我咯?)的查看了一下<code>nums</code>的类型:</p>

<p><img src="http://www.nscookies.com/content/images/2016/07/----.png" alt="可变参数"></p>

<p>看出来没，其实所谓的可变参数都是骗子。就不能少一点套路，多一点真诚吗？其实就是讲所谓的可变参数编程了一个<code>Array</code>了。所以总体来说<code>Swift</code>的可变参数函数实现起来就是讲可变参数变成了数组。</p>

<p>然而其实在OC中大部分情况下没必要用到<code>可变参数函数</code>, 我们可以用<code>NSArray</code>等集合工具来实现即可。并且用集合来实现相对更安全一点。毕竟用集合可以通过集合的许多属性和方法来做一些特殊的处理和判断。所以，切记可变函数参数在极少的情况下才会用到，大部分情况我们只要了解其中的实现以及看见的时候知道是怎么回事即可。</p>

<blockquote>
  <p>PS:具体代码可以从<a href="https://github.com/NSCookies">Github</a>上获取。</p>
  
  <p>如有问题或纠正, 可以联系<a href="http://weibo.com/plutoy0504">@叫什么都不如叫Pluto-Y</a>或在<a href="https://github.com/NSCookies">Github</a></p>
</blockquote>]]></content:encoded></item></channel></rss>