<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Jerry Lee</title>
    <description>Welcome to my world.</description>
    <link>https://stevenlfg.github.io//</link>
    <atom:link href="https://stevenlfg.github.io//feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Tue, 02 Nov 2021 13:00:12 +0000</pubDate>
    <lastBuildDate>Tue, 02 Nov 2021 13:00:12 +0000</lastBuildDate>
    <generator>Jekyll v3.9.0</generator>
    
      <item>
        <title>苹果内购</title>
        <description>&lt;hr /&gt;

&lt;p&gt;最近面试过程中，被面试官多次问到苹果内购相关的实现及遇到的问题，这里梳理以下，做个总结吧。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;内购关键流程&quot;&gt;内购关键流程&lt;/h2&gt;

&lt;p&gt;1、客户端发起支付&lt;/p&gt;

&lt;p&gt;2、客户端监听购买流程&lt;/p&gt;

&lt;p&gt;3、苹果回调订单购买成功时，客户端把苹果给的receipt_data和一些订单信息上报给服务器&lt;/p&gt;

&lt;p&gt;4、服务器拿到receipt_data向苹果服务器校验&lt;/p&gt;

&lt;p&gt;5、苹果服务器返回status为0则表示发回成功&lt;/p&gt;

&lt;p&gt;其他返回值也记录总结一下&lt;/p&gt;

&lt;p&gt;21000 :App Store无法读取你提供的JSON数据&lt;/p&gt;

&lt;p&gt;21002 :收据数据不符合格式&lt;/p&gt;

&lt;p&gt;21003 :收据无法被验证&lt;/p&gt;

&lt;p&gt;21004 :你提供的共享密钥和账户的共享密钥不一致&lt;/p&gt;

&lt;p&gt;21005 :收据服务器当前不可用&lt;/p&gt;

&lt;p&gt;21006 :收据是有效的，但订阅服务已经过期。当收到这个信息时，解码后的收据信息也包含在返回内容中&lt;/p&gt;

&lt;p&gt;21007 :收据信息是测试用（sandbox），但却被发送到产品环境中验证&lt;/p&gt;

&lt;p&gt;21008 :收据信息是产品环境中使用，但却被发送到测试环境中验证&lt;/p&gt;

&lt;p&gt;6、服务器监发现订单校验成功之后，把该笔订单存起来，保存到数据库，可以防止同一笔订单多次发放商品&lt;/p&gt;

&lt;h2 id=&quot;遇到的问题&quot;&gt;遇到的问题&lt;/h2&gt;

&lt;p&gt;1、丢单&lt;/p&gt;

&lt;p&gt;2、双重验证&lt;/p&gt;

&lt;p&gt;3、多次发放商品&lt;/p&gt;

&lt;h2 id=&quot;解决方案&quot;&gt;解决方案&lt;/h2&gt;

&lt;p&gt;丢单问题大多分两种：
1、没有拿到凭证，支付成功，由于网络原因或各种原因没有返回transaction;&lt;/p&gt;

&lt;p&gt;可以去查询苹果默认的[SKPaymentQueue defaultQueue]队列里面查询订单状态&lt;/p&gt;

&lt;p&gt;2、支付成功后，拿到receipt凭证，上传服务器由于网络原因丢失&lt;/p&gt;

&lt;p&gt;可以在支付之前让服务器创建订单，并把交易成功之后的交易凭证和订单保持数据库，用户下次登录之后验证&lt;/p&gt;

&lt;p&gt;做双重验证的目的：&lt;/p&gt;

&lt;p&gt;我们平时测试以及苹果审核的时候都要用沙盒账号测试，如何识别App端发过来的收据是沙盒测试还是正式环境用户的购买呢？这里可以用两种方案，即双重验证。&lt;/p&gt;

&lt;p&gt;先把收据拿到正式环境的验证地址去验证，如果苹果的正式环境验证服务器返回的状态码 status 为 21007，说明当前收据是沙盒环境下产生，则再连接一次沙盒环境服务器进行验证，这样不管是我们自己采用沙盒账号测试还是苹果审核人员采用沙盒账号进行审核、或者用户购买都可以保证收据正常的验证成功。&lt;/p&gt;

&lt;p&gt;判重方案解决多次发放商品：收到客户端上报的 transaction_id 后，直接MD5后去数据库查，能查到说明是重复订单，返回相应错误码给客户端，如果查不到，去苹果那边校验。服务器拿到苹果的校验结果之后，判断订单状态是不是成功，如果成功再发放内购商品。&lt;/p&gt;

</description>
        <pubDate>Mon, 01 Nov 2021 00:00:00 +0000</pubDate>
        <link>https://stevenlfg.github.io//2021/11/%E8%8B%B9%E6%9E%9C%E5%86%85%E8%B4%AD/</link>
        <guid isPermaLink="true">https://stevenlfg.github.io//2021/11/%E8%8B%B9%E6%9E%9C%E5%86%85%E8%B4%AD/</guid>
        
        
      </item>
    
      <item>
        <title>设计模式 生成器模式</title>
        <description>&lt;hr /&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;定义&quot;&gt;定义&lt;/h2&gt;

