? ? ?在Objective-C中猴鲫,id類型表示任何Objective-C類的實(shí)例對(duì)象松靡。相反允跑,Swift將id類型導(dǎo)入為任意類型键俱。當(dāng)您將一個(gè)Swift實(shí)例傳遞給Objective-C API時(shí)兰绣,它將作為id參數(shù)橋接,以便在API中作為Objective-C對(duì)象使用编振。當(dāng)id值作為任意值導(dǎo)入到Swift時(shí)缀辩,運(yùn)行時(shí)將自動(dòng)處理到類引用或值類型的橋接臭埋。
?? ? 向下轉(zhuǎn)換對(duì)象以調(diào)用方法和訪問屬性
?? ? 當(dāng)您使用任何您知道底層類型的類型對(duì)象時(shí),通常將這些對(duì)象向下轉(zhuǎn)換為底層類型是非常有用的臀玄。但是瓢阴,因?yàn)槿魏晤愋投伎梢砸萌魏晤愋停跃幾g器不能保證將向下轉(zhuǎn)換為更特定的類型健无。
?? ? 您可以使用條件類型轉(zhuǎn)換操作符(as?)荣恐,它返回一個(gè)可選的值领追,該值是您嘗試向下轉(zhuǎn)換到的類型的可選值:
?? ? let userDefaults = UserDefaults.standard
?? ? let lastRefreshDate = userDefaults.object(forKey: "LastRefreshDate") // lastRefreshDate is of type Any?
?? ? if let date = lastRefreshDate as? Date {
?? ? print("\(date.timeIntervalSinceReferenceDate)")
?? ? }
?? ? 如果您完全確定對(duì)象的類型居触,那么可以使用強(qiáng)制向下轉(zhuǎn)換操作符(as!)
?? ? let myDate = lastRefreshDate as! Date
?? ? let timeInterval = myDate.timeIntervalSinceReferenceDate
?概述
?? ? 一些類似Objective-C api的目標(biāo)-操作-接受方法或?qū)傩悦鳛閰?shù),然后使用這些名稱動(dòng)態(tài)調(diào)用或訪問方法或?qū)傩允T赟wift中臼膏,使用#selector和#keyPath表達(dá)式分別將這些方法或?qū)傩悦硎緸檫x擇器或密鑰路徑硼被。
?? ? 使用選擇器來安排對(duì)Objective-C方法的調(diào)用
?? ? 在Objective-C中,選擇器是引用Objective-C方法名稱的類型渗磅。在Swift中嚷硫,Objective-C選擇器由選擇器結(jié)構(gòu)表示,使用# Selector表達(dá)式創(chuàng)建它們始鱼。
?? ? 在Swift中仔掸,通過將方法的名稱放在#selector表達(dá)式:#selector(MyViewController.tappedButton(_:))中,可以為Objective-C方法創(chuàng)建選擇器风响。要構(gòu)造屬性的Objective-C getter或setter方法的選擇器嘉汰,使用getter:或setter: label作為屬性名的前綴,比如#selector(getter: MyViewController.myButton)状勤。下面的示例顯示了作為目標(biāo)-操作模式一部分使用的選擇器,用于調(diào)用響應(yīng)touchUpInside事件的方法双泪。
?? ? import UIKit
?? ? class MyViewController: UIViewController {
?? ? let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
?? ? override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?) {
?? ? super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
?? ? let action = #selector(MyViewController.tappedButton)
?? ? myButton.addTarget(self, action: action, forControlEvents: .touchUpInside)
?? ? }
?? ? @objc func tappedButton(_ sender: UIButton?) {
?? ? print("tapped button")
?? ? }
?? ? required init?(coder: NSCoder) {
?? ? super.init(coder: coder)
?? ? }
?? ? }
?? ? 如果需要消除重載函數(shù)之間的歧義持搜,請(qǐng)使用括號(hào)表達(dá)式和as運(yùn)算符使#選擇器表達(dá)式明確地指向特定的重載。
?? ? 使用關(guān)鍵路徑動(dòng)態(tài)訪問Objective-C屬性
?? ? 在Objective-C中焙矛,鍵是標(biāo)識(shí)對(duì)象特定屬性的字符串葫盼。鍵路徑是一個(gè)點(diǎn)分隔鍵字符串,它指定要遍歷的對(duì)象屬性序列村斟。鍵和鍵路徑經(jīng)常用于鍵值編碼(KVC)贫导,這是一種使用字符串標(biāo)識(shí)符間接訪問對(duì)象屬性和關(guān)系的機(jī)制。
?? ? 使用#keyPath字符串表達(dá)式創(chuàng)建編譯器選中的鍵和鍵路徑蟆盹,KVC方法可以使用這些鍵和路徑孩灯,比如value(forKey:)和value(forKeyPath:)。#keyPath字符串表達(dá)式接受鏈?zhǔn)椒椒ɑ驅(qū)傩砸糜饫摹K€支持通過鏈中的可選值鏈接峰档,比如#keyPath(Person.bestFriend.name)。使用#keyPath字符串表達(dá)式創(chuàng)建的鍵路徑不會(huì)傳遞有關(guān)它們引用的屬性或方法的類型信息,這些屬性或方法引用接受鍵路徑的api讥巡。
?? ? 下面的示例定義了一個(gè)Person類掀亩,創(chuàng)建了它的兩個(gè)實(shí)例,并使用幾個(gè)#keyPath字符串表達(dá)式來訪問這些屬性的屬性:
?? ? class Person: NSObject {
?? ? @objc var name: String
?? ? @objc var friends: [Person] = []
?? ? @objc var bestFriend: Person? = nil
?? ? init(name: String) {
?? ? self.name = name
?? ? }
?? ? }
?? ? let gabrielle = Person(name: "Gabrielle")
?? ? let jim = Person(name: "Jim")
?? ? let yuanyuan = Person(name: "Yuanyuan")
?? ? gabrielle.friends = [jim, yuanyuan]
?? ? gabrielle.bestFriend = yuanyuan
?? ? #keyPath(Person.name)
?? ? // "name"
?? ? gabrielle.value(forKey: #keyPath(Person.name))
?? ? // "Gabrielle"
?? ? #keyPath(Person.bestFriend.name)
?? ? // "bestFriend.name"
?? ? gabrielle.value(forKeyPath: #keyPath(Person.bestFriend.name))
?? ? // "Yuanyuan"
?? ? #keyPath(Person.friends.name)
?? ? // "friends.name"
?? ? gabrielle.value(forKeyPath: #keyPath(Person.friends.name))
?? ? // ["Yuanyuan", "Jim"]
將Objective-C導(dǎo)入Swift
使用Swift訪問Objective-C代碼中的類和其他聲明欢顷。
您可以在單個(gè)項(xiàng)目中同時(shí)使用Objective-C和Swift文件槽棍,無論項(xiàng)目最初使用哪種語言。這使得創(chuàng)建混合語言應(yīng)用程序和框架目標(biāo)就像創(chuàng)建用單一語言編寫的應(yīng)用程序或框架目標(biāo)一樣簡(jiǎn)單抬驴。
在混合語言目標(biāo)中使用Swift代碼中的Objective-C聲明的過程略有不同炼七,這取決于您是在編寫應(yīng)用程序還是框架。這兩個(gè)過程描述如下怎爵。
導(dǎo)入應(yīng)用程序目標(biāo)中的代碼
要將一組Objective-C文件導(dǎo)入到同一個(gè)app target中的Swift代碼中特石,需要依賴Objective-C橋接頭文件將這些文件暴露給Swift。當(dāng)您將Swift文件添加到現(xiàn)有的Objective-C應(yīng)用程序或?qū)bjective-C文件添加到現(xiàn)有的Swift應(yīng)用程序時(shí)鳖链,Xcode提供了創(chuàng)建這個(gè)頭文件的功能姆蘸。
如果接受,Xcode將創(chuàng)建橋接頭文件和正在創(chuàng)建的文件芙委,并使用產(chǎn)品模塊名后面跟著“-Bridging-Header.h”來命名它們逞敷。或者灌侣,您可以通過選擇?File > New > File > [operating system] > Source > Header File.推捐,自己創(chuàng)建一個(gè)橋接頭文件。
編輯橋接頭侧啼,將Objective-C代碼暴露給Swift代碼:
在Objective-C橋接頭中牛柒,導(dǎo)入你想要公開給Swift的每個(gè)Objective-C頭。
在構(gòu)建設(shè)置中痊乾,在Swift編譯器-代碼生成中皮壁,確保Objective-C橋接頭構(gòu)建設(shè)置有到橋接頭文件的路徑。路徑應(yīng)該與項(xiàng)目相關(guān)哪审,類似于信息的方式蛾魄。plist路徑在構(gòu)建設(shè)置中指定。在大多數(shù)情況下湿滓,您不需要修改這個(gè)設(shè)置滴须。
在框架目標(biāo)中導(dǎo)入代碼
要在與Swift代碼相同的框架目標(biāo)文件中使用Objective-C聲明,您需要將這些文件導(dǎo)入Objective-C傘形標(biāo)題——框架的主標(biāo)題叽奥。通過配置雨傘頭導(dǎo)入Objective-C文件:
在構(gòu)建設(shè)置中扔水,在打包中,確保將框架目標(biāo)的定義模塊設(shè)置設(shè)置為Yes而线。
在傘形標(biāo)頭中铭污,導(dǎo)入要公開給Swift的每個(gè)Objective-C標(biāo)頭恋日。
Swift看到你在傘頭中公開的每個(gè)標(biāo)題。該框架中的Objective-C文件的內(nèi)容可以從該框架目標(biāo)中的任何Swift文件中自動(dòng)獲得嘹狞,不需要導(dǎo)入語句岂膳。使用Objective-C代碼中的類和其他聲明,使用與系統(tǒng)類相同的Swift語法磅网。
將Swift導(dǎo)入Objective-C
從Objective-C代碼庫(kù)中訪問Swift類型和聲明谈截。
概述
通過導(dǎo)入xcode生成的頭文件,您可以在項(xiàng)目中的Objective-C代碼中使用Swift聲明的類型涧偷。這個(gè)文件是Objective-C的頭文件簸喂,它聲明目標(biāo)中的Swift接口,您可以將它看作是Swift代碼的傘形頭文件燎潮。您不需要做任何特殊的事情來創(chuàng)建生成的header—只需導(dǎo)入它來在Objective-C代碼中使用它的內(nèi)容喻鳄。
標(biāo)題的名稱是由您的產(chǎn)品模塊名稱生成的,后面跟著“-Swift.h”确封。默認(rèn)情況下除呵,此名稱與產(chǎn)品名稱相同,任何非字母數(shù)字字符都用下劃線(_)替換爪喘。如果名稱以數(shù)字開頭颜曾,則將第一個(gè)數(shù)字替換為下劃線。
將Swift聲明導(dǎo)入Objective-C代碼的過程略有不同秉剑,這取決于您是在編寫應(yīng)用程序還是框架泛豪。這兩個(gè)過程描述如下。
導(dǎo)入應(yīng)用程序目標(biāo)中的代碼
當(dāng)您構(gòu)建應(yīng)用程序目標(biāo)時(shí)侦鹏,您可以使用這種語法將您的Swift代碼導(dǎo)入到同一目標(biāo)內(nèi)的任何Objective-C .m文件中诡曙,并替換適當(dāng)?shù)拿Q:
#import"ProductModuleName-Swift.h"
默認(rèn)情況下,生成的頭文件包含使用public或open修飾符標(biāo)記的Swift聲明的接口略水。如果您的應(yīng)用程序目標(biāo)有一個(gè)Objective-C橋接頭岗仑,生成的頭還包括用內(nèi)部修飾符標(biāo)記的接口。標(biāo)記為private或fileprivate修飾符的聲明不會(huì)出現(xiàn)在生成的頭文件中聚请,也不會(huì)公開給Objective-C運(yùn)行時(shí),除非它們顯式地標(biāo)記為@IBAction稳其、@IBOutlet或@objc屬性驶赏。在單元測(cè)試目標(biāo)內(nèi)部,通過將@testable放在product module import語句前面既鞠,您可以訪問導(dǎo)入的內(nèi)部聲明煤傍,就好像它們是公共的一樣。
在框架目標(biāo)中導(dǎo)入代碼
要在與Objective-C代碼相同的框架目標(biāo)中導(dǎo)入一組Swift文件嘱蛋,請(qǐng)將您的Swift代碼的xcode生成的頭文件導(dǎo)入到您想要使用Swift代碼的任何Objective-C .m文件中蚯姆。
因?yàn)樯傻臉?biāo)頭是框架的公共接口的一部分五续,所以在為框架目標(biāo)生成的標(biāo)頭中只會(huì)出現(xiàn)標(biāo)記為public或open修飾符的聲明。用內(nèi)部修飾符標(biāo)記并在從Objective-C類繼承的類中聲明的方法和屬性可以被Objective-C運(yùn)行時(shí)訪問龄恋。但是疙驾,它們?cè)诰幾g時(shí)不可訪問,并且不會(huì)出現(xiàn)在框架目標(biāo)的生成頭文件中郭毕。
在相同的框架下它碎,將Swift代碼導(dǎo)入Objective-C:
在構(gòu)建設(shè)置中,在打包中显押,確保為該框架目標(biāo)定義的模塊設(shè)置設(shè)置為Yes扳肛。
使用這種語法并替換適當(dāng)?shù)拿Q,將Swift代碼從該框架目標(biāo)導(dǎo)入到該目標(biāo)中的任何Objective-C .m文件中:
#import <ProductName/ProductModuleName-Swift.h>
使用前向聲明在Objective-C標(biāo)頭中包含Swift類
當(dāng)Objective-C頭文件中的聲明引用來自同一目標(biāo)的Swift類或協(xié)議時(shí)乘碑,導(dǎo)入生成的頭將創(chuàng)建循環(huán)引用挖息。為了避免這種情況,使用Swift類或協(xié)議的前向聲明在Objective-C接口中引用它兽肤。
// MyObjcClass.h@classMySwiftClass;
@protocolMySwiftProtocol;
@interfaceMyObjcClass : NSObject
?(MySwiftClass *)returnSwiftClassInstance;
- (id)returnInstanceAdoptingSwiftProtocol;
// ...@end
Swift類和協(xié)議的前向聲明只能用作方法和屬性聲明的類型套腹。