oc代码转swift代码网址

2020-03-09 19:00:00 olsQ93038o99S 阅读数 170

作者丨zhangferry

来源丨iOS成长之路

运行环境:Xcode 11.1 Swift5.0

最近参与的一个项目需要从Objective-C(以下简称OC)转到Swift,期间遇到了一些坑,于是有了这篇总结性的文档。如果你也有将OC项目Swift化的需求,可以作为参考。

OC转Swift有一个大前提就是你要对Swift有一定的了解,熟悉Swift语法,最好是完整看过一遍官方的Language Guide。

转换的过程分自动化和手动转译,鉴于自动化工具的识别率不能让人满意,大部分情况都是需要手动转换的。

自动化工具

有一个比较好的自动化工具Swiftify,可以将OC文件甚至OC工程整个转成Swift,号称准确率能达到90%。我试用了一些免费版中的功能,但感觉效果并不理想,因为没有使用过付费版,所以也不好评价它就是不好。

Swiftify还有一个Xcode的插件Swiftify for Xcode,可以实现对选中代码和单文件的转化。这个插件还挺不错,对纯系统代码转化还算精确,但部分代码还存在一些识别问题,需要手动再修改。

手动Swift化

桥接文件

如果你是在项目中首次使用Swift代码,在添加Swift文件时,Xcode会提示你添加一个 .h的桥接文件。如果不小心点了不添加还可以手动导入,就是自己手动生成一个 .h文件,然后在 BuildSettings>SwiftCompiler-General>Objective-CBridgingHeader中填入该 .h文件的路径。

这个桥接文件的作用就是供Swift代码引用OC代码,或者OC的三方库。

#import "Utility.h"
#import <Masonry/Masonry.h>

BridgingHeader的下面还有一个配置项是 Objective-CGeneratedInterfaceHeaderName,对应的值是 ProjectName-Swift.h。这是由Xcode自动生成的一个隐藏头文件,每次Build的过程会将Swift代码中声明为外接调用的部分转成OC代码,OC部分的文件会类似 pch一样全局引用这个头文件。因为是Build过程中生成的,所以只有 .m文件中可以直接引用,对于在 .h文件中的引用下文有介绍。

Appdelegate(程序入口)

Swift中没有 main.m文件,取而代之的是 @UIApplicationMain命令,该命令等效于原有的执行 main.m。所以我们可以把 main.m文件进行移除。

系统API

对于 UIKit框架中的大部分代码转换可以直接查看系统API文档进行转换,这里就不过多介绍。

property(属性)

Swift没有 property,也没有 copynonatomic等属性修饰词,只有表示属性是否可变的 letvar

注意点一OC中一个类分 .h.m两个文件,分别表示用于暴露给外接的方法,变量和仅供内部使用的方法变量。迁移到Swift时,应该将 .m中的property标为 private,即外接无法直接访问,对于 .h中的property不做处理,取默认的 internal,即同模块可访问。

对于函数的迁移也是相同的。

注意点二有一种特殊情况是在OC项目中,某些属性在内部( .m)可变,外部( .h)只读。这种情况可以这么处理:

private(set) var value: String

就是只对 valueset方法就行 private标记。

注意点三Swift中针对空类型有个专门的符号 ?,对应OC中的 nil。OC中没有这个符号,但是可以通过在 nullablenonnull表示该种属性,方法参数或者返回值是否可以空。

如果OC中没有声明一个属性是否可以为空,那就去默认值 nonnull

如果我们想让一个类的所有属性,函数返回值都是 nonnull,除了手动一个个添加之外还有一个宏命令。

NS_ASSUME_NONNULL_BEGIN
/* code */
NS_ASSUME_NONNULL_END

enum(枚举)

OC代码:

typedef NS_ENUM(NSInteger, PlayerState) {
PlayerStateNone= 0,
PlayerStatePlaying,
PlayerStatePause,
PlayerStateBuffer,
PlayerStateFailed,
};
typedef NS_OPTIONS(NSUInteger, XXViewAnimationOptions) {
XXViewAnimationOptionNone= 1<<  0,
XXViewAnimationOptionSelcted1= 1<<  1,
XXViewAnimationOptionSelcted2= 1<<  2,
}

Swift代码

