swift開発の拡張実装ネーミングスペース(インスタンスメソッド、クラスメソッド)

7753 ワード

ネーミングスペース
長期にわたってobjective-c言語の開発に従事している私たちにとって、ネーミングスペースは比較的見慣れない名前かもしれません.
「ネーミングスペース」は、簡単に言えば、同じクラス名の領域は許可されません.Javaやjsの開発に従事したことがある学生は経験があるかもしれませんが、このような言語のネーミングスペースは実は彼らのディレクトリ名で、つまり異なるディレクトリの下で、同じクラス名を許可することができます.
OCは気まずいですが、名前の空間はありません.つまり、グローバルに同じクラス名は許されません.どうやって保証しますか?アップルはクラス名の前に2~3個の唯一の文字を加えて、自分のクラス名を他の区と区別することを提案し、UIView、NSString、MBProgressHUD、CALayer、AFNetworking、SDWebImageなどが登場した.
swiftでは、アップルがついにネーミングスペースを導入したという.任意のクラスでselfを印刷すると「ネーミングスペース.className」が現れる.swiftのネーミングスペースの使用はプロジェクトではなく、プロジェクトにまたがる必要がある.一つのプロジェクトでは、ネーミングスペースであり、同じネーミングスペースでは、すべてのグローバル変数や関数が共有され、importは必要なく、swiftから始まり、政府はpodを使ってサードパーティのフレームワークを管理することをお勧めします.そうしないと、フレームワークを入れてどこでも使えます.
拡張メソッド接頭辞
OCでは、アップルが拡張中の方法に接頭辞を追加することを提案しています.なぜなら、独自の方法や他のライブラリの拡張中の方法と重複することを防止するためです.実際には、前車の鑑があるため、このような方法を書き直したことによるフラッシュバックが多く、xcodeが正常にエラーをキャプチャできないと、調査が難しくなります.
swift拡張では,同様にメソッドオーバーライドの問題に関心を持つ必要があり,原生クラスが持つメソッドについては繰返し定義をオーバーライドでき,最終的には拡張中のメソッドを呼び出すことができるが,拡張中のメソッドは繰返し定義できず,xcodeは検出してエラーを報告する
カスタムネーミングスペース
以上のように、私たち自身が「ネーミングスペース」のようなものをシミュレートするのは、次のような良い選択です.
1.拡張中のメソッドまたは属性が既存のものを上書きし、予期せぬエラーが発生しないようにする
2.ネーミングスペースがあれば、接頭辞を付ける必要はありません.このような美観に影響を与える操作は、コードの可読性が高くなります.
3.ネーミングスペースができて、開発の過程の中で、特に新人に対して、一目で方法あるいは属性が元の類に属して持っているのか拡張しているのかを見ることができて、長い間使用して無意識の認知疲労をもたらすことを防止します
Swift拡張シミュレーション「ネーミングスペース」
まず、swiftのいくつかの概念を知っておきます.
プロトコル:OCのプロトコルと同様に、遵守者が実現する必要があるルールを定義していますが、OCとは異なり、swiftではプロトコルを拡張することもできます.最終的な効果は、プロトコルを遵守するすべてのクラスがプロトコルが拡張される内容を増やすことです.
汎用:swiftは関数、変数、コンテナなどを最大限に柔軟化するために「汎用」を提供し、アプリケーションやsdkをアーキテクチャ化すれば、汎用は最大の利便性を提供します.swiftの標準ライブラリはすべて汎用的に定義されています.例えば、ArrayはIntに詰め込むこともStringに詰め込むこともできます.
汎用制約:その名の通り、汎用によってプロトコル遵守者を制約するタイプです.
正式には、私たちの考えは拡張を通じて「ネーミング空間」をシミュレートすることですが、実はこれは正真正銘のネーミング空間ではありません.ただ、特殊な記号を通じて、私たち自身が拡張した方法の属性などを公式のものと第三者の区別することを望んでいます.

self.circleView.wm.moveToBottom()


circleViewに参加するのはUIViewのインスタンスで、ここでwmは私たちが言っている特殊な記号で、実は属性で、moveToBottomは私たち自身が拡張した方法です.
ここを見ると、最初の問題は、circleViewにwmという属性を拡張する方法です.多くの賢い七面鳥たちはすぐに2つの方法を考えます.1つはUIViewを拡張し、属性を増やすことです.もう1つは,UIViewを1つのプロトコルに準拠させ,プロトコルを拡張することによって属性を追加することである.
拡張するプロパティのタイプは、次のとおりです.

public class NameSpace {

}


方法1:

extension UIView {

  public var wm: NameSpace {

        get {

            return NameSpace()

        }

    }

}


方法2:

///       

public protocol NameSpaceProtocol {

    public var wm: NameSpace { get }

}

///     

extension NameSpaceProtocol {

    public var wm: NameSpace {

        get {

            return NameSpace()

        }

    }

}

/// UIView    

extension UIView: NameSpaceProtocol {



}


私たちはこの2つの方法を比較して、結論はまだ明らかで、方法2のメリットはあります.
1.各クラスを拡張するときは、メソッドのようにmwのプロパティを宣言する必要はありません.NameSpaceProtocolを実装すればいいのです.
2.サブクラスについては、この属性を望んでいない場合は、方式は1つも解けず、方式2つは汎用制約の方式を利用して、好きなように制御することができます.
3.方式二の書き方はswift独自の特性を多く採用しており、スタイル的にはより優雅で、簡単に言えばより装っている.
これに基づいて、NameSpaceを拡張することで、最終的な効果を実現しました.

