文档章节

Swift项目兼容Objective-c问题汇总

一叶博客
 一叶博客
发布于 2015/06/03 13:05
字数 2552
阅读 96
收藏 1

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

一、解决问题

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)**来标记

import UIKit
@objc(ILWriteBySwift)
class ILWriteBySwift {
    var name: String!
    
    class func newInstance() -> ILWriteBySwift {
        return ILWriteBySwift()
    }
}

第二步引入头文件,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"
//通过代码导入类
@class ILSwiftBean;

在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与类文件名一致时,可通过以下代码实例化:

let controller = ILViewController()


运行,界面上空无一物,Xib没有被加载。解决办法,在类的前面加上**@objc(类名)**,例如:

 

import UIKit
@objc(ILViewController)
class ILViewController: 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语法不支持转换,会遇到无法找到对应方法的情况,如下:

import UIKit
enum HTTPState {
    case Succed, Failed, NetworkError, ServerError, Others
}
class ILHTTPRequest: NSObject {
   
    class func requestLogin(userName: String, password: String, callback: (state: HTTPState) -> (Void)) {
        dispatch_async(dispatch_get_global_queue(0, 0), { () -> Void in
            NSThread.sleepForTimeInterval(3)
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                callback(state: HTTPState.Succed)
            })
        })
    }
    
}

对应的**$(SWIFT_MODULE_NAME)-Swift.h**文件为:

SWIFT_CLASS("_TtC12ILSwiftTests13ILHTTPRequest")
@interface ILHTTPRequest : NSObject
- (SWIFT_NULLABILITY(nonnull) instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

从上面的头文件中可以看出,方法**requestLogin**使用了不支持的Swift枚举,转换时方法被自动忽略掉,有以下两种办法,可以巧妙解决类似问题:

4.1 用支持的Swift语法包装

在Swift文件中,添加一个可兼容包装方法**wrapRequestLogin**,注意此方法中不能使用不兼容的类型或者语法

import UIKit
enum HTTPState: Int {
    case Succed = 0, Failed = 1, NetworkError = 2, ServerError = 3, Others = 4
}
class ILHTTPRequest: NSObject {
   
    class func requestLogin(userName: String, password: String, callback: (state: HTTPState) -> (Void)) {
        dispatch_async(dispatch_get_global_queue(0, 0), { () -> Void in
            NSThread.sleepForTimeInterval(3)
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                callback(state: HTTPState.Succed)
            })
        })
    }
    
    class func wrapRequestLogin(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")
@interface ILHTTPRequest : NSObject
+ (void)wrapRequestLogin:(NSString * __nonnull)userName password:(NSString * __nonnull)password callback:(void (^ __nonnull)(NSInteger))callback;
- (SWIFT_NULLABILITY(nonnull) instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

此时,我们可以在Objective-c中直接使用包装后的方法**wrapRequestLogin**

4.2 巧妙使用继承

使用继承可以支持所有的Swift类型,主要的功能在Objective-c中实现,不支持的语法在Swift文件中调用,例如,**ILLoginSuperController**做为父类

@interface ILLoginSuperController : UIViewController
@property (weak, nonatomic) IBOutlet UITextField *userNameField;
@property (weak, nonatomic) IBOutlet UITextField *passwordField;
- (IBAction)loginButtonPressed:(id)sender;
@end
////////////////////////////////////////////////////////////////
@implementation ILLoginSuperController
- (IBAction)loginButtonPressed:(id)sender
{
}
@end
```
创建Swift文件,继承自**ILLoginSuperController**,在此Swift文件中调用那些不支持的语法
``` ruby
import UIKit
class ILLoginController: ILLoginSuperController {
    override func loginButtonPressed(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

import UIKit
@objc(ILLoginBean)
public class ILLoginBean: JSONModel {
    var userAvatarURL: NSString?
    var userPhone: NSString!
    var uid: NSString!
    
}

Tips

在Swift使用JSONModel框架时,字段只能是NSFoundation中的支持类型,Swift下新添加的String、Int、Array等都不能使用

6.3 友盟统计

Swift项目中引入友盟统计SDK会出现**referenced from**错误:

解决办法,找到**Other Linker Flags**,添加**-lz**

七、综述

现在大部分成熟的第三方框架都是使用Objective-c写的,开发时不可避免的涉及到两种语言的混合编程,期间会遇到很多奇怪的问题。因为未知才有探索的价值,Swift的简洁快速,能够极大的推进开发进度。所以从今天开始,大胆的开始尝试

我有一杯洒,可以慰风尖,哈哈~~哈~

© 著作权归作者所有

一叶博客
粉丝 16
博文 13
码字总数 16627
作品 0
朝阳
项目经理
私信 提问
Swift项目兼容Objective-C问题汇总

本文是投稿文章,作者:一叶(博客) 欢迎将原创文章或者译文投给我们,投稿方式:support@cocoachina.com或者在首页点击“投稿爆料” 一、解决问题 Swift项目需要使用封装好的Objective-c组...

TomatosX
2015/06/12
101
0
[iOS开发]Swift调用Objective-C代码

本文写于2014.09.25 最近iOS开发之新编程语言Swift在iOS开发圈内反响比较大,国内外都有很多教程或小示例。 虽然Swift这门语言仍然在不断的进化之中,而且变动还是比较大,苹果公司也不承诺目...

Haffe
2014/09/25
8.5K
0
OSChina 技术专题之 Swift 苹果全新开发语言

Swift 是苹果新推出的编程语言,专门针对 OS X 和 iOS 的应用开发。Swift 在各个方面优于 Objective-C,也不会有那么多复杂的符号和表达式。同时,Swift 更加快速、便利、高效、安全。除此之...

OSC编辑部
2014/11/10
4.3K
4
在同个工程中使用 Swift 和 Objective-C(Swift 2.0更新)

本节包含内容: Mix and Match 概述(Mix and Match Overview) 在同个应用的 target 中导入(Importing Code from Within the Same App Target) 在同个 Framework 的 target 中导入(Impor...

法斗斗
2016/03/01
9
0
无论你是否主力 Swift, 这些事情你都可以了解一下

Swift 再等等?我的答案是:快上车 - 简书 主力 Swift 或许真的要等一等 - 知乎专栏 上一周有两篇文章, 分别讨论了大家在现阶段到底该不该用 Swift 在这里我不是想给出一个答案该不该用 Swif...

四娘
2017/03/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

如何更改iOS应用程序的名称?

我前几天用一个愚蠢的开发代码名称开始了一个iPhone项目,现在我想改变项目的名称,因为它已经接近完成了。 但是我不知道如何使用Xcode来做这件事,尝试在info.plist文件中更改应用程序的名称...

技术盛宴
21分钟前
2
0
关于win10tensorflow的配置(CPU+GPU)

主要内容 CPU篇 GPU篇 【前期准备与注意事项】 环境:window1064位+python36(37)+CUDA9.0+cuDNN7.6+tensorflow_gpu-1.12.0 软件:anaconda+pycharm 硬件:有nvidia显卡的笔记本或台式(非A...

放只虎归个山
30分钟前
1
0
C#中的多行字符串文字

有没有一种简单的方法可以在C#中创建多行字符串文字? 这是我现在所拥有的: string query = "SELECT foo, bar"+ " FROM table"+ " WHERE id = 42"; 我知道PHP有 <<<BLOCKBLOCK; C#是......

javail
37分钟前
3
0
微信支付之小微商户扫盲!支持信用卡,免营业执照!

微信支付商户申请面向线下小微商户开放,符合条件的微信支付服务商可为小微商户发起接入申请。无需营业执照。 小微商户日收款额度为5万元~30万元 。 信用卡支付日限额为1千;月限额1万。 结算...

吴伟祥
今天
4
0
大话SDWebImage(三)-- 图片下载层

四、图片下载层 SDWebImageDownloader是处理图片下载的类 4.1 图片下载步骤 首先介绍下dispatch_barrier,GCD中的dispatch_barrier目的是在并发队列实现串行的效果,创建下载任务SDWebImageD...

aron1992
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部