enumPlayerState: Int{
case none = 0
case playing
case pause
case buffer
case failed
}
structViewAnimationOptions: OptionSet{
let rawValue: UInt
staticletNone= ViewAnimationOptions(rawValue: 1<<0)
staticletSelected1= ViewAnimationOptions(rawValue: 1<<0)
staticletSelected2= ViewAnimationOptions(rawValue: 1<< 2)
//...
}

Swift没有 NS_OPTIONS的概念,取而代之的是为了满足 OptionSet协议的 struct类型。

懒加载

OC代码:

- (MTObject*)object{
if(!_object) {
        _object = [MTObjectnew];
}
return _object;
}

Swift代码:

lazy varobject: MTObject= {
letobject= MTObject()
return imagobjecteView
}()

闭包

OC代码:

typedefvoid(^DownloadStateBlock)(BOOL isComplete);

Swift代码:

typealias DownloadStateBlock= ((_ isComplete: Bool) -> Void)

单例

OC代码:

+ (XXManager*)shareInstance {
staticdispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
});
return instance;
}

Swift对单例的实现比较简单,有两种方式:

第一种

swiftletshared=XXManager()// 声明在全局命名区(global namespace)ClassXXManager{}

你可能会疑惑,为什么没有 dispatch_once,如何保证多线程下创建的唯一性?其实是这样的,Swift中全局变量是懒加载,在AppDelegate中被初始化,之后所有的调用都会使用该实例。而且全局变量的初始化是默认使用 dispatch_once的,这保证了全局变量的构造器(initializer)只会被调用一次,保证了 shard原子性

第二种

ClassXXManager{
staticlet shared = XXManager()
privateoverride init() {
// do something
}
}

Swift 2 开始增加了 static关键字,用于限定变量的作用域。如果不使用 static,那么每一个 shared都会对应一个实例。而使用 static之后, shared成为全局变量,就成了跟上面第一种方式原理一致。可以注意到,由于构造器使用了 private 关键字,所以也保证了单例的原子性。

初始化方法和析构函数

对于初始化方法OC先调用父类的初始化方法,然后初始自己的成员变量。Swift先初始化自己的成员变量,然后在调用父类的初始化方法。

OC代码:

// 初始化方法
@interfaceMainView: UIView
@property(nonatomic, strong) NSString*title;
- (instancetype)initWithFrame:(CGRect)frame title:(NSString*)title NS_DESIGNATED_INITIALIZER;
@end
@implementationMainView
- (instancetype)initWithFrame:(CGRect)frame title:(NSString*)title {
if(self= [super initWithFrame:frame]) {
self.title = title;
}
returnself;
}
@end
// 析构函数
- (void)dealloc {
//dealloc
}

Swift代码:

classMainViewSwift: UIView{
let title: String
    init(frame: CGRect, title: String) {
self.title = title
super.init(frame: frame)
}
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
}
        deinit {
//deinit
}
}

函数调用

OC代码:

// 实例函数(共有方法)
- (void)configModelWith:(XXModel*)model {}
// 实例函数(私有方法)
- (void)calculateProgress {}
// 类函数
+ (void)configModelWith:(XXModel*)model {}

Swift代码

// 实例函数(共有方法)
func configModel(with model: XXModel) {}
// 实例函数(私有方法)
private func calculateProgress() {}
// 类函数(不可以被子类重写)
static func configModel(with model: XXModel) {}
// 类函数(可以被子类重写)
class func configModel(with model: XXModel) {}
// 类函数(不可以被子类重写)
classfinal func configModel(with model: XXModel) {}

OC可以通过是否将方法声明在 .h文件表明该方法是否为私有方法。Swift中没有了 .h文件,对于方法的权限控制是通过权限关键词进行的,各关键词权限大小为:private<fileprivate<internal<public<open

其中 internal为默认权限,可以在同一 module下访问。

NSNotification(通知)

OC代码:

// add observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(method) name:@"NotificationName"object:nil];
// post
[NSNotificationCenter.defaultCenter postNotificationName:@"NotificationName"object:nil];

Swift代码:

// add observer
NotificationCenter.default.addObserver(self, selector: #selector(method), name: NSNotification.Name(rawValue: "NotificationName"), object: nil)
// post
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "NotificationName"), object: self)

