デザインパターン、iOS開発者の外観。 パート0。シングルトン・ロナー

私は感じて忘れています。

私は書いて暗記しました。

私はzrobivとzrozumіvです。

私はIntoshをひねりました、今私はマスターです。

(V.V.Bublik)








小さな紹介。



投稿の冒頭で、ウクライナ語で意図的に引用を行いました。 事実、大学の2年生のプログラミング教師から聞いたのはまさにこの言葉であり、今でもこの形で私はこれらの言葉を思い出しています。 ご想像のとおり、この引用は孔子の声明への参照ですが、習熟の達成に非常に重要な追加があります。







そして、この一連の投稿を書くようになったのはこれらの言葉でした。 実際、私はiOSの初心者開発者であり、デザインパターンを本当に理解したいと思っています。 そして、EricとElizabeth Freemanの著書「Design Patterns」を取り上げ、Objective-CとSwiftで各パターンの例を書くよりも良い方法を思いつきませんでした。 したがって、各パターンの本質と両方の言語の機能をよりよく理解できます。







内容:



パート0。シングルトン・ロナー

パート1.戦略

パート2.オブザーバー







だから、私の意見では最も単純なパターンから始めましょう。











ロナー、彼はシングルトンです。







シングルトンの主なタスクは、アプリケーションのライフサイクル全体にわたって特定のクラスのオブジェクトを1つだけユーザーに提供することです。 私の場合、iOS開発では、このようなオブジェクトの必要性の最も良い例はUIApplication



クラスです。 アプリケーションの存続期間中に、 UIApplication



クラスの唯一のオブジェクトが必要であることは論理的です。







それでは、この本の例を使って、Objective-CとSwiftのシングルトンを見てみましょう。







最初に、あるクラスのオブジェクトを作成する方法を見つけましょう。 非常にシンプル:







 // Objective-C [[MyClass alloc] init]
      
      





 // Swift MyClass()
      
      





そして、ここで著者は、上記のメソッドがこのクラスのオブジェクトをいくつでも作成できるという考えに私たちを導きます。 したがって、シングルトンへの途中で最初に行うことは、外部からのクラスのオブジェクトの作成を禁止することです。 プライベート初期化子がこれを助けてくれます。







そして、これが迅速に実装された場合は簡単です。







 // Swift class MyClass { private init() {} }
      
      





それはobjective-cでは一見簡単ではありません。 実際、すべてのobj-cクラスには共通の祖先NSObject



があり、その中にパブリック初期化子があります。 したがって、クラスのヘッダーファイルで、このメソッドがクラスにアクセスできないことを示す必要があります。







 // Objective-C @interface MyClass : NSObject - (instancetype)init UNAVAILABLE_ATTRIBUTE; @end
      
      





したがって、外部からクラスのオブジェクトを作成しようとすると、コンパイル段階でエラーが発生します。 わかった Objective-Cでは、クラスのオブジェクトの作成も禁止されています。 確かに、これはまだ完全にプライベートなイニシャライザーではありませんが、数秒後にこれに戻ります。







したがって、本質的に、コンストラクターはプライベートであるため、オブジェクトを作成できないクラスを取得しました。 そして、これらすべてをどうするか? クラス内にクラスのオブジェクトを作成します。 そして、このために静的メソッドを使用します(オブジェクトではなく、クラスのメソッド):







 // Swift class MyClass { private init() {} static func shared() -> MyClass { return MyClass() } }
      
      





 // Objective-C @implementation MyClass + (instancetype)sharedInstance { return [[MyClass alloc] init]; } @end
      
      





そして、もしすべてが迅速かつ簡単に再び明確になった場合、objective-cでは初期化に問題があります。









- (instancetype)init



利用できないと以前に言ったので、それは非常に論理的です。 また、クラス内を含めて利用できません。 どうする 実装ファイルにプライベート初期化子を記述し、静的メソッドで使用します。







 // Objective-C @implementation MyClass - (instancetype)initPrivate { self = [super init]; return self; } + (instancetype)sharedInstance { return [[MyClass alloc] initPrivate]; } @end
      
      





(はい、そしてヘッダーファイルに+ (instancetype)sharedInstance



を置くことを忘れないでください、それはパブリックでなければなりません)







これですべてがコンパイルされ、この方法でクラスのオブジェクトを取得できます。







 // Objective-C [MyClass sharedInstance]
      
      





 // Swift MyClass.shared()
      
      





シングルトンはほぼ準備ができています。 オブジェクトが一度だけ作成されるように、静的メソッドを修正するためだけに残ります。







 // Objective-C @implementation Singleton - (instancetype)initPrivate { self = [super init]; return self; } + (instancetype)sharedInstance { static Singleton *uniqueInstance = nil; if (nil == uniqueInstance) { uniqueInstance = [[Singleton alloc] initPrivate]; } return uniqueInstance; } @end
      
      





 // Swift class Singleton { private static var uniqueInstance: Singleton? private init() {} static func shared() -> Singleton { if uniqueInstance == nil { uniqueInstance = Singleton() } return uniqueInstance! } }
      
      





ご覧のとおり、このために、クラスの作成されたオブジェクトが一度格納される静的変数が必要でした。 静的メソッドを呼び出すたびに、 nil



がチェックされ、オブジェクトが既に作成されてこの変数に書き込まれている場合、再作成されません。 シングルトンの準備ができました、乾杯! :)







今、本からいくつかの例。



