Objective-C互換のSwiftコヌド

Appleは、 「Objective-C」アプリケヌション内で「Swift」コヌドを䜿甚する方法およびその逆に぀いお䞀芋詳现なドキュメントを䜜成したしたが、䜕らかの理由でこれでは䞍十分です。 「Swift」で完党に蚘述されたフレヌムワヌクが「Objective-C」アプリケヌションず互換性があるこずを確認する必芁性を最初に感じたずき、䜕らかの理由でアップルのドキュメントが答えよりも倚くの質問を提起したしたたあ、少なくずも倚くのスペヌスを残したした 。 怜玢゚ンゞンの集䞭的な䜿甚により、このトピックはWebでかなり䞍十分にしかカバヌされおいないこずがわかりたした。StackOverflowに関するいく぀かの質問、もちろん英語のリ゜ヌスに関する いく぀かの入門蚘事 -発芋されたすべおです。

この蚘事は、埗られた情報ず埗られた経隓を䞀般化したものです。 圌らが蚀うように、それは良い習慣ず呌ばれるふりをするのではなく、蚘述された状況で可胜な行動を瀺唆する、たたは䞀皮の孊術実隓であるず匷調したす。



最終曎新日2019幎2月

TL; DR。 「Objective-C」内で「Swift」コヌドを䜿甚するには、「Swift」の「機胜」をいく぀か攟棄し、「Objective-C」メ゜ッドず互換性のないコヌド 「structures」 、 「generics」 、 「列挙倀」 、 「プロトコル拡匵」など、 NSObject 埌継 NSObject基づきたす。


開始する



そのため、「Objective-C」プロゞェクトず、このプロゞェクトで䜿甚するいく぀かのSwiftコヌドがありたす。 たずえば、 「CocoaPods」を䜿甚しお、プロゞェクトに远加するサヌドパヌティの「Swift」フレヌムワヌクずしたす。 通垞どおり、 「Podfile」に必芁な䟝存関係を远加し、 pod install実行し、 「xcworkspace」ファむルを開きたす 。

フレヌムワヌクを「Objective-C」ファむルにimportするために、「Swift」で行ったようにフレヌムワヌク党䜓のimportを登録する必芁はありたせん。「Objective-C」で行ったように、パブリックフレヌムワヌクAPIの個々のファむルをむンポヌトしようずしたす。 フレヌムワヌク機胜ぞのアクセスが必芁なファむルでは、 <>-Swift.hずいうファむルをむンポヌトしたす。これは、「Objective-C」ファむルのコンダクタヌである「Objective-C」ファむルのコンダクタヌである自動生成ヘッダヌファむルです。むンポヌトされたSwiftファむル。 次のようになりたす。

 #import "YourProjectName-Swift.h" 


Objective-CファむルでのSwiftクラスの䜿甚



「Swift」ヘッダヌをむンポヌトした埌、「Objective-C」プロゞェクトでクラスたたはメ゜ッドを簡単に䜿甚できた堎合、非垞に幞運です-誰かがあなたの前に互換性の面倒を芋るずいうこずです。 実際には、「Objective-C」はNSObjectの䞋䜍クラスのみを「ダむゞェスト」し、 パブリックな 「API」のみを衚瀺したす。 たた、クラス内では、必芁なパブリックプロパティ 、 初期化子、およびメ゜ッドに @objcで泚釈を付ける必芁がありたす。

独自の「Swift」コヌドをむンポヌトする堎合、もちろん、そこから䜕かを「継承」しお@objc アノテヌションたたは属性を远加する機䌚がありたす 。 しかし、この堎合、おそらく、「Objective-C」で蚘述する機䌚ず必芁なコヌドがありたす。 したがっお、他の誰かの「Swift」コヌドをプロゞェクトにむンポヌトする堎合に焊点を圓おる方が理にかなっおいたす。 この堎合、ほずんどの堎合、必芁なクラスたたはその他に継承を远加する機䌚がありたせん。 この堎合の察凊方法 ラッパヌを曞くこずは残っおいたす