可以注意到,Swift中通知中心 NotificationCenter不带 NS前缀,通知名由字符串变成了 NSNotification.Name的结构体。

改成结构体的目的就是为了便于管理字符串,原本的字符串类型变成了指定的 NSNotification.Name类型。上面的Swift代码可以修改为:

extension NSNotification.Name{
staticletNotificationName= NSNotification.Name("NotificationName")
}
// add observer
NotificationCenter.default.addObserver(self, selector: #selector(method), name: .NotificationName, object: nil)
// post
NotificationCenter.default.post(name: .NotificationName, object: self)

protocol(协议/代理)

OC代码:

@protocolXXManagerDelegate<NSObject>
- (void)downloadFileFailed:(NSError*)error;
@optional
- (void)downloadFileComplete;
@end
@interfaceXXManager: NSObject
@property(nonatomic, weak) id<XXManagerDelegate> delegate;
@end

Swift中对 protocol的使用拓宽了许多,不光是 class对象, structenum也都可以实现协议。需要注意的是 structenum为指引用类型,不能使用 weak修饰。只有指定当前代理只支持类对象,才能使用 weak。将上面的代码转成对应的Swift代码,就是:

@objc protocol XXManagerDelegate{
    func downloadFailFailed(error: Error)
@objc optional func downloadFileComplete() // 可选协议的实现
}
classXXManager: NSObject{
    weak vardelegate: XXManagerDelegate?
}

@objc是表明当前代码是针对 NSObject对象,也就是 class对象,就可以正常使用weak了。

如果不是针对NSObject对象的delegate,仅仅是普通的class对象可以这样设置代理:

protocol XXManagerDelegate: class{
    func downloadFailFailed(error: Error)
}
classXXManager{
    weak vardelegate: XXManagerDelegate?
}

值得注意的是,仅 @objc标记的 protocol可以使用 @optional

Swift和OC混编注意事项

函数名的变化

如果你在一个Swift类里定义了一个delegate方法:

@objc protocol MarkButtonDelegate{
    func clickBtn(title: String)
}

如果你要在OC中实现这个协议,这时候方法名就变成了:

- (void)clickBtnWithTitle:(NSString*)title {
// code
}

这主要是因为Swift有指定参数标签,OC却没有,所以在由Swift方法名生成OC方法名时编译器会自动加一些修饰词,已使函数作为一个句子可以"通顺"。

在OC的头文件里调用Swift类

如果要在OC的头文件里引用Swift类,因为Swift没有头文件,而为了让在头文件能够识别该Swift类,需要通过 @class的方法引入。

@classSwiftClass;
@interfaceXXOCClass: NSObject
@property(nonatomic, strong) SwiftClass*object;
@end

对OC类在Swift调用下重命名

因为Swift对不同的module都有命名空间,所以Swift类都不需要添加前缀。如果有一个带前缀的OC公共组件,在Swift环境下调用时不得不指定前缀是一件很不优雅的事情,所以苹果添加了一个宏命令 NS_SWIFT_NAME,允许在OC类在Swift环境下的重命名:

NS_SWIFT_NAME(LoginManager)
@interfaceXXLoginManager: NSObject
@end

这样我们就将 XXLoginManager在Swift环境下的类名改为了 LoginManager

引用类型和值类型

  • struct 和 enum 是值类型,类 class 是引用类型。

  • String, Array和 Dictionary都是结构体,因此赋值直接是拷贝,而 NSStringNSArray 和 NSDictionary则是类,所以是使用引用的方式。

  • struct 比 class 更“轻量级”, struct 分配在栈中, class 分配在堆中。

id类型和AnyObject

OC中 id类型被Swift调用时会自动转成 AnyObject,他们很相似,但却其实概念并不一致。Swift中还有一个概念是 Any,他们三者的区别是:

  • id 是一种通用的对象类型,它可以指向属于任何类的对象,在OC中即是可以代表所有继承于 NSObject的对象。

  • AnyObject可以代表任何 class类型的实例。

  • Any可以代表任何类型,甚至包括 func类型。

从范围大小比较就是:id<AnyObject<Any

其他语法区别及注意事项(待补充)

1、Swift语句中不需要加分号 ;

2、关于Bool类型更加严格,Swift不再是OC中的非0就是真,真假只对应 truefalse

3、Swift类内一般不需要写 self,但是闭包内是需要写的。

4、Swift是强类型语言,必须要指定明确的类型。在Swift中 IntFloat是不能直接做运算的,必须要将他们转成同一类型才可以运算。

5、Swift抛弃了传统的 ++--运算,抛弃了传统的C语言式的 for循环写法,而改为 for-in

6、Swift的 switch操作,不需要在每个case语句结束的时候都添加 break

7、Swift对 enum的使用做了很大的扩展,可以支持任意类型,而OC枚举仅支持 Int类型,如果要写兼容代码,要选择Int型枚举。

8、Swift代码要想被OC调用,需要在属性和方法名前面加上 @objc

9、Swift独有的特性,如泛型, struct,非Int型的 enum等被包含才函数参数中,即使添加 @objc也不会被编译器通过。

10、Swift支持重载,OC不支持。

11、带默认值的Swift函数再被OC调用时会自动展开。

语法检查

对于OC转Swift之后的语法变化还有很多细节值得注意,特别是对于初次使用Swift这门语言的同学,很容易遗漏或者待着OC的思想去写代码。这里推荐一个语法检查的框架SwiftLint,可以自动化的检查我们的代码是否符合Swift规范。

可以通过 cocoapods进行引入,配置好之后,每次 Build的过程,Lint脚本都会执行一遍Swift代码的语法检查操作,Lint还会将代码规范进行分级,严重的代码错误会直接报错,导致程序无法启动,不太严重的会显示代码警告(⚠️)。

如果你感觉SwiftLint有点过于严格了,还可以通过修改 .swiftlint.yml文件,自定义属于自己的语法规范。

近期精彩内容推荐:  

 有个程序媛上司是什么体验

 一个天才程序员的黑帮大佬人生

 200行Python代码做一个换脸程序

 在 IntelliJ IDEA 中使用 Git,太方便了!

在看点这里好文分享给更多人↓↓

2015-07-02 19:02:46 u013230511 阅读数 1521

初始化函数

对于oc中的[[XX alloc]init] swift不需要alloc 也不需要init。直接调用以类名为函数名的工厂方法进行初始化。
声明的变量可以不显式声明类型

//objective-c
UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
//swift
let myTextField = UITextField(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 40.0)) //显式声明类型
let myTextField = UITextField(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 40.0)  //不声明类型