&lt;p&gt;也叫创建者模式，它将一个复杂对象的构建与它的表示分离，使得同样的构建过程可以创建不同的表示&lt;/p&gt;

&lt;h2 id=&quot;举例&quot;&gt;举例&lt;/h2&gt;

&lt;p&gt;生成器模式将复杂的创建逻辑进行分割，例如生产汽车，分步骤创建安装不同的零件。如果创建逻辑简单则没有拆分的必要。&lt;/p&gt;

&lt;h2 id=&quot;优点&quot;&gt;优点&lt;/h2&gt;
&lt;p&gt;1、客户端不必知道产品内部组成的细节，将产品本身与产品的创建过程解耦，使得相同的创建过程可以创建不同的产品对象；&lt;/p&gt;

&lt;p&gt;2、每一个具体建造者都相对独立，而与其他的具体建造者无关，因此可以很方便地替换具体建造者或增加新的具体建造者， 用户使用不同的具体建造者即可得到不同的产品对象；&lt;/p&gt;

&lt;p&gt;3、增加新的具体建造者无须修改原有类库的代码，指挥者类针对抽象建造者类编程，系统扩展方便，符合“开闭原则”；&lt;/p&gt;

&lt;p&gt;4、可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中，使得创建过程更加清晰，也更方便使用程序来控制创建过程&lt;/p&gt;

&lt;h2 id=&quot;缺点&quot;&gt;缺点&lt;/h2&gt;
&lt;p&gt;1、建造者模式所创建的产品一般具有较多的共同点，其组成部分相似，如果产品之间的差异性很大，则不适合使用建造者模式，因此其使用范围受到一定的限制；&lt;/p&gt;

&lt;p&gt;2、如果产品的内部变化复杂，可能会导致需要定义很多具体建造者类来实现这种变化，导致系统变得很庞大。&lt;/p&gt;

&lt;p&gt;##代码举例##&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@interface Builder : NSObject
+ (void)buildEngine;
+ (void)buildWheel;
+ (void)buildBody;
@end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Sat, 15 Dec 2018 00:00:00 +0000</pubDate>
        <link>https://stevenlfg.github.io//2018/12/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E7%94%9F%E6%88%90%E5%99%A8%E6%A8%A1%E5%BC%8F/</link>
        <guid isPermaLink="true">https://stevenlfg.github.io//2018/12/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E7%94%9F%E6%88%90%E5%99%A8%E6%A8%A1%E5%BC%8F/</guid>
        
        
      </item>
    
      <item>
        <title>设计模式 单例模式</title>
        <description>&lt;hr /&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;定义&quot;&gt;定义&lt;/h2&gt;

&lt;p&gt;单例模式确保某一个类只有一个实例，并提供一个访问它的全局访问点。&lt;/p&gt;

&lt;h2 id=&quot;举例&quot;&gt;举例&lt;/h2&gt;

&lt;p&gt;单例模式下，对应类只能生成一个实例。就像一个王国只能有一个国王，一旦王国里的事物多起来，这唯一的国王也容易职责过重。&lt;/p&gt;

&lt;h2 id=&quot;优点&quot;&gt;优点&lt;/h2&gt;
&lt;p&gt;1、提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例，所以它可以严格控制客户怎样以及何时访问它；&lt;/p&gt;

&lt;p&gt;2、因为该类在系统内存中只存在一个对象，所以可以节约系统资源。&lt;/p&gt;

&lt;h2 id=&quot;缺点&quot;&gt;缺点&lt;/h2&gt;
&lt;p&gt;1、由于单例模式中没有抽象层，因此单例类很难进行扩展；&lt;/p&gt;

&lt;p&gt;2、对于有垃圾回收系统的语言Java，C#来说，如果对象长时间不被利用，则可能会被回收。那么如果这个单例持有一些数据的话，在回收后重新实例化时就不复存在了。&lt;/p&gt;

&lt;p&gt;##代码举例##&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@implementation Singleton
+(instancetype)shareInstance{
static Singleton *shareInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&amp;amp;onceToken, ^{
    shareInstance = [[Singleton alloc]init];
});
return shareInstance;
}
@end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Mon, 10 Dec 2018 00:00:00 +0000</pubDate>
        <link>https://stevenlfg.github.io//2018/12/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/</link>
        <guid isPermaLink="true">https://stevenlfg.github.io//2018/12/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/</guid>
        
        
      </item>
    
      <item>
        <title>设计模式 抽象工厂模式</title>
        <description>&lt;hr /&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/iazppbifre56l5x0eaodng2i/%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82.png&quot; alt=&quot;抽象工厂.png-4.5kB&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;定义&quot;&gt;定义&lt;/h2&gt;

&lt;p&gt;提供一个创建一系列相关或相互依赖对象的接口，而无需指定他们具体的类，抽象工厂和工厂方法不同的地方在于，生产产品的工厂是抽象的。&lt;/p&gt;

&lt;p&gt;抽象工厂模式和工厂方法模式很相似，但是抽象工厂模式将抽象发挥的更加极致，是三种工厂模式中最抽象的一种设计模式。抽象工厂模式，也叫做Kit模式，提供了创建一系列相关抽象子类的接口，而无需指定它们具体的类型。&lt;/p&gt;

&lt;p&gt;抽象工厂模式中定义了抽象工厂类，抽象工厂类中定义了每个系列的抽象子类创建所需的方法，这些方法对应着不同类型的抽象子类实例化过程。每个工厂子类都对应着一个系列，工厂子类通过重写这些方法来实例化当前系列的抽象子类。&lt;/p&gt;

&lt;p&gt;抽象工厂模式包含四部分：&lt;/p&gt;

&lt;p&gt;抽象工厂类：定义创建抽象子类的具体行为，根据系列中不同类型的抽象子类可能会有多种行为。&lt;/p&gt;

&lt;p&gt;工厂子类：继承自抽象工厂类，根据当前抽象子类对应的系列，重写父类定义的对应行为。对应的抽象子类系列不同，行为的实现方式也不同。&lt;/p&gt;

&lt;p&gt;抽象类：定义当前类型抽象子类的操作，子类继承父类完成具体的操作。在抽象工厂模式中，可能会有多种抽象类的定义。&lt;/p&gt;

&lt;p&gt;抽象子类：根据当前类型继承自对应的抽象类，并根据系列的不同重写抽象类定义的实现。&lt;/p&gt;

&lt;h2 id=&quot;和工厂方法模式有什么不同&quot;&gt;和工厂方法模式有什么不同&lt;/h2&gt;

&lt;p&gt;在工厂方法模式中，工厂子类负责抽象子类的实例化，每个工厂子类对应着一个抽象子类，且具有唯一性。而在抽象工厂模式中，一个工厂子类代表一个系列，工厂子类根据当前系列对不同类型的抽象子类进行创建。工厂方法模式中工厂子类对应的是一个类型的抽象子类，抽象工厂模式对应的是一个系列的抽象子类。
工厂方法模式一个工厂子类对应一个抽象子类的设计，会有很大的浪费，产生了过多的类。而抽象工厂模式更好的利用了工厂子类，使每个工厂子类对应着一个系列的抽象子类，这种设计非常适用于两个具有相同结构关系，但是分属于不同系列的系列之间的切换。
总之就是，工厂方法模式是针对单个类型的抽象类，而抽象工厂模式是针对具有相同结构的一系列类型的抽象类。&lt;/p&gt;

&lt;h2 id=&quot;举例&quot;&gt;举例&lt;/h2&gt;

&lt;p&gt;可口可乐公司生产可乐的同时，也需要生产装可乐的瓶子和箱子，瓶子和箱子也是可口可乐专属定制的，同样百事可乐公司也会有这个需求。这个时候我们的工厂不仅仅生产可乐饮料的工厂，还必须同时生产同一主题的瓶子和箱子，所以它是一个抽象的主题工厂，专门生产同一主题的不同商品。&lt;/p&gt;

&lt;h2 id=&quot;优点&quot;&gt;优点&lt;/h2&gt;
&lt;p&gt;1.具体产品在应用层代码隔离，不需要关心产品细节。只需要知道自己需要的产品是属于哪个工厂的即可；&lt;/p&gt;

&lt;p&gt;2.当一个产品族中的多个对象被设计成一起工作时，它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说，是一种非常常用的设计模式。&lt;/p&gt;

&lt;h2 id=&quot;缺点&quot;&gt;缺点&lt;/h2&gt;
&lt;p&gt;规定了所有可能被创建的产品集合，产品族中扩展新的产品困难，需要修改抽象工厂的接口；&lt;/p&gt;

&lt;p&gt;##代码举例##&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@implementation CocaColaFactory
+(Cola *)createCola{
return [CocaCola new];
}

+(CocaColaBottle*)createBottle{
return [CocaColaBottle new];
}

+(CocaColaBox*)createBox{
return [CocaColaBox new];
}

@implementation PesiColaFactory
+(Cola *)createCola{
return [PesiCola new];
}

+(PesiColaBottle*)createBottle{
return [PesiColaBottle new];
}

+(PesiColaBox*)createBox{
return [PesiColaBox new];
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Wed, 05 Dec 2018 00:00:00 +0000</pubDate>
        <link>https://stevenlfg.github.io//2018/12/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F/</link>
        <guid isPermaLink="true">https://stevenlfg.github.io//2018/12/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F/</guid>
        
        
      </item>
    
      <item>
        <title>设计模式 工厂方法模式</title>
        <description>&lt;hr /&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/fu9wdb2gzx5qefwjfq10e54v/%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95.png&quot; alt=&quot;工厂方法.png-2.9kB&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;定义&quot;&gt;定义&lt;/h2&gt;

&lt;p&gt;又称为工厂模式，工厂父类负责定义创建产品对象的公共接口，而工厂子类则负责生成具体的产品对象，即通过不同的工厂子类来创建不同的产品对象；&lt;/p&gt;

&lt;p&gt;工厂方法模式和简单工厂模式十分类似，大致结构是基本类似的。不同在于工厂方法模式对工厂类进行了进一步的抽象，将之前的一个工厂类抽象成了抽象工厂和工厂子类，抽象工厂定义一个创建抽象子类的接口，抽象工厂的子类实现这些接口并决定实例化哪个抽象子类。工厂子类决定着创建哪个抽象子类，外界决定着创建哪种工厂子类，抽象子类和工厂子类是一一对应的。&lt;/p&gt;

&lt;p&gt;在工厂方法模式中，和简单工厂模式一样，对外隐藏了抽象子类的创建过程，外界只需要关心工厂类即可，负责实例化的工厂子类决定着最后的结果。&lt;/p&gt;

&lt;p&gt;工厂方法模式主要包含四部分：&lt;/p&gt;

&lt;p&gt;工厂抽象类：定义创建抽象子类的接口，通过接口返回具体的抽象子类。&lt;/p&gt;

&lt;p&gt;工厂子类：继承自工厂抽象类，并重写父类的方法来创建对应的抽象子类。&lt;/p&gt;

&lt;p&gt;抽象类：定义抽象子类所需的属性和方法，子类通过继承自抽象类获得这些方法。&lt;/p&gt;

&lt;p&gt;抽象子类：继承自抽象类，实现具体的操作。&lt;/p&gt;

&lt;p&gt;为什么要用工厂方法模式？&lt;/p&gt;

&lt;p&gt;在简单工厂模式的代码中，如果我们没有使用反射机制，只是标准的简单工厂模式代码。会有一个问题，就是如果新增加其他运算功能，需要创建一个抽象子类，但是还需要修改工厂类中的代码逻辑，这种设计是不符合开放封闭原则的。开放封闭原则对于修改是关闭的，对于扩展是开放的。而且将所有的操作子类的判断和实例化都由一个工厂类完成，如果业务比较复杂会导致工厂类负担较重。&lt;/p&gt;

&lt;p&gt;工厂方法模式将之前负责生成具体抽象子类的工厂类，抽象为工厂抽象类和工厂子类组成的一系列类。每创建一个抽象子类，就需要创建一个工厂子类，并且一一对应，由工厂子类去生成对应的抽象子类，由外界使用方来决定生成哪个工厂子类。这样在增加新的需求时，就不需要对工厂抽象类进行修改，而是对应新增的抽象子类创建对应的工厂子类即可。&lt;/p&gt;

&lt;h2 id=&quot;举例&quot;&gt;举例&lt;/h2&gt;

&lt;p&gt;工厂方法和简单工厂有一些区别，简单工厂是由一个代工厂生产不同的产品，而工厂方法是对工厂进行抽象化，不同产品都由专门的具体工厂来生产。可口可乐工厂专门生产可口可乐，百事可乐工厂专门生产百事可乐。&lt;/p&gt;

&lt;h2 id=&quot;优点&quot;&gt;优点&lt;/h2&gt;
&lt;p&gt;1.用户只需要关心其所需产品对应的具体工厂是哪一个即可，不需要关心产品的创建细节，也不需要知道具体产品类的类名；&lt;/p&gt;

&lt;p&gt;2.当系统中加入新产品时，不需要修改抽象工厂和抽象产品提供的接口，也无需修改客户端和其他的具体工厂和具体产品，而只要添加一个具体工厂和与其对应的具体产品就可以了，符合了开闭原则；&lt;/p&gt;

&lt;h2 id=&quot;缺点&quot;&gt;缺点&lt;/h2&gt;
&lt;p&gt;当系统中加入新产品时，除了需要提供新的产品类之外，还要提供与其对应的具体工厂类。因此系统中类的个数将成对增加，增加了系统的复杂度；&lt;/p&gt;

&lt;h2 id=&quot;代码举例&quot;&gt;代码举例&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+(Cola*)createCola{
return [Cola new];
}

+(Box*)createBox{
return [Box new];
}

+(Bottle *)createBottle{
return [Bottle new];
}

Cola *pesiCola = [PesiColaFactory createCola];
Cola *cocaCola = [CocaColaFactory createCola];
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Wed, 21 Nov 2018 00:00:00 +0000</pubDate>
        <link>https://stevenlfg.github.io//2018/11/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F/</link>
        <guid isPermaLink="true">https://stevenlfg.github.io//2018/11/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F/</guid>
        
        
      </item>
    
      <item>
        <title>设计模式 简单工厂模式</title>
        <description>&lt;hr /&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/p7tcq5qv1ycwo1uval29n9r6/%E7%AE%80%E5%8D%95%E5%B7%A5%E5%8E%82.png&quot; alt=&quot;简单工厂.png-3.3kB&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;定义&quot;&gt;定义&lt;/h2&gt;

&lt;p&gt;专门定义类(工厂类)来负责创建其他类的实例。可以根据创建方法的参数来返回不同类的实例，被创建的实例通常都具有共同的父类。&lt;/p&gt;

&lt;p&gt;简单工厂模式中定义一个抽象类，抽象类中声明公共的特征及属性，抽象子类继承自抽象类，去实现具体的操作。工厂类根据外界需求，在工厂类中创建对应的抽象子类实例并传给外界，而对象的创建是由外界决定的。外界只需要知道抽象子类对应的参数即可，而不需要知道抽象子类的创建过程，在外界使用时甚至不用引入抽象子类。&lt;/p&gt;

&lt;p&gt;简单工厂模式将抽象子类的创建，和关于抽象子类相关的业务逻辑分离，降低对象间的耦合度。由于工厂类只是为外界创建对象，所以并不需要实例化工厂类对象，只需要为外界提供类方法即可。外界需要什么类型的抽象子类，只需要传递对应的参数即可。外界不需要知道具体的抽象子类，只需要使用抽象类即可。&lt;/p&gt;

&lt;p&gt;简单工厂模式主要包含三部分：&lt;/p&gt;

&lt;p&gt;工厂类：根据外界的需求，决定创建并返回哪个具体的抽象子类。&lt;/p&gt;

&lt;p&gt;抽象类：定义抽象子类所需的属性和方法，子类通过继承自抽象类获得这些方法。&lt;/p&gt;

&lt;p&gt;抽象子类：继承自抽象类，是具体操作的实现者，根据需求重写父类继承过来的方法。&lt;/p&gt;

&lt;h2 id=&quot;业务场景&quot;&gt;业务场景&lt;/h2&gt;

&lt;p&gt;简单工厂模式主要适用于抽象子类的业务逻辑相同，但具体实现不同的情况。不同的操作子类执行同样的方法，最后的结果却是不同的，这也是多态的一种表现方式。&lt;/p&gt;

&lt;h2 id=&quot;优点&quot;&gt;优点&lt;/h2&gt;

&lt;p&gt;1.使用者只需要给工厂类传入一个正确的约定好的参数，就可以获取你所需要的对象，而不需要知道其创建细节，一定程度上减少系统的耦合；&lt;/p&gt;

&lt;p&gt;2.客户端无需知道所创建的具体产品类的类名，只需要知道具体产品类所对应的参数即可，减少开发者的记忆成本；&lt;/p&gt;

&lt;h2 id=&quot;缺点&quot;&gt;缺点&lt;/h2&gt;

&lt;p&gt;1.如果业务上添加新产品的话，就需要修改工厂类原有的判断逻辑，这其实是违背了开闭原则的；&lt;/p&gt;

&lt;p&gt;2.在产品类型较多时，有可能造成工厂逻辑过于复杂。所以简单工厂模式比较适合产品种类比较少而且增多的概率很低的情况；&lt;/p&gt;

&lt;h2 id=&quot;代码举例&quot;&gt;代码举例&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;+(Cola *)createColaWithType:(NSInteger)type{
switch (type) {
    case 0:
        return [CocaCola new];
        break; 
    case 1:
        return [PesiCola new];
        break;
    default:
        return nil;
        break;
} }

Cola *cocaCola = [SimpleFactory createColaWithType:0];
Cola *pesiCola = [SimpleFactory createColaWithType:1];
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
        <pubDate>Tue, 20 Nov 2018 00:00:00 +0000</pubDate>
        <link>https://stevenlfg.github.io//2018/11/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E7%AE%80%E5%8D%95%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F/</link>
        <guid isPermaLink="true">https://stevenlfg.github.io//2018/11/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E7%AE%80%E5%8D%95%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F/</guid>
        
        
      </item>
    
      <item>
        <title>深入理解runloop</title>
        <description>
&lt;hr /&gt;

&lt;hr /&gt;
&lt;h2 id=&quot;runloop简介&quot;&gt;RunLoop简介&lt;/h2&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;运行循环，在程序运行过程中循环做一些事情，如果没有Runloop程序执行完毕就会立即退出，如果有Runloop程序就会一直运行，并且时时刻刻在等待用户的输入操作。
RunLoop可以在需要的时候自己跑起来运行，在没有操作的试试就停下来休息。充分节省CPU资源，提高程序性能。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;runloop基本作用&quot;&gt;RunLoop基本作用&lt;/h2&gt;
&lt;p&gt;1.保持程序持续运行
 程序一启动就会开一个主线程，主线程一开起来就会跑一个主线程对应的RunLoop,RunLoop保证主线程不会被销毁，也就保证了程序的持续运行;&lt;/p&gt;

&lt;p&gt;2.处理App中的各种事件
 比如定时器事件，触摸事件，Selector事件;&lt;/p&gt;

&lt;p&gt;3.节省CPU资源，提高程序性能
 程序运行起来时，当什么操作都没有做的时候，Runloop就会告诉CPU，现在没有事情做，我要去休息，这时CPU就会将其资源释放出来去做其他的事情，当有事情做的时候Runloop就会立马起来去做事情。&lt;/p&gt;

&lt;p&gt;RunLoop内部运行原理：
 &lt;img src=&quot;http://static.zybuluo.com/stevenlfg/06r4dhqptxgw65bo8vtrla3h/1434508-467259882fa87be8.png&quot; alt=&quot;1434508-467259882fa87be8.png-65kB&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;runloop和线程之间的关系&quot;&gt;RunLoop和线程之间的关系&lt;/h2&gt;

&lt;p&gt;1.每条线程都有唯一的一个与之对应的RunLoop对象;&lt;/p&gt;

&lt;p&gt;2.RunLoop保存在一个全局的Dictionary里，线程作为key,RunLoop作为value;&lt;/p&gt;

&lt;p&gt;3.主线程的RunLoop已经自动创建好了，子线程的RunLoop需要主动创建;&lt;/p&gt;

&lt;p&gt;4.RunLoop在第一次获取时创建，在线程结束时销毁。&lt;/p&gt;

&lt;h2 id=&quot;runloop启动流程&quot;&gt;RunLoop启动流程&lt;/h2&gt;

&lt;p&gt;UIApplicationMain函数内启动了RunLoop,程序不会马上退出，二是保持运行状态。
因此每一个应用必须要有一个RunLoop,我们值得主线程一开起来，就会跑一个和主线程对应的RunLoop，那么RunLoop一定在程序的入口main函数中开启。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/y1ul69h5yftleupfjl8bkajp/WX20190404-174047.png&quot; alt=&quot;WX20190404-174047.png-41.7kB&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们看一下UIApplicationMain的API
&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/psy66cu04stjwptqrcawezb9/WX20190404-174129.png&quot; alt=&quot;WX20190404-174129.png-58.8kB&quot; /&gt;&lt;/p&gt;

</description>
        <pubDate>Sat, 10 Nov 2018 00:00:00 +0000</pubDate>
        <link>https://stevenlfg.github.io//2018/11/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3RunLoop/</link>
        <guid isPermaLink="true">https://stevenlfg.github.io//2018/11/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3RunLoop/</guid>
        
        
      </item>
    
      <item>
        <title>Kvo的本质</title>
        <description>
&lt;hr /&gt;

&lt;p&gt;KVO的全称是Key -Value Observing,即“键值监听”，可用于监听某个对象属性值的改变&lt;/p&gt;

&lt;hr /&gt;
&lt;h2 id=&quot;代码实现&quot;&gt;代码实现&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/ze1ka66b7hliziq47e48cnit/WX20190403-152226.png&quot; alt=&quot;WX20190403-152226.png-209.2kB&quot; /&gt;&lt;/p&gt;

&lt;p&gt;结果输出
&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/hag3bgsjdkk42qrhdpkqpczl/WX20190403-153047.png&quot; alt=&quot;WX20190403-153047.png-11.3kB&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以看到我们只监听了person1属性值改变，所以只输出person1的改变。那问题来了，我们给person1,person2都改变了age属性的值，本质都是调用了LGPerson类中setAge这个方法，那么为什么person1会走到oberveValueForKeyPath这个方法，person2不会呢？
KVO底层实现分析
———
我们断点打印一下person1和person2的isa，看一下他们实例对象isa指向的类对象是什么？&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/g5ji7vbsmx8tuxai902lttnk/WX20190403-154305.png&quot; alt=&quot;WX20190403-154305.png-12.7kB&quot; /&gt;&lt;/p&gt;

&lt;p&gt;person1的isa指针打印出的是NSKVONotifying_LGPerson，person2的isa指针打印出的是LGPerson
如果不监听person1的属性，我们看一下person1的isa指针&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/rf7xzye5vzl9svl8aghdtpkv/WX20190403-161900.png&quot; alt=&quot;WX20190403-161900.png-6.4kB&quot; /&gt;
person1和person2都是实例对象，所以person1和person2的isa指针指向的都是类对象。
通过对比我们发现两者isa指针指向的对象发生了变化，现在我们再做深一步的研究，添加监听之后两者调用的setAge方法有没有变化呢&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/8tpjivzsldv5pgm01do5ti2c/WX20190403-162858.png&quot; alt=&quot;WX20190403-162858.png-17kB&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/ibuctf354mcxxhr3b1duprme/WX20190403-162931.png&quot; alt=&quot;WX20190403-162931.png-17.6kB&quot; /&gt;
果然和我们猜想的一样，监听之后调用方法变成了_NSSetIntValueAndNotify
如果定义的属性是类型是double，则调用的是_NSSetDoubleValueAndNotify(),那么我们可以推测Foundation框架中有许多此类型的函数比如_NSSetBoolValueAndNotify，_NSSetCharValueAndNotify，_NSSetFloatValueAndNotify，通过属性的不同类型调用不同的函数。&lt;/p&gt;

&lt;hr /&gt;
&lt;h2 id=&quot;nskvonotifying_lgperson内部结构解析&quot;&gt;NSKVONotifying_LGPerson内部结构解析&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/uvyzl92e5t8ngsgmhjmr0q9r/WX20190403-164346.png&quot; alt=&quot;WX20190403-164346.png-88.4kB&quot; /&gt;
打印结果：
&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/yq9rj9f9zcq8hipmtjfuif8i/WX20190403-164319.png&quot; alt=&quot;WX20190403-164319.png-11.5kB&quot; /&gt;
通过上述代码我们发现NSKVONotifying_LGPerson中有4个对象方法。分别为setAge: class dealloc _isKVOA&lt;/p&gt;

&lt;p&gt;验证didChangeValueForKey:内部会调用observer的observeValueForKeyPath:ofObject:change:context:方法&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/gxdk2vj6qqb85878hcispw6b/WX20190403-165304.png&quot; alt=&quot;WX20190403-165304.png-73.9kB&quot; /&gt;
打印结果：
&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/36oxz5ntknuoz6seyiy4fxpg/WX20190403-165527.png&quot; alt=&quot;WX20190403-165527.png-37.2kB&quot; /&gt;
通过打印内容可以看到，确实在didChangeValueForKey方法内部已经调用了observer的observeValueForKeyPath:ofObject:change:context:方法。&lt;/p&gt;

</description>
        <pubDate>Tue, 07 Aug 2018 00:00:00 +0000</pubDate>
        <link>https://stevenlfg.github.io//2018/08/KVO%E7%9A%84%E6%9C%AC%E8%B4%A8/</link>
        <guid isPermaLink="true">https://stevenlfg.github.io//2018/08/KVO%E7%9A%84%E6%9C%AC%E8%B4%A8/</guid>
        
        
      </item>
    
      <item>
        <title>多线程gcd(一)</title>
        <description>
&lt;hr /&gt;
&lt;h2 id=&quot;一各种队列的执行效果&quot;&gt;(一)各种队列的执行效果&lt;/h2&gt;
&lt;p&gt;1.常见的多线程方案
&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/63jlphbovr1zehk2piaxynw4/WX20180802-165249.png&quot; alt=&quot;WX20180802-165249.png-116.1kB&quot; /&gt;
2.同步和异步的主要影响:能不能开启新的线程
同步:在当前的线程中执行任务，不具备开启新线程的能力
异步:在新的线程中开启任务，具备开启新线程的能力&lt;/p&gt;

&lt;p&gt;3.并发和串行的主要影响:任务的执行方式
并发:多个任务并发(同时)执行
串行:一个任务执行完毕之后，再执行下个任务
队列的类型包括:
并发队列，串行队列，主队列(也是一个串行队列)
决定了任务的执行方式(并发，串行)&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/qf0y98btq1ktyp5xun4v17n2/WX20180802-165032.png&quot; alt=&quot;WX20180802-165032.png-82kB&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;二常见的死锁案例&quot;&gt;(二)常见的死锁案例&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;使用sync函数往当前串行队列中添加任务，会卡住当前的串行队列(产生死锁)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1.产生死锁&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NSLog(@&quot;任务1&quot;);

dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_sync(mainQueue, ^{
    NSLog(@&quot;任务2&quot;);
});
NSLog(@&quot;任务3&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;2.不会产生死锁&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NSLog(@&quot;任务1&quot;);

dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
    NSLog(@&quot;任务2&quot;);
});
NSLog(@&quot;任务3&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.产生死锁(sync并且是串行队列粗体文本)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    NSLog(@&quot;任务1&quot;);
dispatch_queue_t queue = dispatch_queue_create(&quot;myqueu&quot;, DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    NSLog(@&quot;任务2&quot;);

    dispatch_sync(queue, ^{
        NSLog(@&quot;任务3&quot;);

    });
    NSLog(@&quot;任务4&quot;);

});
NSLog(@&quot;任务5&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;4.不会产生死锁(不是同一个队列)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  NSLog(@&quot;任务1&quot;);
dispatch_queue_t queue = dispatch_queue_create(&quot;myqueue&quot;, DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create(&quot;myqueue2&quot;, DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    NSLog(@&quot;任务2&quot;);
    dispatch_sync(queue2, ^{
        NSLog(@&quot;任务3&quot;);

    });
    NSLog(@&quot;任务4&quot;);
});
NSLog(@&quot;任务5&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;5.不产生死锁(并发队列)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; NSLog(@&quot;任务1&quot;);
dispatch_queue_t queue = dispatch_queue_create(&quot;myqueu&quot;, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    NSLog(@&quot;任务2&quot;);
    dispatch_sync(queue, ^{
        NSLog(@&quot;任务3&quot;);
    });
    NSLog(@&quot;任务4&quot;);
});
NSLog(@&quot;任务5&quot;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

</description>
        <pubDate>Thu, 02 Aug 2018 00:00:00 +0000</pubDate>
        <link>https://stevenlfg.github.io//2018/08/%E5%A4%9A%E7%BA%BF%E7%A8%8BGCD(%E4%B8%80)/</link>
        <guid isPermaLink="true">https://stevenlfg.github.io//2018/08/%E5%A4%9A%E7%BA%BF%E7%A8%8BGCD(%E4%B8%80)/</guid>
        
        
      </item>
    
      <item>
        <title>Cocoapods创建私有库(二)</title>
        <description>
&lt;hr /&gt;

&lt;p&gt;接着上篇文章，用Cocoapods创建私有库,完成了我们自己的私有库制作，在接下来的开发工作中，可能会根据需求去更新我们的私有库，本篇文章分享下更新私有库的经历。&lt;/p&gt;

&lt;h2 id=&quot;一更新&quot;&gt;一.更新&lt;/h2&gt;

&lt;p&gt;具体做法是将要添加的源文件放到Pod/Classes中，然后编辑.podspec文件。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/4l9vywh1r0p5edsdb0ok7rzh/1@2x.png&quot; alt=&quot;1@2x.png-63.4kB&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/btctktd27do1y1fs2nbx3ef0/8@2x.png&quot; alt=&quot;8@2x.png-44.6kB&quot; /&gt;&lt;/p&gt;

&lt;p&gt;编程完成后，本地验证下&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/d3e2copthpjog84duo1aw0yq/3@2x.png&quot; alt=&quot;3@2x.png-80.4kB&quot; /&gt;&lt;/p&gt;

&lt;p&gt;本地验证通过之后，就可以推送到远端了。这里面有个坑，就是要push tag,由于我漏了这一步，出现了一下问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/tfhm97de70wjg0jmzesceqsx/4@2x.png&quot; alt=&quot;4@2x.png-134.3kB&quot; /&gt;&lt;/p&gt;

&lt;p&gt;可以尝试如下代码&lt;/p&gt;

&lt;p&gt;git tag -m “update tag” 1.0.1&lt;/p&gt;

&lt;p&gt;git push –tags&lt;/p&gt;

&lt;p&gt;然后再重新执行pod repo push ** **.podspec –allow-warnings&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/ji43jcgs62gejrrr909a1soj/5@2x.png&quot; alt=&quot;5@2x.png-261.4kB&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;二查看&quot;&gt;二.查看&lt;/h2&gt;

&lt;p&gt;上面的更新操作完成之后，就可以在~/.cocoapods/repos目录下查看&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/s9neo1oj6t6gf3ngh7851pgu/6@2x.png&quot; alt=&quot;6@2x.png-39kB&quot; /&gt;&lt;/p&gt;

&lt;p&gt;执行pod search 查看&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.zybuluo.com/stevenlfg/ic3a08v516u570kbiwymyl7t/7@2x.png&quot; alt=&quot;7@2x.png-57.8kB&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果想要删除一个私有Spec Repo，只需要执行一条命令即可&lt;/p&gt;

&lt;p&gt;pod repo remove **&lt;/p&gt;

&lt;p&gt;当然我们还可以通过一下命令添加回来。&lt;/p&gt;

&lt;p&gt;pod repo add ** git@coding.net:** / **.git&lt;/p&gt;

</description>
        <pubDate>Sat, 02 Jun 2018 00:00:00 +0000</pubDate>
        <link>https://stevenlfg.github.io//2018/06/Cocoapods%E5%88%9B%E5%BB%BA%E7%A7%81%E6%9C%89%E5%BA%93(%E4%BA%8C)/</link>
        <guid isPermaLink="true">https://stevenlfg.github.io//2018/06/Cocoapods%E5%88%9B%E5%BB%BA%E7%A7%81%E6%9C%89%E5%BA%93(%E4%BA%8C)/</guid>
        
        
      </item>
    
  </channel>
</rss>