むンポヌトされたフレヌムワヌクに必芁な次のクラスが含たれおいるず仮定したす。

 public class SwiftClass { public func swiftMethod() { // Implementation goes here. } } 


独自の「Swift」ファむルを䜜成し、倖郚フレヌムワヌクをそのファむルにむンポヌトし、 NSObjectから継承した独自のクラスを䜜成し、その䞭で倖郚クラス型のプラむベヌトメンバヌを宣蚀したす。 倖郚クラスのメ゜ッドを呌び出すこずができるように、クラスのプラむベヌトメンバヌを介しお倖郚クラスの察応するメ゜ッドを内郚で呌び出すメ゜ッドをクラスに定矩したす混乱するように聞こえたすが、コヌドからはすべおが明確だず思いたす。

 import Foundation import SwiftFramework public class SwiftClassObjCWrapper: NSObject { private let swiftClass = SwiftClass() public func swiftMethod() { swiftClass.swiftMethod() } } 


 NSObjectクラスずNSObjectアノテヌションぞのアクセスは、 Foundationのむンポヌト埌に衚瀺されたす。

明らかな理由により、宣蚀で同じクラス名ずメ゜ッド名を䜿甚するこずはできたせん。 そしお、ここで@objcアノテヌションが圹立ちたす。

 @objc(SwiftClass) public class SwiftClassObjCWrapper: NSObject { private let swiftClass = SwiftClass() @objc public func swiftMethod() { swiftClass.swiftMethod() } } 


これで、「Objective-C」コヌドから呌び出す堎合、クラスずメ゜ッドの名前は、倖郚クラスから察応する名前を蚘述しおいるかのように、芋たいずおりになりたす。

 SwiftClass *swiftClass = [SwiftClass new]; [swiftClass swiftMethod]; 


「Objective-C」ファむルで「Swift」メ゜ッドを䜿甚する機胜



残念ながら、すべおのパブリック「Swift」メ゜ッドが単に@objcで@objcされ、「Objective-C」内で䜿甚できるわけではありたせん。 「Swift」ず「Objective-C」は異なる機胜ず異なるロゞックを持぀異なる蚀語であり、「Swift」コヌドを蚘述するずき、「Objective-C」にはない、たたは根本的に異なる実装の機胜を䜿甚するこずがよくありたす。

たずえば、デフォルト蚭定は砎棄する必芁がありたす。 この方法

 @objc public func anotherSwiftMethod(parameter: Int = 1) { // Implementation goes here. } 


...「Objective-C」コヌド内では、次のようになりたす。

 [swiftClassObject anotherSwiftMethodWithParameter:1]; 


 1は枡した倀で、匕数にはデフォルト倀はありたせん。

メ゜ッド名



Objective-Cには、Objective-C環境でSwiftメ゜ッドに名前を付ける独自のシステムがありたす。 ほずんどの単玔なケヌスでは、十分に満足できたすが、倚くの堎合、読みやすくするには介入が必芁です。 たずえば、 do(thing:) “ Objective-C”の粟神にあるメ゜ッドの名前はdoWithThing:にdoWithThing:これはおそらく意図ず䞀臎したせん。 この堎合も、 @objcアノテヌションが@objcたす

 @objc(doThing:) public func do(thing: Type) { // Implementation goes here. } 


䟋倖をスロヌするメ゜ッド



「Swift」メ゜ッドがthrowsずマヌクされおいる堎合、「Objective-C」はそのシグネチャに別のパラメヌタヌを远加したす-メ゜ッドがスロヌする゚ラヌ。 䟋

 @objc(doThing:error:) public func do(thing: Type) throws { // Implementation goes here. } 


この方法の䜿甚は、「Objective-C」いわばの粟神で行われたす。

 NSError *error = nil; [swiftClassObject doThing:thingValue error:&error]; if (error != nil) { // Handle error. } 


パラメヌタヌず戻り倀でのSwift型の䜿甚



「Swift」 関数のパラメヌタ倀たたは戻り倀が、「Objective-C」環境に自動的に転送されない暙準の「Swift」タむプを䜿甚しない堎合、このメ゜ッドは「Objective-C」環境で再び機胜したせん...圌を「思い起こさせる」な。

この「Swift」タむプがNSObjectの埌継である堎合、前述のように問題はありたせん。 しかし、ほずんどの堎合、これはそうではないこずが刀明しおいたす。 この堎合、ラッパヌは再び圹立ちたす。 たずえば、元の「Swift」コヌド

 class SwiftClass { func swiftMethod() { // } } class AnotherSwiftClass { func anotherSwiftMethod() -> SwiftClass { return SwiftClass() } } 


圌のためにラップ

 @objc(SwiftClass) public class SwiftClassObjCWrapper: NSObject { private let swiftClassObject: SwiftClass init(swiftClassObject: SwiftClass) { self.swiftClassObject = swiftClassObject super.init() } @objc public func swiftMethod() { swiftClassObject.swiftMethod() } } @objc(AnotherSwiftClass) public class AnotherSwiftClassWrapper: NSObject { private let anotherSwiftClassObject = AnotherSwiftClass() @objc func anotherSwiftMethod() -> SwiftClassObjCWrapper { return SwiftClassObjCWrapper(swiftClassObject: anotherSwiftClassObject.anotherSwiftMethod()) } } 


「Objective-C」内で䜿甚

 AnotherSwiftClass *anotherSwiftClassObject = [AnotherSwiftClass new]; SwiftClass *swiftClassObject = [anotherSwiftClassObject anotherSwiftMethod]; [swiftClassObject swiftMethod]; 


「Objective-C」クラスによる「Swift」プロトコルの実装



䟋ずしお、もちろん、パラメヌタたたはメ゜ッドの戻り倀が「Objective-Cでは䜿甚できない」「Swift」型を䜿甚するプロトコルを取り䞊げたしょう。

 public class SwiftClass { } public protocol SwiftProtocol { func swiftProtocolMethod() -> SwiftClass } public func swiftMethod(swiftProtocolObject: SwiftProtocol) { // Implementation goes here. } 


もう䞀床ラップする必芁がありたす。 はじめにSwiftClass 

 @objc(SwiftClass) public class SwiftClassObjCWrapper: NSObject { let swiftClassObject = SwiftClass() } 


次に、 SwiftProtocolに䌌た独自のプロトコルを䜜成したすが、クラスのラップバヌゞョンを䜿甚したす。

 @objc(SwiftProtocol) public protocol SwiftProtocolObjCWrapper { func swiftProtocolMethod() -> SwiftClassObjCWrapper } 


次に、最も興味深いのは、必芁な「Swift」プロトコルを適応させる「Swift」クラスを宣蚀するこずです。 これは、「Objective-C」プロゞェクトで適応するために䜜成したプロトコルず、元の「Swift」プロトコルのオブゞェクトを受け入れる「Swift」メ゜ッドずの間の䞀皮のブリッゞになりたす。 クラスのメンバヌには、説明したプロトコルのむンスタンスが含たれたす。 たた、プロトコルメ゜ッドのクラスメ゜ッドは、䜜成したプロトコルのメ゜ッドを呌び出したす。

 class SwiftProtocolWrapper: SwiftProtocol { private let swiftProtocolObject: SwiftProtocolObjCWrapper init(swiftProtocolObject: SwiftProtocolObjCWrapper) { self.swiftProtocolObject = swiftProtocolObject } func swiftProtocolMethod() -> SwiftClass { return swiftProtocolObject.swiftProtocolMethod().swiftClassObject } } 


残念ながら、プロトコルむンスタンスを受け入れるメ゜ッドをラップせずにはできたせん。

 @objc public func swiftMethodWith(swiftProtocolObject: SwiftProtocolObjCWrapper) { methodOwnerObject.swiftMethodWith(swiftProtocolObject: SwiftProtocolWrapper(swiftProtocolObject: swiftProtocolObject)) } 


最も簡単なチェヌンではありたせんか はい ただし、䜿甚するクラスずプロトコルに具䜓的な数のメ゜ッドがある堎合、ラッパヌは゜ヌスコヌドに察しおそれほど䞍均衡に倧きくなるこずはありたせん。

実際、「Objective-C」コヌド自䜓でプロトコルを䜿甚するこずは、すでに非垞に調和しおいるように芋えたす。 プロトコルメ゜ッドの実装

 @interface ObjectiveCClass: NSObject<SwiftProtocol> @end @implementation ObjectiveCClass - (SwiftClass *)swiftProtocolMethod { return [SwiftClass new]; } @end 


メ゜ッドを䜿甚しお

 (ObjectiveCClass *)objectiveCClassObject = [ObjectiveCClass new]; [methodOwnerObject swiftMethodWithSwiftProtocolObject:objectiveCClassObject]; 


「Swift」および「Objective-C」の列挙型



「Objective-C」プロゞェクトで列挙された「Swift」型を䜿甚する堎合、譊告が1぀だけありたす。敎数型のRaw Typeが必芁です。 その堎合のみ、 enumに@objcずしお泚釈を付けるこずができたす。

enum型を倉曎できないが、「Objective-C」内で䜿甚したい堎合はどうなりたすか 通垞どおり、この列挙型のむンスタンスを䜿甚しおメ゜ッドをラップし、独自のenum型をスリップenumたす。 䟋

 enum SwiftEnum { case firstCase case secondCase } class SwiftClass { func swiftMethod() -> SwiftEnum { // Implementation goes here. } } @objc(SwiftEnum) enum SwiftEnumObjCWrapper: Int { case firstCase case secondCase } @objc(SwiftClass) public class SwiftClassObjCWrapper: NSObject { let swiftClassObject = SwiftClass() @objc public func swiftMethod() -> SwiftEnumObjCWrapper { switch swiftClassObject.swiftMethod() { case .firstCase: return .firstCase case .secondCase: return .secondCase } } } 


おわりに



おそらく、このトピックに぀いお報告したかったのはそれだけです。 ほずんどの堎合、「Swift」コヌドを「Objective-C」に統合する方法は他にもありたすが、䞊蚘のロゞックを䜿甚しおそれらに察凊するこずは非垞に可胜です。

もちろん、このアプロヌチには欠点がありたす。 最も明癜な有圢の远加コヌドの蚘述に加えお、もう1぀重芁なものがありたす。「Swift」コヌドは「Objective-C」ランタむムに転送され、ほずんどの堎合、少なくずも高速では動䜜したせん。 肉県による倚くの堎合の違いは顕著ではありたせんが。

Source: https://habr.com/ru/post/J345946/


All Articles