除此之外一般的OC类也可以这样使用

//objective-c
UIColor *color = [UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0];
//swift
let color = UIColor(red: 0.5, green: 0.0, blue: 0.5, alpha: 1.0)

访问属性

和OC一样,可以使用点(.) 操作符访问成员变量

myTextField.textColor = UIColor.darkGrayColor()
myTextField.text = "Hello world"

调用函数

和OC一样,可以使用点(.) 操作符调用函数
括号内填入参数

//objective-c
[myTableView insertSubview:mySubview atIndex:2];
//swift
myTableView.insertSubview(mySubview, atIndex: 2)

苹果原文链接

2015-10-07 11:36:00 weixin_30767921 阅读数 137

1.

Objectice-C code:

 1 NSShadow *shadow = [NSShadow new];
 2 
 3 [shadow setShadowColor:[UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0]];
 4 
 5 [shadow setShadowOffset:CGSizeMake(0, 1)];
 6 
 7 NSDictionary *attributes = @{
 8 
 9                                 NSForegroundColorAttributeName: [UIColor colorWithRed:220.0/255.0 green:104.0/255.0 blue:1.0/255.0 alpha:1.0],
10 
11                                 NSShadowAttributeName: shadow,
12 
13                                 NSFontAttributeName: [UIFont fontWithName:@"AmericanTypewriter" size:16.0]
14 
15                              };
16 
17 [self.navigationItem.rightBarButtonItem setTitleTextAttributes:attributes forState: UIControlStateNormal];
18 
19 // Or you can use.
20 
21 [[UIBarItem appearance] setTitleTextAttributes:attributes forState: UIControlStateNormal];

Swift Code:

// Bar title text color

let shadow = NSShadow()

shadow.shadowColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)

shadow.shadowOffset = CGSizeMake(0, 1)

