文章组织脉络:java
从Objective-C到Swift的语法差别。咱们熟悉的Objective-C特性在Swift中如何展示。react
从Objective-C到Swift的进步改进。研究对比Swift在安全性,易用性上的提高,给咱们带来的新编程范式。ios
目录:git
1.属性(property)和实例变量(instance variable)github
2.控制流web
3.函数编程
4.类与初始化(Initializers)swift
5.枚举与结构体数组
6.协议(Protocols)安全
7.Swift与Cocoa
8.总结
1.属性(property)和实例变量(instance variable)
Objective-C property in Swift world
在Cocoa世界开发的过程当中,咱们最常打交道的是property.
典型的声明为:
1
|
@property (strong,nonatomic) NSString *string;
|
而在Swift当中,摆脱了C的包袱后,变得更为精炼,咱们只需直接在类中声明便可
1
2
3
|
class Shape {
var
name =
"shape"
}
|
注意到这里,咱们再也不须要@property指令,而在Objective-C中,咱们能够指定property的attribute,例如strong,weak,readonly等。
而在Swift的世界中,咱们经过其余方式来声明这些property的性质。
须要注意的几点:
strong: 在Swift中是默认的
weak: 经过weak关键词申明
1
|
weak
var
delegate: UITextFieldDelegate?
|
readonly,readwrie 直接经过声明常量let,声明变量var的方式来指明
copy 经过@NSCopying指令声明。
值得注意的是String,Array和Dictionary在Swift是以值类型(value type)而不是引用类型(reference type)出现,所以它们在赋值,初始化,参数传递中都是以拷贝的方式进行(简单来讲,String,Array,Dictionary在Swift中是经过struct实现的)
延伸阅读:Value and Reference Types
nonatomic,atomic 全部的Swift properties 都是nonatomic。可是咱们在线程安全上已经有许多机制,例如NSLock,GCD相关API等。我的推测缘由是苹果想把这一个原本就用的不多的特性去掉,线程安全方面交给平时咱们用的更多的机制去处理。
而后值得注意的是,在Objective-C中,咱们能够跨过property直接与instance variable打交道,而在Swift是不能够的。
例如:咱们能够不须要将someString声明为property,直接使用便可。即便咱们将otherString声明为property,咱们也能够直接用_otherString来使用property背后的实例变量。
1
2
3
4
|
@interface SomeClass : NSObject {
NSString *someString;
}
@property(nonatomic, copy) NSString* otherString;
|
而在Swift中,咱们不能直接与instance variable打交道。也就是咱们声明的方式简化为简单的一种,简单来讲在Swift中,咱们只与property打交道。
A Swift property does not have a corresponding instance variable, and the backing store for a property is not accessed directly
小结
所以以前使用OC致使的像巧哥指出的开发争议就再也不须要争执了,在Swift的世界里,咱们只与property打交道。
而且咱们在OC中init和dealloc不能使用属性self.property = XXX来进行设置的状况得以解决和统一。
(不知道这一条规定,在init直接用self.property = value 的同窗请自觉阅读iOS夯实:内存管理)
我的以为这看似小小一点变更使Swift开发变得更加安全以及在代码的风格更为统一与稳定。
Swift property延伸:
Stored Properties和Computed properties
在Swift中,property被分为两类:Stored Properties和Computed properties 简单来讲,就是stored properties 可以保存值,而computed properties只提供getter与setter,利用stored properties来生成本身的值。我的感受Computed properties更像方法,而不是传统意义的属性。可是这样一个特性存在,使得咱们更容易组织咱们的代码。
延伸阅读:computed property vs function
Type Properties
Swift提供了语言级别定义类变量的方法。
In C and Objective-C, you define static constants and variables associated with a type as global static variables.In Swift, however, type properties are written as part of the type’s definition, within the type’s outer curly braces, and each type property is explicitly scoped to the type it supports.
在Objective-C中,咱们只能经过单例,或者static变量加类方法来本身构造类变量:
1
2
3
4
5
6
7
8
9
10
11
12
|
@interface Model
+ (int) value;
+ (void) setValue:(int)val;
@end
@implementation Model
static int value;
+ (int) value
{ @synchronized(self) {
return
value; } }
+ (void) setValue:(int)val
{ @synchronized(self) { value = val; } }
@end
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// Foo.h
@interface Foo {
}
+(NSDictionary*) dictionary;
// Foo.m
+(NSDictionary*) dictionary
{
static NSDictionary* fooDict = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
// create dict
});
return
fooDict;
}
|
而在Swift中咱们经过清晰的语法便能定义类变量:
经过static定义的类变量没法在子类重写,经过class定义的类变量则可在子类重写。
1
2
3
4
5
6
7
8
9
|
struct SomeStructure {
static
var
storedTypeProperty =
"Some value."
static
var
computedTypeProperty: Int {
return
1
}
class
var
overrideableComputedTypeProperty: Int {
return
107
}
}
|
同时利用类变量咱们也有了更优雅的单例模式实现:
1
2
3
4
|
class singletonClass {
static let sharedInstance = singletonClass()
private init() {}
// 这就阻止其余对象使用这个类的默认的'()'初始化方法
}
|
Swift单例模式探索:The Right Way to Write a Singleton
延伸:目前Swift支持的type propertis中的Stored Properties类型不是传统意义上的类变量(class variable),暂时不能经过class 关键词定义,经过static定义的类变量相似java中的类变量,是没法被继承的,父类与子类的类变量指向的都是同一个静态变量。
延伸阅读: Class variables not yet supported
1
2
3
4
5
6
|
class SomeStructure {
class
var
storedTypeProperty =
"Some value."
}
//Swift 2.0
Error: Class stored properties not yet supported
in
classes
|
经过编译器抛出的错误信息,相信在将来的版本中会完善Type properties。
2.控制流
Swift与Objective-C在控制流的语法上关键词基本是一致的,可是扩展性和安全性获得了很大的提高。
主要有三种类型的语句
if,switch和新增的guard
for,while
break,continue
主要差别有:
关于if
语句里的条件再也不须要使用()包裹了。
1
2
3
4
|
let number = 23
if
number < 10 {
print(
"The number is small"
)
}
|
可是后面判断执行的的代码必须使用{}包裹住。
为何呢,在C,C++等语言中,若是后面执行的语句只有语句,咱们能够写成:
1
2
3
|
int number = 23
if
(number < 10)
NSLog(
"The number is small"
)
|
可是若是有时要在后面添加新的语句,忘记添加{},灾难就极可能发送。
:) 像苹果公司本身就犯过这样的错误。下面这段代码就是著名的goto fail错误,致使了严重的安全性问题。
1
2
3
4
5
6
7
|
if
((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
goto fail;
goto fail;
// :)注意 这不是Python的缩减
... other checks ...
fail:
... buffer frees (cleanups) ...
return
err;
|
最终在Swift,苹果终于在根源上消除了可能致使这种错误的可能性。
if 后面的条件必须为Boolean表达式
也就是不会隐式地与0进行比较,下面这种写法是错误的,由于number并非一个boolean表达式,number != 0才是。
1
2
3
|
int number = 0
if
number{
}
|
关于for
for循环在Swift中变得更方便,更强大。
得益于Swift新添加的范围操做符...与...<
咱们可以将以前繁琐的for循环:
1
2
3
4
|
for
(int i = 1; i <= 5; i++)
{
NSLog(@
"%d"
, i);
}
|
改写为:
1
2
3
|
for
index
in
1...5 {
print(index)
}
|
固然,熟悉Python的亲们知道Python的range函数很方便,咱们还能自由选择步长。 像这样:
>>> range(1,5) #表明从1到5(不包含5)
[1, 2, 3, 4]
>>> range(1,5,2) #表明从1到5,间隔2(不包含5)
[1, 3]
虽然在《The Swift Programming Language》里面没有提到相似的用法,可是在Swift中咱们也有优雅的方法办到。
1
2
3
|
for
index
in
stride(from: 1, through: 5, by: 2) {
print(index)
}
// through是包括5
|
而后对字典的遍历也加强了.在Objective-c的快速枚举中咱们只能对字典的键进行枚举。
1
2
3
4
|
NSString *key;
for
(key
in
someDictionary){
NSLog(@
"Key: %@, Value %@"
, key, [someDictionary objectForKey: key]);
}
|
而在Swift中,经过tuple咱们能够同时枚举key与value:
1
2
3
4
|
let dictionary = [
"firstName"
:
"Mango"
,
"lastName"
:
"Fang"
]
for
(key,value)
in
dictionary{
print(key+
" "
+value)
}
|
关于Switch
Swich在Swift中也获得了功能的加强与安全性的提升。
不须要Break来终止往下一个Case执行
也就是下面这两种写法是等价的。
1
2
3
4
5
6
7
8
9
|
let character =
"a"
switch
character{
case
"a"
:
print(
"A"
)
break
case
"b"
:
print(
"B"
)
break
default
: print(
"character"
)
|
1
2
3
4
5
6
7
|
let character =
"a"
switch
character{
case
"a"
:
print(
"A"
)
case
"b"
:
print(
"B"
)
default
: print(
"character"
)
|
这种改进避免了忘记写break形成的错误,本身深有体会,曾经就是由于漏写了break而花了一段时间去debug。
若是咱们想不一样值统一处理,使用逗号将值隔开便可。
1
2
3
4
|
switch
some value to consider {
case
value 1,value 2:
statements
}
|
Switch支持的类型
在OC中,Swtich只支持int类型,char类型做为匹配。
而在Swift中,Switch支持的类型大大的拓宽了。实际上,苹果是这么说的。
A switch statement supports any kind of data
这意味在开发中咱们可以可以对字符串,浮点数等进行匹配了。
以前在OC繁琐的写法就能够进行改进了:
1
2
3
4
5
6
7
8
9
|
if
([cardName isEqualToString:@
"Six"
]) {
[self setValue:6];
}
else
if
([cardName isEqualToString:@
"Seven"
]) {
[self setValue:7];
}
else
if
([cardName isEqualToString:@
"Eight"
]) {
[self setValue:8];
}
else
if
([cardName isEqualToString:@
"Nine"
]) {
[self setValue:9];
}
|
1
2
3
4
5
6
7
8
9
10
|
switch
carName{
case
"Six"
:
self.vaule = 6
case
"Seven"
:
self.vaule = 7
case
"Eight"
:
self.vaule = 8
case
"Night"
:
self.vaule = 9
}
|
3.函数
对于在OC中,方法有两种类型,类方法与实例方法。方法的组成由方法名,参数,返回值组成。
在Swift中函数的定义基本与OC同样。
主要区别为:
经过func关键词定义函数
返回值在->关键词后标注
各举一个类方法与实例方法例子。
1
2
|
+ (UIColor*)blackColor
- (void)addSubview:(UIView *)view
|
对应的swift版本
1
2
|
class func blackColor() -> UIColor
//类方法, 经过 class func 关键词声明
func addSubview(view: UIView)
//实例方法
|
改进:
在Swift中,函数的最重要的改进就是函数做为一等公民(first-class),和对象同样能够做为参数进行传递,能够做为返回值,函数式编程也成为了Swift支持的编程范式。
In computer science, a programming language is said to have first-class functions if it treats functions as first-class citizens. Specifically, this means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures
让咱们初略感觉一下函数式编程的魅力:
举一个例子,咱们要筛选出一个数组里大于4的数字。
在OC中咱们可能会用快速枚举来进行筛选。
1
2
3
4
5
6
7
|
NSArray *oldArray = @[@1,@2,@3,@4,@5,@6,@7,@8,@9,@10];
NSMutableArray *newArray;
for
(NSNumber* number
in
oldArray) {
if
([number compare:@4] == NSOrderedDescending ) {
[newArray addObject:number];
}
}
|
而在Swift中,咱们用两行代码解决这个问题:
1
2
|
let oldArray = [1,2,3,4,5,6,7,8,9,10]
let newArray = oldArray.filter({$0 > 4})
|
进一步了解Swift的函数式编程能够经过这篇优秀的博客Functional Reactive Programming in Swift
我的以为另一个很棒的改进是:Default parameter values
在咱们的项目中,常常会不断进行功能的增添。为了新增特性,许多方法在开发的过程当中不断变更。举一个例子:咱们开始有一个tableViewCell,它的设置方法一开始简单地须要一个Model参数:
1
|
func configureCellWithModel(Model: model)
|
不久以后,咱们想对部分Cell增添一个设置背景颜色的功能。方法须要再接收多一个参数:
1
|
func configureCellWithModel(Model: model,color:UIColor)
|
这个时候方法改变,因此涉及到这些方法的地方都须要修改。给咱们形成的困扰
一是:须要作许多重复修改的工做。
二是:没法作得很好的扩展和定制,有些地方的cell须要设置颜色,有些不须要。可是在OC里,咱们只能对全部的cell都赋值。你可能以为咱们能够写两个方法,一个接收颜色参数,一个不接受。可是咱们知道这不是一个很好的解决方法,会形成冗余的代码,维护起来也不方便。
而在Swift中,default parameter values的引入让咱们可以这样修改咱们的代码:
1
|
func configureCellWithModel(Model: model,color:UIColor = UIColor.whiteColor())
|
这样的改进能让咱们写出的代码更具向后兼容性,减小了咱们的重复工做量,减小了犯错误的可能性。
4.类与初始化(Initializers)
文件结构与访问控制
在swift中,一个类再也不分为interface(.h)与implementation(.m)两个文件实现,直接在一个.swift文件里进行处理。好处就是咱们只需管理一份文件,以往两头奔波修改的状况就获得解放了,也减小了头文件与实现文件不一样步致使的错误。
这时咱们会想到,那么咱们如何来定义私有方法与属性呢,在OC中咱们经过在class extension中定义私有属性,在.m文件定义私有方法。
而在Swift中,咱们经过Access Control来进行控制。
properties, types, functions等可以进行版本控制的统称为实体。
Public:能够访问本身模块或应用中源文件里的任何实体,别人也能够访问引入该模块中源文件里的全部实体。一般状况下,某个接口或Framework是能够被任何人使用时,你能够将其设置为public级别。
Internal:能够访问本身模块或应用中源文件里的任何实体,可是别人不能访问该模块中源文件里的实体。一般状况下,某个接口或Framework做为内部结构使用时,你能够将其设置为internal级别。
Private:只能在当前源文件中使用的实体,称为私有实体。使用private级别,能够用做隐藏某些功能的实现细节
一个小技巧,若是咱们有一系列的私有方法,咱们能够把它们组织起来,放进一个extension里,这样就不须要每一个方法都标记private,同时也便于管理组织代码:
1
2
3
4
5
|
// MARK: Private
private extension ViewController {
func privateFunction() {
}
}
|
建立对象与alloc和init
关于初始化,在Swift中建立一个对象的语法很简洁:只需在类名后加一对圆括号便可。
1
|
var
shape = Shape()
|
而在Swift中,initializer也与OC有所区别,Swift的初始化方法不返回数据。而在OC中咱们一般返回一个self指针。
Unlike Objective-C initializers, Swift initializers do not return a value. Their primary role is to ensure that new instances of a type are correctly initialized before they are used for the first time.
Swift的初始化方法让咱们只关注对象的初始化。以前在OC世界中为何要self = [super init]?。这种问题得以免。Swift帮助咱们处理了alloc的过程。也让咱们的代码更简洁明确。
而在Swift中,init也有了更严格的规则。
对于全部Stored Properties,都必须在对象被建立出来前设置好。也就是咱们必须在init方法中赋好值,或是直接给属性提供一个默认值。
若是有property能够被容许在初始出来时没有值,也就是须要在建立出来后再赋值,或是在程序运行过程均可能不会被赋值。那么这个property必须被声明为optional类型。该类型的属性会在init的时候初始化为nil.
initializer严格分为Designated Initializer和Convenience Initializer 而且有语法定义。
而在Objective-C中没有明确语法标记哪一个初始化方式是convenience方法。关于Designated Initializer可参阅以前的:Objective-C 拾遗:designated initializer
1
2
3
4
5
6
7
|
init(parameters) {
statements
}
convenience init(parameters) {
statements
}
|
5.枚举与结构体
枚举
在Swift中,枚举是一等公民。(first-class)。可以拥有方法,computed properties等以往只有类支持的特性。
在C中,枚举为每一个成员指定一个整型值。而在Swift中,枚举更强大和灵活。咱们没必要给枚举成员提供一个值。若是咱们想要为枚举成员提供一个值(raw value),咱们能够用字符串,字符,整型或浮点数类型。
1
2
3
4
5
6
7
|
enum CompassPoint {
case
North
case
South
case
East
case
West
}
var
directionToHead = CompassPoint.West
|
结构体
Struct在Swift中和类有许多相同的地方,能够定义属性,方法,初始化方法,可经过extension扩展等。
不一样的地方在于struct是值类型.在传递的过程当中都是经过拷贝进行。
在这里要提到在前面第一节处提到了String,Array和Dictionary在Swift是以值类型出现的。这背后的缘由就是String,Array,Dictionary在Swift中是经过Struct实现的。而以前在Objective-C它们都是经过class实现的。
Swift中强大的Struct使得咱们可以更多与值类型打交道。Swift的值类型加强了不可变性(Immutabiliity)。而不可变性提高了咱们代码的稳定性,多线程并发的安全性。
在WWDC2014《Advanced iOS Application Architecture and Patterns》中就有一节的标题是Simplify with immutability。
延伸阅读:WWDC心得:Advanced iOS Application Architecture and Patterns
6.协议(Protocols)
语法:
在Objective-C中咱们这么声明Protocol:
1
2
3
|
@protocol SampleProtocol
- (void)someMethod;
@end
|
而在Swift中:
1
2
3
4
|
protocol SampleProtocol
{
func someMethod()
}
|
在Swift遵循协议:
1
2
3
4
|
class AnotherClass: SomeSuperClass, SampleProtocol
{
func someMethod() {}
}
|
那么以前Objective-C的protocol中,咱们能够标志optional。那在Swift中呢?
遗憾的是,目前纯Swift的protocol还不支持optional。但根据苹果官方论坛的一位员工的回答,将来Swift是会支持的。
Optional methods in protocols are limited to @objc protocols only because we haven't implemented them in native protocols yet. This is something we plan to support. We've gotten a number of requests for abstract/pure virtual classes and methods too.
— Joe Groff
Source: https://devforums.apple.com/message/1051431#1051431
protocol和delegate是紧密联系的。那么咱们在Swift中如何定义Delegate呢?
1
2
3
4
5
|
protocol MyDelegate : class {
}
class MyClass {
weak
var
delegate : MyDelegate?
}
|
注意到上面的protocol定义后面跟着的class。这意味着该protocol只能被class类型所遵照。
而且只有遵照了class protocol的delegate才能定义为weak。这是由于在Swift中,除了class可以遵照协议,枚举和结构一样可以遵照协议。而枚举和结构是值类型,不存在内存管理的问题。所以只须要class类型的变量声明为weak便可。
利用Swift的optional chaining,咱们可以很方便的检查delegate是否为Nil,是否有实现某个方法:
之前咱们要在Objective-C这样检查:
1
2
3
|
if
(self.dataSource && [self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
}
|
在Swift中,很是的优雅简洁。
1
2
|
if
let thisSementTitle = dataSource?.titleFroSegmentAtIndex?(index){
}
|
新特性:
1.在Swift中,protocol变得更增强大,灵活:
2.class,enum,structure均可以遵照协议。
Extension也能遵照协议。利用它,咱们不须要继承,也可以让系统的类也遵循咱们的协议。
例如:
1
2
3
4
5
6
7
8
|
protocol myProtocol {
func hello() -> String
}
extension String:myProtocol{
func hello() -> String {
return
"hello world!"
}
}
|
咱们还可以用这个特性来组织咱们的代码结构,以下面的代码所示,将UITableViewDataSource的实现移到了Extension。使代码更清晰。
1
2
3
4
|
// MARK: - UITableViewDataSource
extension MyViewcontroller: UITableViewDataSource {
// table view data source methods
}
|
3.Protocol Oriented Programming
随着Swift2.0的发布,面向协议编程正式也加入到了Swift的编程范式。Cool.
这种编程方式经过怎样的语法特性支撑的呢?
那就是咱们可以对协议进行扩展,也就是咱们可以提供协议的默认实现,可以为协议添加新的方法与实现。
用前面的myProtocol为例子,咱们在Swift里这样为它提供默认实现。
1
2
3
4
5
|
extension myProtocol{
func hello() -> String {
return
"hello world!"
}
}
|
咱们还能对系统原有的protocol进行扩展,大大加强了咱们的想象空间。Swift2.0的实现也有不少地方用extension protocol的形式进行了重构。
面向协议编程可以展开说不少,在这里这简单地介绍了语法。有兴趣的朋友能够参考下面的资料:
Session 408: Protocol-Oriented Programming in Swift
IF YOU'RE SUBCLASSING, YOU'RE DOING IT WRONG.
7.Swift与Cocoa
一门语言的的强大与否,除了自身优秀的特性外,很大一点还得依靠背后的框架。Swift直接采用苹果公司经营了好久的Cocoa框架。如今咱们来看看使用Swift和Cocoa交互一些须要注意的地方。
id与AnyObject
在Swift中,没有id类型,Swift用一个名字叫AnyObject的protocol来表明任意类型的对象。
1
|
id myObject = [[UITableViewCell alloc]init];
|
1
|
var
myObject: AnyObject = UITableViewCell()
|
咱们知道id的类型直到运行时才能被肯定,若是咱们向一个对象发送一条不能响应的消息,就会致使crash。
咱们能够利用Swift的语法特性来防止这样的错误:
1
|
myObject.method?()
|
若是myObject没有这个方法,就不会执行,相似检查delegate是否有实现代理方法。
在Swift中,在AnyObject上获取的property都是optional的。
闭包
OC中的block在Swift中无缝地转换为闭包。函数实际上也是一种特殊的闭包。
错误处理
以前OC典型的错误处理步骤:
1
2
3
4
5
6
7
|
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@
"/path/to/file"
];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if
(!success) {
NSLog(@
"Error: %@"
, error.domain);
}
|
在Swift中:
1
2
3
4
5
6
7
|
let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath(
"/path/to/file"
)
do
{
try
fileManager.removeItemAtURL(URL)
}
catch
let error as NSError {
print(
"Error: \(error.domain)"
)
}
|
KVO
Swift支持KVO。可是KVO在Swift,我的以为是不够优雅的,KVO在Swift只限支持继承NSObject的类,有其局限性,在这里就不介绍如何使用了。
网上也出现了一些开源库来解决这样的问题。有兴趣能够参考一下:
KVO 在OS X中有Binding的能力,也就是咱们可以将两个属性绑定在一块儿,一个属性变化,另一个属性也会变化。对与UI和数据的同步更新颇有帮助,也是MVVM架构的需求之一。以前已经眼馋这个特性好久了,虽然Swift没有原生带来支持,Swift支持的泛型编程给开源界带来许多新的想法。下面这个库就是实现binding的效果。
8.总结
到这里就基本介绍完Swift当中最基本的语法和与Objective-C的对比和改进。
事实上Swift的世界相比OC的世界还有不少新鲜的东西等待咱们去发现和总结,Swift带来的多范式编程也将给咱们编程的架构和代码的组织带来更来的思考。而Swift也是一个不断变化,不断革新的语言。相信将来的发展和稳定性会更让咱们惊喜。这篇文章也将随着Swift的更新而不断更新,同时限制篇幅,突出重点。
但愿这篇文章可以给各位同行的小伙伴们快速了解和学习Swift提供一点帮助。有疏漏错误的地方欢迎直接提出。感谢。
参考: