1. 程式人生 > 實用技巧 >Swift專案相容Objective-c問題彙總

Swift專案相容Objective-c問題彙總

一、解決問題

Swift專案需要使用封裝好的Objective-c元件、第三方類庫,蘋果提供的解決方案能夠處理日常大部分需求,但還不能稱之為完美,混編過程中會遇到很多問題。本文將Swift相容Objective-c的問題彙總,以幫助大家更好的使用Swift,內容列表如下:

1. Swift呼叫Objective-c程式碼

2. Objective-c呼叫Swift程式碼

3. Swift相容Xib/Storyboard

4. Objective-c巧妙呼叫不相容的Swift方法

5. 多Target編譯錯誤解決

6. 第三方類庫支援

二、基礎混合程式設計

Swift與Objective-c的程式碼相互呼叫,並不像Objective-c與C/C++那樣方便,需要做一些額外的配置工作。無論是Swift呼叫Objective-c還是Objective-c呼叫Swift,Xcode在處理上都需要兩個步驟:

2.1 Swift呼叫Objective-c程式碼

Xcode對於Swift呼叫Objective-c程式碼,除巨集定義外,其它支援相對完善。

2.1.1 使用Objetvie-c的第一步

告訴Xcode、哪些Objective-c類要使用,新建.h標頭檔案,檔名可以任意取,建議採用**"專案名-Bridging-Header.h"**命令格式。

Tips

Swift之IOS專案,在Xcode6建立類檔案,預設會自動選擇OS X標籤下的檔案,這時*一定要選擇iOS標籤*下的檔案,否則會出現語法智慧提示不起作用,嚴重時會導致打包出錯。

2.1.2 第二步,Target配置,使建立的標頭檔案生效

設定**Objective-C Bridging Header**時,路徑要配置正確,例如:建立的名為**"ILSwift-Bridging-Header.h"**檔案,存於ILSwift專案資料夾的根目錄下,寫法如下:

ILSwift/ILSwift-Bridging-Header.h

當然,在新專案中,直接建立一個Objective-c類,Xcode會提示:

直接選擇**Yes**即可,如果不小心點了其它按鈕,可以按照上面的步驟一步一步新增。

2.2 Objective-c呼叫Swift程式碼

2.2.1 Objective-c呼叫Swift程式碼兩個步驟

第一步告訴Xcode哪些類需要使用(繼承自NSObject的類自動處理,不需要此步驟),通過關鍵字**@objc(className)**來標記

importUIKit
@objc(ILWriteBySwift)
classILWriteBySwift{
varname:String!

classfuncnewInstance()->ILWriteBySwift{
returnILWriteBySwift()
}
}

第二步引入標頭檔案,Xcode標頭檔案的命名規則為

$(SWIFT_MODULE_NAME)-Swift.h

示例如下:

#import"ILSwift-Swift.h"

Tips

不清楚**SWIFT_MODULE_NAME**可通過以下步驟檢視

2.2.2找不到$(SWIFT_MODULE_NAME)-Swift.h

1.遇到此問題可按以下步驟做常規性檢查

1.確定匯入SWIFT_MODULE_NAME)-Swift.h標頭檔案的檔名正確

2.SWIFT_MODULE_NAME)-Swift.h在clean後沒有重新構建,執行Xcode->Product->Build

2.標頭檔案迴圈

在混合程式設計的專案中,由於兩種語言的同時使用,經常會出現以下需求:在Swift專案中需要使用Objectvie-c寫的A類,而A類又會用到Swift的一些功能,標頭檔案的迴圈,導致編譯器不能正確構建**$(SWIFT_MODULE_NAME)-Swift.h**,遇到此問題時,在.h檔案做如下處理

//刪除以下標頭檔案
//#import"ILSwift-Swift.h"
//通過程式碼匯入類
@classILSwiftBean;

在Objevtive-c的.m檔案最上面,新增

#import"ILSwift-Swift.h"

出現**Use of undecalared identifier**錯誤或者找不到方法,如下:

引起的原因有以下幾種可能:

1.使用的Swift類不是繼承自NSObject,加入關鍵字即可

2.SWIFT_MODULE_NAME)-Swift.h沒有實時更新,Xcode->Product->Build

3.此Swift檔案中使用了Objective-c不支援的型別或者語法,如private

出現**部分方法找不到**的問題,Xcode無智慧提示:

此方法使用了Objective-c不支援的型別或者語法

蘋果官方給出的不支援轉換的型別

  • Generics

  • Tuples

  • Enumerations defined in Swift

  • Structures defined in Swift

  • Top-level functions defined in Swift

  • Global variables defined in Swift

  • Typealiases defined in Swift

  • Swift-style variadics

  • Nested types

  • Curried functions

三、Xib/StoryBoard支援

Swift專案在使用Xib/StoryBoard時,會遇到兩種不同的問題

1.Xib:不載入檢視內容

2.Storyboard:找不到類檔案

###3.1 Xib不載入檢視內容

在建立UIViewController時,預設選中Xib檔案,在Xib與類檔名一致時,可通過以下程式碼例項化:

letcontroller=ILViewController()

執行,介面上空無一物,Xib沒有被載入。解決辦法,在類的前面加上**@objc(類名)**,例如:

importUIKit
@objc(ILViewController)
classILViewController:UIViewController{

}

Tips:

StoryBoard中建立的UIViewController,不需要**@objc(類名)**也能夠保持相容

3.2 Storyboard找不到類檔案

Swift語言引入了Module概念,在通過關鍵字**@objc(類名)**做轉換的時候,由於Storboard沒有及時更新Module屬性,會導致如下兩種型別錯誤:

3.2.1 用**@objc(類名)**標記的Swift類或者Objective-c類可能出現錯誤:

2015-06-02 11:27:42.626 ILSwift[2431:379047] Unknown class _TtC7ILSwift33ILNotFindSwiftTagByObjcController in Interface Builder file.

解決辦法,按下圖,選中Module中的空白,直接回車

####3.2.2 無**@objc(類名)**標記的Swift類

2015-06-02 11:36:29.788 ILSwift[2719:417490] Unknown class ILNotFindSwiftController in Interface Builder file.

解決辦法,按下圖,選擇正確的Module

3.產生上面錯誤的原因:

在設定好Storyboard後,直接在類檔案中,新增或者刪除**@objc(類名)**關鍵字,導致Storyboard中 Module屬性沒有自動更新,所以一個更通用的解決辦法是,讓Storyboard自動更新Module,如下:

3.3 錯誤模擬Demo下載

為了能夠讓大家更清楚的瞭解解決流程,將上面的錯誤進行了模擬,想動手嘗試解決以上問題的同學可以直接下載Demo

四、Objective-c巧妙呼叫不相容的Swift方法

在Objective-c中呼叫Swift類中的方法時,由於部分Swift語法不支援轉換,會遇到無法找到對應方法的情況,如下:

importUIKit
enumHTTPState{
caseSucced,Failed,NetworkError,ServerError,Others
}
classILHTTPRequest:NSObject{

classfuncrequestLogin(userName:String,password:String,callback:(state:HTTPState)->(Void)){
dispatch_async(dispatch_get_global_queue(0,0),{()->Voidin
NSThread.sleepForTimeInterval(3)
dispatch_async(dispatch_get_main_queue(),{()->Voidin
callback(state:HTTPState.Succed)
})
})
}

}

對應的**$(SWIFT_MODULE_NAME)-Swift.h**檔案為:

SWIFT_CLASS("_TtC12ILSwiftTests13ILHTTPRequest")
@interfaceILHTTPRequest:NSObject
-(SWIFT_NULLABILITY(nonnull)instancetype)initOBJC_DESIGNATED_INITIALIZER;
@end

從上面的標頭檔案中可以看出,方法**requestLogin**使用了不支援的Swift列舉,轉換時方法被自動忽略掉,有以下兩種辦法,可以巧妙解決類似問題:

4.1 用支援的Swift語法包裝

在Swift檔案中,新增一個可相容包裝方法**wrapRequestLogin**,注意此方法中不能使用不相容的型別或者語法

importUIKit
enumHTTPState:Int{
caseSucced=0,Failed=1,NetworkError=2,ServerError=3,Others=4
}
classILHTTPRequest:NSObject{

classfuncrequestLogin(userName:String,password:String,callback:(state:HTTPState)->(Void)){
dispatch_async(dispatch_get_global_queue(0,0),{()->Voidin
NSThread.sleepForTimeInterval(3)
dispatch_async(dispatch_get_main_queue(),{()->Voidin
callback(state:HTTPState.Succed)
})
})
}

classfuncwrapRequestLogin(userName:String,password:String,callback:(state:Int)->(Void)){
self.requestLogin(userName,password:password){(state)->(Void)in
callback(state:state.rawValue)
}
}

}