let color : UIColor = UIColor(red: 220.0/255.0, green: 104.0/255.0, blue: 1.0/255.0, alpha: 1.0)

let titleFont : UIFont = UIFont(name: "AmericanTypewriter", size: 16.0)!

let attributes = [

                        NSForegroundColorAttributeName : color,

                        NSShadowAttributeName : shadow,

                        NSFontAttributeName : titleFont

                 ]

self.navigationItem.rightBarButtonItem?.setTitleTextAttributes(attributes, forState: UIControlState.Normal)

// Or you can use

UIBarItem.appearance().setTitleTextAttributes(attributes, forState: UIControlState.Normal)

 2.

OC:

    //设置导航栏字体颜色

    [self.navigationController.navigationBar setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor whiteColor],UITextAttributeTextColor,nil]];

    //[UIFont fontWithName:@"Arial-Bold" size:0.0], UITextAttributeFont

Swift:

UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName:UIColor.whiteColor(),

        NSFontAttributeName: UIFont(name: "Heiti SC", size: 24.0)!]

3.

OC: 

//改变UITabBarItem字体颜色

    [[UITabBarItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor colorWithRed:0 green:0.72 blue:0.69 alpha:1],UITextAttributeTextColor, nil] forState:UIControlStateSelected];

Swift:

let attributes =  [NSForegroundColorAttributeName: UIColor(red: 0, green: 0.72, blue: 0.69, alpha: 1)]

        UITabBarItem.appearance().setTitleTextAttributes(attributes, forState: UIControlState.Selected)

4.

OC:

//警示样式

    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"标题" message:@"这是个UIAlertController的默认样式" preferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDestructive handler:nil];

    UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];

    [alertController addAction:cancelAction];

    [alertController addAction:okAction];

    [self presentViewController:alertController animated:YES completion:nil];

Swift:

//警示样式

        let alertController = UIAlertController(title: "标题", message:"这个是UIAlertController的默认样式", preferredStyle: UIAlertControllerStyle.Alert)

        let cancelAction = UIAlertAction(title: "取消", style:UIAlertActionStyle.Destructive, handler: nil)

        let okAction = UIAlertAction(title: "确定", style:UIAlertActionStyle.Default, handler:nil)

        alertController.addAction(cancelAction)

        alertController.addAction(okAction)

        self.presentViewController(alertController,animated:true,completion:nil)

 

转载于:https://www.cnblogs.com/abelsu/p/4858443.html

2017-04-12 20:11:58 studying_ios 阅读数 5955

最近在研究swift,就我看来,swift确实是比OC更优秀的语言,这可以体现在很多方面,网上已经对比的很透彻,就不一一赘述。
今天研究了一下如何在OC项目中使用swift文件,即OC和swift的混编问题,发现在OC中要使用swift文件也是蛮简单的,实践过程列如下:
1)首先随便创建了一个OC项目,就叫 DetialViewDemo
这里写图片描述
2)因为Swift的代码默认是使用module管理的,所以两者混编的最重要的一步,就是把我们的Swift代码作为一个module暴露给OC工程,具体步骤如下:
这里写图片描述
3)创建一个swift文件或者将已有的swift文件拖入项目中,我这里是拖入了一个练习swift写的一个类:
这里写图片描述
注意点:swift文件添加的时候会提示是否添加桥接文件,直接点击不创建就OK
4)好了,文件都准备好了,接下来就需要在使用到swift文件的类中引入”Product Module Name-Swift.h”,其中Product Module Name其实就是项目的名称,这个在上面配置Module的图中也能看到,然后我们就可以愉快地和swift进行玩耍了
这里写图片描述
注意:引入Product Module Name-Swift.h的时候不一定自动提示,手写之后编译就可以了

当然,我很好奇这是怎么回事,然后我就command+点击 Person 进入了内部查看,发现原来苹果将所有的swift文件中的代码转成了oc的代码,不管你引入多少个swift文件,都会一股脑在Product Module Name-Swift.h文件中翻译成oc文件以便我们oc程序调用
这里写图片描述

怎么样,很强大吧,朋友们赶紧转过来吧,开始自己的swift开发,这绝对是苹果的未来啊!

Swift与OC转换

阅读数 5102