したがって、チョコレート工場があり、調理にはハイテクチョコレートとミルクヒーター(私はミルクチョコレートが大好きです)を使用しています。これはプログラムコードによって制御されます。







 // Objective-C //   ChocolateBoiler.h @interface ChocolateBoiler : NSObject - (void)fill; - (void)drain; - (void)boil; - (BOOL)isEmpty; - (BOOL)isBoiled; @end //   ChocolateBoiler.m @interface ChocolateBoiler () @property (assign, nonatomic) BOOL empty; @property (assign, nonatomic) BOOL boiled; @end @implementation ChocolateBoiler - (instancetype)init { self = [super init]; if (self) { self.empty = YES; self.boiled = NO; } return self; } - (void)fill { if ([self isEmpty]) { // fill boiler with milk and chocolate self.empty = NO; self.boiled = NO; } } - (void)drain { if (![self isEmpty] && [self isBoiled]) { // drain out boiled milk and chocolate self.empty = YES; } } - (void)boil { if (![self isEmpty] && ![self isBoiled]) { // boil milk and chocolate self.boiled = YES; } } - (BOOL)isEmpty { return self.empty; } - (BOOL)isBoiled { return self.boiled; } @end
      
      





 // Swift class ChocolateBoiler { private var empty: Bool private var boiled: Bool init() { self.empty = true self.boiled = false } func fill() { if isEmpty() { // fill boiler with milk and chocolate self.empty = false self.boiled = false } } func drain() { if !isEmpty() && isBoiled() { // drain out boiled milk and chocolate self.empty = true } } func boil() { if !isEmpty() && !isBoiled() { // boil milk and chocolate self.boiled = true } } func isEmpty() -> Bool { return empty } func isBoiled() -> Bool { return boiled } }
      
      





ご覧のとおり、ヒーターは最初に混合物で満たされ( fill



)、その後沸騰させ( boil



)、その後ミルクチョコレートの製造に移されます( drain



)。 問題を回避するには、プログラム内にヒーターを制御するクラスのインスタンスが1つしかないことを確認する必要があります。そのため、プログラムコードを変更します。







 // Objective-C @implementation ChocolateBoiler - (instancetype)initPrivate { self = [super init]; if (self) { self.empty = YES; self.boiled = NO; } return self; } + (instancetype)sharedInstance { static ChocolateBoiler *uniqueInstance = nil; if (nil == uniqueInstance) { uniqueInstance = [[ChocolateBoiler alloc] initPrivate]; } return uniqueInstance; } // other methods @end
      
      





 // Swift class ChocolateBoiler { private var empty: Bool private var boiled: Bool private static var uniqueInstance: ChocolateBoiler? private init() { self.empty = true self.boiled = false } static func shared() -> ChocolateBoiler { if uniqueInstance == nil { uniqueInstance = ChocolateBoiler() } return uniqueInstance! } // other methods }
      
      





だから、すべてが大丈夫です。 クラスのオブジェクトは1つだけであり、工場で予期しない状況が発生しないことを100%確信しています(正確には100%?)。 そして、objective-cのコードがかなりよさそうだとすれば、swiftは十分な速さではありません。 少し書き直してみましょう。







 // Swift class ChocolateBoiler { private var empty: Bool private var boiled: Bool static let shared = ChocolateBoiler() private init() { self.empty = true self.boiled = false } // other methods }
      
      





事実は、 shared



れている静的定数に孤立したオブジェクトを安全に格納でき、 nil



をチェックしてこのためのメソッド全体を記述する必要がないということです。 オブジェクト自体は、この定数に最初にアクセスしたときに作成され、一度書き込まれます。







しかし、マルチスレッドについてはどうでしょうか?



プログラムのフローで作業を使用する瞬間まで、すべてが正確に機能します。 シングルトンスレッドを安全にする方法は?







繰り返しになりますが、結局のところ、追加のアクションを実行する必要はまったくありません。 定数は既にスレッドセーフです。これは、値を一度だけ書き込むことができ、これにより最初に到達するスレッドが作成されるためです。







ただし、objective-cでは、静的メソッドを調整する必要があります。







 // Objective-C + (instancetype)sharedInstance { static ChocolateBoiler *uniqueInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ uniqueInstance = [[ChocolateBoiler alloc] initPrivate]; }); return uniqueInstance; }
      
      





dispatch_once



内のブロックdispatch_once



一度だけ実行されることdispatch_once



保証されており、最初のスレッドがそれに到達すると、他のすべてのスレッドはブロックの実行が終了するまで待機します。







まとめましょう。



そこで、objective-cおよびswiftでシングルトーンを記述する方法を見つけました。 両方の言語でSingleton



クラスの最終コードを提供します。







 // Objective-C //   Singleton.h @interface Singleton : NSObject - (instancetype)init UNAVAILABLE_ATTRIBUTE; + (instancetype)sharedInstance; @end //   Singleton.m @implementation Singleton - (instancetype)initPrivate { self = [super init]; return self; } + (instancetype)sharedInstance { static Singleton *uniqueInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ uniqueInstance = [[Singleton alloc] initPrivate]; }); return uniqueInstance; } @end
      
      





 // Swift class Singleton { static let shared = Singleton() private init() {} }
      
      





追伸



すべての読者とコメンテーターに聞いてみたい。不正確なものを見たり、コードの記述方法を知っているなら、私はもっと正確に/もっときれいに/もっと正確に書いた-それについて教えてください。 私は他の人を教えるためではなく、自分で学ぶためにここに書いています。 結局のところ、勉強している間、私たちは若いままです。







ご清聴ありがとうございました。








All Articles