對應的**$(SWIFT_MODULE_NAME)-Swift.h**檔案為:

SWIFT_CLASS("_TtC12ILSwiftTests13ILHTTPRequest")
@interfaceILHTTPRequest:NSObject
+(void)wrapRequestLogin:(NSString*__nonnull)userNamepassword:(NSString*__nonnull)passwordcallback:(void(^__nonnull)(NSInteger))callback;
-(SWIFT_NULLABILITY(nonnull)instancetype)initOBJC_DESIGNATED_INITIALIZER;
@end

此時,我們可以在Objective-c中直接使用包裝後的方法**wrapRequestLogin**

4.2 巧妙使用繼承

使用繼承可以支援所有的Swift型別,主要的功能在Objective-c中實現,不支援的語法在Swift檔案中呼叫,例如,**ILLoginSuperController**做為父類

@interfaceILLoginSuperController:UIViewController
@property(weak,nonatomic)IBOutletUITextField*userNameField;
@property(weak,nonatomic)IBOutletUITextField*passwordField;
-(IBAction)loginButtonPressed:(id)sender;
@end
////////////////////////////////////////////////////////////////
@implementationILLoginSuperController
-(IBAction)loginButtonPressed:(id)sender
{
}
@end
```
建立Swift檔案,繼承自**ILLoginSuperController**,在此Swift檔案中呼叫那些不支援的語法
```ruby
importUIKit
classILLoginController:ILLoginSuperController{
overridefuncloginButtonPressed(sender:AnyObject!){
ILHTTPRequest.requestLogin(self.userNameField.text,password:self.passwordField.text){(state)->(Void)in
//具體業務邏輯
}
}

}

五、多Target編譯錯誤解決

在使用多Target時,會出現一些編譯錯誤

5.1 Use of undeclared type

此類錯誤,是因為當前執行的Target找不到必須編譯檔案。將檔案新增到Target即可,如下支援**ILSwiftTests** Target,選中**ILSwiftTests**前的複選框即可

5.2 does not have a member named

此類錯誤可能由於如下兩種原因引起,解決辦法同上:

1.此方法來自父類,父類檔案沒有加入到當前Target

2.此方法來自擴充套件,擴充套件沒有加入到當前Target

Tips

如果檢查發現,所有的類檔案都已經準確新增到Target中,但編譯還是不通過,此時著重檢查橋接檔案是否正確設定,是否將相應的標頭檔案加入到了橋接檔案中。如無特別要求,建議將所有Target的橋接檔案全都指向同一檔案。關於橋接檔案的設定,請參考**2.1**

六、第三方類庫支援

Swift專案取消了預編譯檔案,一些第三方Objective-c庫沒有匯入必要框架(如UIKit)引起編譯錯誤

6.1 Cocoapods找不到.o檔案

在使用了Cocoapods專案中,會出現部分類庫的.o檔案找不到,導致此種錯誤主要是以下兩種問題:

1.類庫本身存在編譯錯誤

2.Swift沒有預編譯,UIKit等沒有匯入

將此庫檔案中的程式碼檔案直接加到專案中,編譯,解決錯誤

6.2 JSONModel支援

在Swift中可以使用JSONModel部分簡單功能,一些複雜的資料模型建議使用Objevtive-c

importUIKit
@objc(ILLoginBean)
publicclassILLoginBean:JSONModel{
varuserAvatarURL:NSString?
varuserPhone:NSString!
varuid:NSString!

}

Tips

在Swift使用JSONModel框架時,欄位只能是NSFoundation中的支援型別,Swift下新新增的String、Int、Array等都不能使用

6.3 友盟統計

Swift專案中引入友盟統計SDK會出現**referenced from**錯誤:

解決辦法,找到**Other Linker Flags**,新增**-lz**

七、綜述

現在大部分成熟的第三方框架都是使用Objective-c寫的,開發時不可避免的涉及到兩種語言的混合程式設計,期間會遇到很多奇怪的問題。因為未知才有探索的價值,Swift的簡潔快速,能夠極大的推進開發進度。所以從今天開始,大膽的開始嘗試

我有一杯灑,可以慰風尖,哈哈~~哈~

來源:嘉定網站建設