extension NameSpace {

  public func moveToBottom() {



}

}

///   ,UIView          

let circleView = UIView()

circleView.wm.moveToBottom()


2つ目の質問が来ました.ここで私たちが本当に拡張しているのはNameSpaceです.私たちの目標はUIViewだけです.Date、Int、Stringなどの拡張をしなければなりません.実際にはNameSpaceに拡張しています.区別しないと、クラスの調整方法の1つで、Xcodeは他の目標を含めて拡張する方法をすべて提示します.実際に本ターゲット以外のメソッドを呼び出すと、コンパイルも間違っていませんが、これは私たちが望んでいるものではありません.そこで、汎用制約を導入して区別します.

///     

public final class NameSpace {



}

///   UIView

extension NameSpace where T == UIView {

}


これは快適で、使用中にUIVEewの拡張がショートカットに現れないわけではありません.ここでも小さな最適化をしましたが、NameSpaceがこれ以上使うことを望んでいないので、finalの説明を追加しました.
3つ目の質問ですwm.moveToBottom()このメソッドでは、moveToBottomメソッドでcircleViewにアクセスする必要があるメソッドやプロパティはどのように整備されていますか?実際に拡張されているのはNameSpaceクラスであることを知っています.NameSpaceに元のオブジェクトを記録する必要があります.

///       

public protocol NameSpaceProtocol {

    associatedtype TargetType



    ///            

    var wm: NameSpace { get }

}

///     

public final class NameSpace {

    internal var base: T

    init(_ base: T) {

        self.base = base

    }

}

///     

extension NameSpaceProtocol {

    public var wm: NameSpace {

        get {

            return NameSpace(self)

        }

    }

}

///         self.base      

extension NameSpace where T == UIView {

public func moveToBottom() {

    print("my x is \(self.base.frame.origin.x)")

}

}


ここまで書いて、すでに大部分の七面鳥たちに需要を満たして、実際にインターネット上の大多数の資料もここまでで、しかし依然として一部の不満があって、私達がずっとしたのはすべて実例の属性あるいは方法に対して拡張するので、もし類の属性あるいは方法ならば、類はUIcolorに似ています.wm.color(hexString)では、実は簡単で、過程は余計な説明にすぎず、直接貼り付けます.

///       

public protocol NameSpaceProtocol {

    associatedtype TargetType



    ///            

    var wm: NameSpace { get }



    ///           

    static var wm: NameSpace.Type { get }

}

///     

public final class NameSpace {

    internal var base: T

    internal var BASE: Self.Type

    init(_ base: T) {

        self.base = base

        self.BASE = Self.self

    }

}

///     

extension NameSpaceProtocol {

    public var wm: NameSpace {

        get {

            return NameSpace(self)

        }

    }

    public static var wm: NameSpace.Type {

        get {

            return NameSpace.self

        }

    }

}

///   :  UIImage

extension UIImage: NameSpaceProtocol {}

extension NameSpace where T == UIImage {



    ///         ,    

    public class func image(color: UIColor?, size: CGSize = CGSize(width: 1, height: 1)) -> UIImage {

        UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale);

        if let color = color, let currentContext = UIGraphicsGetCurrentContext() {

            let fillRect = CGRect(x: 0, y: 0, width: size.width, height: size.height);

            currentContext.setFillColor(color.cgColor)

            currentContext.fill(fillRect)

            let colorImage = UIGraphicsGetImageFromCurrentImageContext()

            UIGraphicsEndImageContext()

            return colorImage ?? UIImage()

        }

        return UIImage()

    }



  ///   ,    

  public var width: CGFloat {

      return self.base.size.width

}

}

///     

let image = UIImage.wm.image(color: .red, size: CGSize(width: 100, height: 200))

print("the image width is\(image.wm.width)")


注意1:NameSpaceを拡張する前に、NameSpaceProtocolプロトコルを実装する必要がありますが、実際の開発では、親が実装したため、子が実装する必要はありません.NSObjectをNameSpaceProtocolプロトコルを実装できるようにするよりも、実装したと警告することがあります.その後、UIVEewなどはこの手順を書く必要はありません.
注意2:七面鳥がruntime拡張属性を利用したい場合があるかもしれませんが、NameSpace拡張時に属性runtime方式で追加する場合はselfに必ず追加することに注意してください.ベース:

///     

    private var target: ButtonActionTarget? {

        get {

            return objc_getAssociatedObject(self.base, "buttonActionTarget") as? ButtonActionTarget

        }

        set {

            if let newValue = newValue {

                objc_setAssociatedObject(self.base, "buttonActionTarget", newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

            }

        }

    }


selfに追加すると、getのときはnilのままであることがわかります.これは、NameSpaceの役割ドメインが大きくないためです.NameSpace Protocolを拡張するときにNameSpaceを一時的に初期化しただけで、参照保存されていません.
そしてSelfについてself, .Typeの理解では、クエリーを実行できますが、簡単に言えば:
Self:プロトコルで使用され、プロトコル自体または実装者またはサブクラスのタイプを表します.
.self:インスタンスの後ろに使用するとインスタンス自体、タイプの後ろにタイプ自体が表示されます.
.Type:呼び出し元のタイプを取得する
最後に、このネーミングスペースの効果を実現する理由を再宣言します.
1.呼び出しの際、Xcodeのショートカットではターゲットの持つメソッドが表示されず、紛らわしいこともなく、新人にとって非常にフレンドリー
2.ネーミングスペースを追加し、上書きのリスクを効果的に回避
3.もっと優雅で、多くの有名な第三者もそうしました.例えばRxSwift