Swift编程语言的常见问题(FAQ)
概述:初次接触Swift总会有不少问题,比如刚入行的开发者应该学习Objective-C还是Swift?有没有Swift可以实现,但Objective-C不能实现的事情?以及能否用Swift来调用自己的Objective-C代码或者第三方库等等。下面这篇文章针对这些问题进行了解答。
作为苹果在WWDC 2014上发布的新编程语言,Swift绝对是当前的热门话题。Swift由苹果开发者工具部门总监克里斯·拉特纳(Chris Lattner)耗时四年开发而成,苹果宣称Swift的特点是:快速、现代、安全、互动,且全面优于Objective-C语言。
在Swift发布之后,随之而来的便是各种入门教程,各种上手体验,各种用Swift编写的demo...不管是经验丰富的开发者还是新手开发者都急切地想上手体验下这门汲取了Objective-C、Rust、Haskell、Ruby、Python、C#以及CLU等编程语言精华的新语言。不过,初次接触Swift总会有不少问题,比如刚入行的开发者应该学习Objective-C还是Swift?有没有Swift可以实现,但Objective-C不能实现的事情?以及能否用Swift来调用自己的Objective-C代码或者第三方库等等。下面这篇文章针对这些问题进行了解答。
基础部分
1、我是个刚入行的开发者,我应该学习Objective-C还是Swift,还是两个都学?
这取决于你是计划到其他公司工作,还是作为一个独立iOS开发者。
a. 如果你是其他公司的全职iOS开发者或者顾问,你最好两个都学。因为很多公司现有的代码还是用Objective-C写的,你需要理解这些代码。并且一些公司不会立刻过渡到Swift语言开发。此外,你还需要理解使用Objective-C编写的海量iOS库、教程以及示例项目。另外,随着时间的推移,很多公司会逐渐过渡到Swift开发,所以你也需要学习Swift。
b. 如果你是独立开发者,你打算从一开始就使用Swift,理论上你只了解Swift就可以了。但如果你有时间,还是应该去了解Objective-C,这样你就可以使用已有的Objective-C强大的资源库。
根据市场的变化和Swift渗透率的增长,这个问题的答案也在不断变化。最终,知道Objective-C可能会像知道COBOL一样了。
2、我有多年的Objective-C开发经验。对于Swift,我是新手么?
是,但也不是。如果你为苹果平台开发过一段时间,你仍有很大的优势。因为你很熟悉Xcode和Cocoa/Cocoa Touch API。学习Xcode和上千个Cocoa/Cocoa Touch API要比学Swift花的时间更长,所以你有很好的开发基础。长话短说,一旦你熟悉了Swift代码,你就能很快地学习Swift。
3、iOS 8和OS X Yosemite的应用程序只使用Swift语言么?
不是。Swift语言能与Objective-C流畅地交互,反之亦然。苹果并没有完全将Objective-C 的API转成Swift,但你仍可以在Swift代码中仍使用这些API。时间会证明一切,但是很多年内iOS和OS X商店在采用Swift语言的同时仍会继续依赖Objective-C语言。
4、Swift语言适用于其他版本的iOS和OS X系统么?
是的!Xcode 6可以为iOS 7及以上和OS X10.9及以上的系统版本编译Swift代码。其实,现在你从App Store下载的WWDC app就是苹果用Swift语言编写的。
但是,记住苹果不允许使用Xcode Beta版本创建的应用程序提交到App Store。因此,你需要等到Xcode 6正式版发布才能在App Store中上传Swift编写的应用程序。
5、.Swift语言是取代Objective-C,还是对其的补充?
引用苹果官方的一句话,“Objective-C不会消失,Swift和Objective-C可同时用于Cocoa和CocoaTouch开发。”因此,你仍然可以继续使用Objective-C。然而,苹果似乎鼓励你使用Swift进行新的开发,而不是希望你重写所有的Objective-C代码。我们猜测苹果在未来的框架和API开发中将会逐渐减少使用Objective-C语言,甚至有一天会弃用Objective-C,所以早作准备吧!
6、playground是什么?
playground是一个文件,你可以编写代码的同时即刻看到运行效果。对于学习Swift或者新的API、原型代码或者算法真的很有用处!
7、如何学习Swift?
网上已经有很多不错的资源:
你也可以在Xcode中阅读本书:Help\Documentation and API Reference\New Features in Xcode 6 Beta\Swift Language\The Swift Programming Language\A Swift Tour\Open Playground)
我们的 Swift video tutorial series 和 Swift Quick Start tutorial
8、未来你们的书和教程会使用Swift吗?
会的。我们会帮开发者过渡到Swift。
进阶
1、有没有Swift可以实现,但Objective-C不能实现的事情?或者反过来说。
是的。Swift是一门现代语言,引入了很多Objective-C不支持的内容。比如命名空间(namspacing)、可选类型(optionals)、元组(tuples)、泛型(generics),类型推断(type inference)以及其他等等。当然,Objective-C也有一些Swift不具备的特性,比如messaging nil.
想了解更多细节,建议在读完本文后阅读下苹果的官方文档: Using Swift with Cocoa and Objective-C Guide (这里有 中文版 )
2、有没有Swift不能用的API?
在写这篇文章时,我还没发现有。但在使用Objective-C和Swift互通方面有些注意事项:
a. 当一个Objective-C的API返回一个id时,Swift将接收到AnyObject。
b. 当一个Objective-C API返回nil,Swift将获得一组Optional类型的值,且被设置成NONE,Swift以这种方式来表示一个变量为nil。Swift的变量必须始终包含一个值,因为不能保证Objective-C 方法不会返回nil,所以Swift使用Optional类型枚举来保存任何从Objective-C API返回的对象。
c. 当一个Objective-C API返回一个集合时,由于Swift无法判断NSArray或NSDictionary存储了什么类型,所以它会被转换为AnyObject 。基于你对API的了解,向下转换(downcast)你的集合是个很好的习惯。思考一个返回NSString实例的数组的Objective-C方法。由于你已经知道返回的数组里包含了字符串,所以你可以像下边那样安全地进行转换:
1
2
3
4
5
6
7
|
let fruits : AnyObject[] = // some Objective-C API that returns NSArray of NSStrings for fruit in fruits as String[] { println(fruit) } |
d. 当一个Swift API返回一个元组(Tuple),Objective-C将收不到任何值。这是因为Objective-C不支持元组,因此该方法不适用于Objective-C代码。以下是Objective-C不支持的类型:
· 泛型(Generics)
· 元组(Tuples)
· 在Swift中定义的枚举(Enumerations defined in Swift)
· 在Swift中定义的结构体(Structures defined in Swift)
· 在Swift中定义的顶级函数(Top-level functions defined in Swift)
· 在Swift中定义的全局变量(Global variables defined in Swift)
· 在Swift中定义的类型别名(Typealiases defined in Swift)
· Swift风格的variadics(Swift-style variadics)
· 嵌套类型(Nested types)
· Curried 函数(Curried functions)
3、Playground中,println()结果在哪里?
你必须打开Assistant Editor才能看到控制台输出。步骤:View > Assistant Editor > Show Assistant Editor,或者使用快捷键: Option + Command + Return.(感谢Chris LaPollo 的这个观点)
4、怎样在Playgrounds中看到那些很酷的值的图形?
你可以在Playgrounds里绘出值的结果,这对于可视化算法是很方便的。在playground里面输入能够产生值的代码:
1
2
3
4
5
|
for x in 1..10 { x } |
在侧边栏,你会看到类似于“9 times”的东西。把鼠标移到这一行上,会出现“+”按钮。点击这个按钮(并确保你打开了Assistant Editor),你将会看到图形。
5、你如何运行REPL?
在终端运行以下命令,告诉它使用Xcode 6的命令行工具。
1
|
sudo xcode-select -s /Applications/Xcode6-Beta.app/Contents/Developer/ |
然后运行以下代码开始Swift REPL。
1
|
xcrun swift |
如果准备退出,你可以键入:exit或者:quit。你也可以使用CTRL+D快捷键。
6.你能用Swift来调用自己的Objective-C代码或者第三方库吗?如果能,要怎么做呢?
可以!当你往Xcode项目里添加第一个.swift文件时,系统会提示你让Xcode创建一个桥接头文件(bridging header file)。你可以在这个头文件中导入你希望可见于Swift代码的Objective-C头文件。然后,所有的类无需导入都可为Swift所用,你可以使用和系统类相同的Swift语法来使用自定义Objective-C代码。
7、那么,数组只能包含一个类型的对象吗?如果我想要不同的类型呢?
在Swift中,强烈建议你使用只包含一种类型的强类型数组,语法像是:
1
|
var goodArray: String[] = [ "foo" , "bar" ] |
也就是说,从技术上讲,你依然可以创建包含多个类型对象的数组。但最好在做之前问问自己为什么想这么做。按照这种说法,你可以使用AnyObject:创建一个包含不同类型对象的Swift数组:
1
|
var brokenArray: AnyObject[] = [ "foo" , 1, 12.23, true ] |
8、对于字典(dictionary)也是一样吗?字典也是强类型(strongly typed)的吗?
是的,不过你依然可以用AnyObject来解决。对于字典来说,它里边所有的值不是同一个类型也讲得通。以下是用字典表示的从服务器端返回的一个JSON响应:
来看一个服务器JSON响应的例子,用字典来表示:
1
|
var employee : Dictionary<String, AnyObject> = [ "FirstName" : "Larry" , "LastName" : "Rodgers" , "Salary" : 65_000.00] |
这个字典有两个String类型的键和一个Double值类型的键。虽然这是可行的,但可能的话你应该创建一级类模型对象来表示数据,而不是依赖字典。
具体细节
1、在Swift中有id的等价替代吗?
有。像上边提到的那样,当Objective-C API 返回id类型时,Swift使用AnyObject替换。AnyObject类型可以代表任何类类型的实例。另外也有Any可代表任何类型的实例(除了函数类型)。
2、如何在Swift中自省?(类似 if ([obj isKindOfClass:[Foo class]]) { … })
你可以使用is关键字检查变量或者常量的类型。编译器足够聪明让你知道使用is是多余的。由于Swift的类型安全机制,不大可能为同一个引用赋一个不同的类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var someValue : Any? someValue = "String" if someValue is String { println( "someValue is a String" ) } else { println( "someValue is something else" ) } |
注意如果你想这么编写的话...
1
2
3
4
5
6
7
8
9
10
11
|
var someValue = "String" if someValue is String { println( "someValue is a String" ) } else { println( "someValue is something else" ) } |
你会收到一个编译器警告:
1
2
3
|
Playground execution failed: error: <REPL>:7:14: error: 'is' test is always true if someValue is String { |
3、如何在Swift的枚举中存放位移值?(i.e. MyVal = 1<<5)
不幸的是苹果尚未解决这个问题。不过有传言称他们正努力在这方面做的更好。按照这个说法,我们需要今天编写代码,对吧?以下是别人如何做到了这一点。
Nate Cook 使用以下方案处理这个问题,你可以在 Stack Overflow 上查看更多细节:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
struct MyOptions : RawOptionSet { var value: UInt = 0 init(_ value: UInt) { self.value = value } func toRaw() -> UInt { return self.value } func getLogicValue() -> Bool { return self.value != 0 } static func fromRaw(raw: UInt) -> MyOptions? { return MyOptions(raw) } static func fromMask(raw: UInt) -> MyOptions { return MyOptions(raw) } static var None: MyOptions { return MyOptions(0) } static var FirstOption: MyOptions { return MyOptions(1 << 0) } static var SecondOption: MyOptions { return MyOptions(1 << 1) } static var ThirdOption: MyOptions { return MyOptions(1 << 2) } } func == (lhs: MyOptions, rhs: MyOptions) -> Bool { return lhs.value == rhs.value } func | (lhs: MyOptions, rhs: MyOptions) -> MyOptions { return MyOptions(lhs.value | rhs.value) } func & (lhs: MyOptions, rhs: MyOptions) -> MyOptions { return MyOptions(lhs.value & rhs.value) } func ^ (lhs: MyOptions, rhs: MyOptions) -> MyOptions { return MyOptions(lhs.value ^ rhs.value) } |
教程组成员 Chris LaPollo 想到了下边更简单的方法,你可以在他的 帖子 里 阅读更多内容。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class MyOptions { class var None : UInt32 { return 0 } class var All : UInt32 { return UInt32.max } class var First : UInt32 { return 1 } class var Second : UInt32 { return 1<<1 } class var Third : UInt32 { return 1<<2 } } |
范例用法
1
2
3
|
physicsBody.categoryBitMask = MyOptions.First physcisBody.collisionBitMask = MyOptions.First | MyOptions.Second |
Dave Lawson也写了一篇你可能感兴趣的文章: Implementing NS_OPTIONS in Swift
以上是几则相关的处理方法,不过希望苹果在以后的版本中会解决这个问题。
4、Swift如何和Grand Central Dispatch一起使用?
同样的方法,你可以像在Objective-C中那样使用C API.在处理并发性时,你也可以使用苹果高级NSOperationQueue。
5、Objective-C中的国际化宏命令呢?
类似Objective-C中的NSLocalizedString,你可以在Swift中使用NSLocalizedString(key:tableName:bundle:value:comment:) 方法为国际化做准备。tableName、bundle以及value arguments都有默认值,所以如果你正使用NSLocalizedString,你可以编写如下代码:
1
2
3
4
5
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), { println( "test" ) }); |
6、我需要担心引用生命周期吗?
当然!当两个对象彼此之间是强引用时,仍能创建一个retain cycle。你可以使用Objective-C中一样的方法break这个retain cycle。有三个关键字用于声明引用类型,详情如下,弱引用和无主引用将解决你的引用生命周期问题。
何时应该使用强引用、弱引用以及无主引用呢?
强引用:强引用会使得ARC保留实例直到不再需要它们。当移除所有强引用时,引用实例就会被释放。注意默认情况下强引用是隐式的, 所以你不必显式地声明它。
弱引用:你应该在独立生命周期的对象间使用弱引用。当为一个对象设置弱引用时,如果出于内存压力释放了对象,表示你不介意这一点。弱引用的值必须是一个变量,使用var定义 ,并且必须是使用?运算符的Optional类型。由于弱引用是可选的,所以你决不能以一个已经不存在的无效实例的引用来结束。当引用实例被释放时,ARC将会自动把引用设 置为nil。
无主引用:你应该为有相同生命周期的对象使用无主引用;比如当一个对象指向其自身,以及你希望避免一个retain cycle。无论何时只要引用有一个值就可以使用无主引用,但当你需要告诉ARC不要将它设置为nil时。无主引用的行为类似于Objective-C的unsafe_unretained。你要确保你不会在引用对象被释放后访问引用,这样会导致你的 app崩溃。无主引用不能是可选的,不能被设置为nil。无主引用也是隐式解析。
分号去哪里了?
分号在Swift中是可选的,不过出于易读性的目的,苹果建议你不要再使用分号了。但有时候仍会在Swift中使用分号,比如在循环语句中。
下一步做什么?
这仅仅是版本1,苹果的目的非常清楚,他们将在该语言上进行迭代,所以你可以向苹果报告bug,要求新特性等等。在该版本正式发布之前,仍有很大的提升空间。
CocoaPods如何适用于swift?
可能以类似的方式。Swift项目仍然是Xcode项目,并且支持多个target,但是有提升创建模块和自定义框架能力的潜在空间。有可能会重新改写CocoaPods以适应这个新特性。有人使用CocoaPods协助Swift项目工作,并且聪明的CocoaPods开发人员 已经在讨论这个问题了 。