ios swwift语言基本语法

原文:A Swift Tour

翻译:http://blog.qunee.com/2014/06/swift语法介绍/ 

Hello world - Swift

传统建议新语言的第一个程序是打印“Hello world”,对于Swift是下面的一行代码

1
println ( "Hello, world" )

如果你用C或者Objective-C编写过代码,这段句法会感觉熟悉,在Swift中,这是一行完整的代码,不需要引入单独的库比如输入输出或者字符串 操作,在全局作用域下编写的代码会作为程序的入口,所以你也不需要main函数,你同样不需要在每段代码后面写分号 这次tour将教你使用Swift编程的足够信息,完成各种编程任务,如果你有不理解也不用担心,本次演示的任何内容在后面的章节中都有详细的介绍

简单赋值

使用 let定义常量,使用var定义变量,常量的值不需要在编译的时候知道,但你必须只设置一次,这意味着你可以使用常量来定义这样的数值:一次决定,多处使用

1
2
3
var myVariable  = 42
myVariable  = 50
let myConstant  = 42

常量和变量必须为你指定的类型,但是你并不需要总是显示的指定类型,创建变量或者常量时,直接提供一个数值让编译器知道他的类型,下面的例子中,编译器指 定myVariable为整型,因为他的初始值是一个整型 如果初始值没有足够的信息(比如没有初始值),可以在变量后面指定一个类型,通过冒号分割

1
2
3
let implicitInteger  = 70
let implicitDouble  = 70.0
let explicitDouble : Double  = 70

数值不会隐式的转换成另一种类型,你必须转换成另一种类型,显式的指定类型

1
2
3
let  label = "The width is "
let width  = 94
let widthLabel  = label + String ( width )

有更简单的方式让字符串中包含数值:在小括号中编写数值,并在括号之前写上(\),如下例:

1
2
3
4
let apples  = 3
let oranges  = 5
let appleSummary  = "I have \(apples) apples."
let fruitSummary  = "I have \(apples + oranges) pieces of fruit."

使用中括号[]创建数组和字典,通过序号或者key来访问集合中的元素

1
2
3
4
5
6
7
8
var shoppingList  = [ "catfish" , "water" , "tulips" , "blue paint" ]
shoppingList[ 1 = "bottle of water"
 
var occupations  = [
   "Malcolm" : "Captain" ,
   "Kaylee" : "Mechanic" ,
]
occupations[ "Jayne" = "Public Relations"

创建一个空的数组或者字典,使用下面的初始化语法

1
2
let emptyArray  = String[] ( )
let emptyDictionary  = Dictionary < String , Float > ( )

如果类型信息可以被断定,你可以写一个空数组[],或者一个空的字典[:],比如当你为一个变量设置新的值,或者给函数传递一个参数

1
shoppingList  = []    / / Went shopping  and bought everything.

控制流

使用if和switch判断条件,使用for-in, for, while和do-while 循环,条件或者循环参数的小括号是可选的,但是主体的花括号是必须的

1
2
3
4
5
6
7
8
9
10
let individualScores  = [ 75 , 43 , 103 , 87 , 12 ]
var teamScore  = 0
for score  in individualScores  {
   if score  > 50 {
     teamScore  + = 3
   } else {
     teamScore  + = 1
   }
}
teamScore

if声明中,条件必须是Boolean表达式,这意味着类似 if score{…}de的写法是错误的,不会隐式的与0比较 你可以将if和let结合使用,这意味着数值为可选,可选值可能是一个数值也可能是nil表示值缺失,在类型后面标记问号(?)表示这个值为可选

1
2
3
4
5
6
7
8
var optionalString : String?  = "Hello"
optionalString  = = nil
 
var optionalName : String?  = "John Appleseed"
var greeting  = "Hello!"
if let  name = optionalName  {
   greeting  = "Hello, \(name)"
}

如果可选值为nil,条件为flase,代码会被跳过,否则会进入并设置常量,使得变量在代码体中可用 Switch支持任何类型和多种操作符,不限于整型和相等操作

1
2
3
4
5
6
7
8
9
10
11
let vegetable  = "red pepper"
switch vegetable  {
case "celery" :
   let vegetableComment  = "Add some raisins and make ants on a log."
case "cucumber" , "watercress" :
   let vegetableComment  = "That would make a good tea sandwich."
case let x  where x.hasSuffix ( "pepper" ) :
   let vegetableComment  = "Is it a spicy \(x)?"
default :
   let vegetableComment  = "Everything tastes good in soup."
}

在执行完匹配的条件后,程序将跳出选择条件,不会继续到下一个条件,所以不需要在每个条件中指定break

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let interestingNumbers  = [
   "Prime" : [ 2 , 3 , 5 , 7 , 11 , 13 ] ,
   "Fibonacci" : [ 1 , 1 , 2 , 3 , 5 , 8 ] ,
   "Square" : [ 1 , 4 , 9 , 16 , 25 ] ,
]
var largest  = 0
for ( kind , numbers ) in interestingNumbers  {
   for number in numbers  {
     if number > largest  {
       largest  = number
     }
   }
}
largest

使用while重复一段代码块知道条件改变,循环条件也可以放在后面,这样可以确保循环被至少调用一次

1
2
3
4
5
6
7
8
9
10
11
var n  = 2
while n  < 100 {
   = * 2
}
n
 
var m  = 2
do  {
   = * 2
} while m  < 100
m

可以使用序号遍历,既可以使用.. 指定序号范围,也可以写完整的表达式

1
2
3
4
5
6
7
8
9
10
11
var firstForLoop  = 0
for in 0. . 3 {
   firstForLoop  + = i
}
firstForLoop
 
var secondForLoop  = 0
for var i  = 0 ; i  < 3 + + {
   secondForLoop  + = 1
}
secondForLoop

函数与闭包

使用 func声明一个函数,通过函数名和参数调用一个函数,使用 -> 声明函数返回类型

1
2
3
4
func greet ( name : String , day : String ) - > String  {
   return "Hello \(name), today is \(day)."
}
greet ( "Bob" , "Tuesday" )

使用值组表示多个数值返回

1
2
3
4
func getGasPrices ( ) - > ( Double , Double , Double ) {
   return ( 3.5 9 , 3.6 9 , 3.7 9 )
}
getGasPrices ( )

函数还可以设置可变参数,会将他们整合到一个数组中

1
2
3
4
5
6
7
8
9
func sumOf ( numbers : Int... ) - > Int  {
   var sum  = 0
   for number in numbers  {
     sum  + = number
   }
   return sum
}
sumOf ( )
sumOf ( 42 , 597 , 12 )

函数可以嵌套,嵌套函数可以访问外部函数的声明的变量,对于很长或者复杂的函数,你可以使用嵌套函数来组织代码

1
2
3
4
5
6
7
8
9
func returnFifteen ( ) - > Int  {
   var y  = 10
   func  add ( ) {
     + = 5
   }
   add ( )
   return y
}
returnFifteen ( )

函数也是一种类型(第一等类型),这意味着你可以返回一个函数作为函数返回值

1
2
3
4
5
6
7
8
func makeIncrementer ( ) - > ( Int  - > Int ) {
   func addOne ( number : Int ) - > Int  {
     return 1 + number
   }
   return addOne
}
var increment  = makeIncrementer ( )
increment ( 7 )

函数还可以作为另一个函数的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
func hasAnyMatches ( list : Int[] , condition : Int  - > Bool ) - > Bool  {
   for item in list {
     if condition ( item ) {
       return true
     }
   }
   return false
}
func lessThanTen ( number : Int ) - > Bool  {
   return number < 10
}
var numbers  = [ 20 , 19 , 7 , 12 ]
hasAnyMatches ( numbers , lessThanTen )

闭包实际上是函数的一种特殊形式,你可以写一个闭包,使用花括号({})包裹代码,没有函数名,使用 in 来分隔传入参数和返回类型

1
2
3
4
5
numbers.map ( {
   ( number : Int ) - > Int  in
   let  result = 3 * number
   return result
   } )

你可以选择更简洁的闭包方式,当一个闭包类型已知,比如某个委托的回调函数,你可以省略它的参数和返回类型,单声明的闭包默认会返回这个唯一的声明变量

1
numbers.map ( { number in 3 * number } )

你可以通过序号替代名称来访问参数,这种方式尤其适合非常短的闭包

1
sort ( [ 1 , 5 , 3 , 12 , 2 ] ) { $ 0 > $ 1 }

对象和类

使用class创建类,类中的属性声明与常量和变量声明一样,只是他位于这个类的上下文中,同样的函数和方法声明也一样

1
2
3
4
5
6
class Shape  {
   var numberOfSides  = 0
   func simpleDescription ( ) - > String  {
     return "A shape with \(numberOfSides) sides."
   }
}

创建一个类实例,使用类名加括号,使用点操作符访问实例的属性和方法

1
2
3
var shape  = Shape ( )
shape.numberOfSides  = 7
var shapeDescription  = shape.simpleDescription ( )

这个版本的Shape类缺少一些重要的东西:用于初始化这个类的构造器,可以使用init来创建

1
2
3
4
5
6
7
8
9
10
11
12
class NamedShape  {
   var numberOfSides : Int  = 0
   var  name : String
 
   init ( name : String ) {
     self. name = name
   }
 
   func simpleDescription ( ) - > String  {
     return "A shape with \(numberOfSides) sides."
   }
}

注意,使用self来就区别传入参数的name和自身的name属性,构造器中的构造参数与函数参数的传递方式一样,所有的属性都需要指定值,可以在声明 时指定(如numberOfSides)也可以在构造函数中指定(如name) 如果你希望在对象被回收时执行清除动作,可以使用deinit创建一个析构器 子类名包含在父类名称之后,使用冒号分隔,类不需要从任何标准的根类继承,所以你可以包含或者忽略父类 子类中的重写方法需要标记上override,如果没有 override,会被编译器检查出错,同样编译器还会检测带override标志的函数是否真的存在于父类中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Square : NamedShape  {
   var sideLength : Double
 
   init ( sideLength : Double , name : String ) {
     self.sideLength  = sideLength
     super.init ( name : name )
     numberOfSides  = 4
   }
 
   func area ( ) - >  Double  {
     return sideLength  * sideLength
   }
 
   override func simpleDescription ( ) - > String  {
     return "A square with sides of length \(sideLength)."
   }
}
let test  = Square ( sideLength : 5.2 , name : "my test square" )
test.area ( )
test.simpleDescription ( )

除了简单属性外,属性还可以具备getter和setter方法

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
class EquilateralTriangle : NamedShape  {
   var sideLength : Double  = 0.0
 
   init ( sideLength : Double , name : String ) {
     self.sideLength  = sideLength
     super.init ( name : name )
     numberOfSides  = 3
   }
 
   var perimeter : Double  {
   get {
     return 3.0 * sideLength
   }
   set {
     sideLength  = newValue  / 3.0
   }
   }
 
   override func simpleDescription ( ) - > String  {
     return "An equilateral triagle with sides of length \(sideLength)."
   }
}
var triangle  = EquilateralTriangle ( sideLength : 3.1 , name : "a triangle" )
triangle.perimeter
triangle.perimeter  = 9.9
triangle.sideLength

在perimeter的setter函数中,新值隐式的命名为newValue,你也可以在set后面的括号中提供一个显式的名称 注意EquilateralTriangle类的构造函数有三步: 1、为子类声明的属性设置值 2、调用父类构造器 3、改变父类定义的属性值,如何用到方法或者getter, setter的工作可以在这之后执行 如果你不需要控制属性,但需要在属性设置前后做些处理,可以使用willSet和didSet,举例,下面的类中可以确保三角形边长与方形边长相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class TriangleAndSquare  {
   var triangle : EquilateralTriangle  {
   willSet  {
     square.sideLength  = newValue.sideLength
   }
   }
   var square : Square  {
   willSet  {
     triangle.sideLength  = newValue.sideLength
   }
   }
   init ( size : Double , name : String ) {
     square  = Square ( sideLength : size , name : name )
     triangle  = EquilateralTriangle ( sideLength : size , name : name )
   }
}
var triangleAndSquare  = TriangleAndSquare ( size : 10 , name : "another test shape" )
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square  = Square ( sideLength : 50 , name : "larger square" )
triangleAndSquare.triangle.sideLength

类中的方法与函数有一个重要区别,函数参数名只在函数体中使用,但方法的参数名在调用方法时也需要(除了第一个参数),默认不论是调用方法时还是在方法内,方法的参数名都是相同的,你可以指定另一个名称用于方法内

1
2
3
4
5
6
7
8
class Counter  {
   var count : Int  = 0
   func incrementBy ( amount : Int , numberOfTimes  times : Int ) {
     count  + = amount  * times
   }
}
var counter  = Counter ( )
counter.incrementBy ( 2 , numberOfTimes : 7 )

当使用可选参数时,可以在方法或者属性操作符前加上(?),如果?前是nil,所有?后面的内容都会忽略,整个表达式的值为nil

1
2
let optionalSquare : Square?  = Square ( sideLength : 2.5 , name : "optional square" )
let sideLength  = optionalSquare?.sideLength

枚举与结构

使用enum创建枚举,与类或者其他可命名的类型一样,枚举可以定义方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum Rank : Int  {
   case Ace  = 1
   case Two , Three , Four , Five , Six , Seven , Eight , Nine , Ten
   case Jack , Queen , King
   func simpleDescription ( ) - > String  {
     switch self  {
     case .Ace :
       return "ace"
     case .Jack :
       return "jack"
     case .Queen :
       return "queen"
     case .King :
       return "king"
     default :
       return String ( self.toRaw ( ) )
     }
   }
}
let ace  = Rank.Ace
let aceRawValue  = ace.toRaw ( )

上面的例子中,枚举中的原始值类型是int,所以你必须指定第一个原始值,余下的会按顺序指定,你也可以使用浮点数或者字符串作为枚举的原始类型 使用toRaw和fromRaw函数转换枚举值和原始值

1
2
3
if let convertedRank  = Rank.fromRaw ( 3 ) {
   let threeDescription  = convertedRank.simpleDescription ( )
}

枚举中的成员数值必须是实际的值,不能通过其他方式设置,实际上对于没有原始值的情况,你也不能为他设置一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
enum Suit  {
   case Spades , Hearts , Diamonds , Clubs
   func simpleDescription ( ) - > String  {
     switch self  {
     case .Spades :
       return "spades"
     case .Hearts :
       return "hearts"
     case .Diamonds :
       return "diamonds"
     case .Clubs :
       return "clubs"
     }
   }
}
let hearts  = Suit.Hearts
let heartsDescription  = hearts.simpleDescription ( )

注意枚举中的Hearts成员引用的两种方式:当指派给常量hearts时,使用了Suit.Hearts全名称,而在switch中,使用 了.Hearts,因为self 已经知道是suit,在知道类型时,你可以使用这种缩写 使用struct创建结构,结构支持许多类似类的功能,比如方法和构造函数,两者最大的区别是,结构是值传递,类为引用传递

1
2
3
4
5
6
7
8
9
struct Card  {
   var rank : Rank
   var suit : Suit
   func simpleDescription ( ) - > String  {
     return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
   }
}
let threeOfSpades  = Card ( rank : .Three , suit : .Spades )
let threeOfSpadesDescription  = threeOfSpades.simpleDescription ( )

枚举成员实例可以拥有关联值,相同枚举成员可以有不同的关联值,你可以在创建实例时设置关联值,关联值与原始值不同,同一个枚举成员的所有实例有相同的原始值,但可以有不同的关联值 举个例子,考虑从服务中获取日升日落的时间,服务可以响应这些信息或者一些异常信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum ServerResponse  {
   case Result ( String , String )
   case Error ( String )
}
 
let success  = ServerResponse.Result ( "6:00 am" , "8:09 pm" )
let failure  = ServerResponse.Error ( "Out of cheese." )
 
switch success  {
case let .Result ( sunrise , sunset ) :
   let serverResponse  = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error ( error ) :
   let serverResponse  = "Failure...  \(error)"
}

注意从VerverResponse中获取的日升日落的具体数值可以在switch条件中获取

协议和扩展

使用protocol声明一个协议

1
2
3
4
protocol ExampleProtocol  {
   var simpleDescription : String  { get }
   mutating func adjust ( )
}

类,枚举,结构都可以使用协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class SimpleClass : ExampleProtocol  {
   var simpleDescription : String  = "A very simple class."
   var anotherProperty : Int  = 69105
   func adjust ( ) {
     simpleDescription  + = "  Now 100% adjusted."
   }
}
var a  = SimpleClass ( )
a.adjust ( )
let aDescription  = a.simpleDescription
 
struct SimpleStructure : ExampleProtocol  {
   var simpleDescription : String  = "A simple structure"
   mutating func adjust ( ) {
     simpleDescription  + = " (adjusted)"
   }
}
var b  = SimpleStructure ( )
b.adjust ( )
let bDescription  = b.simpleDescription

注意,在SimpleStructure声明中使用mutating关键词用于标示这个方法会修改结构,而SimpleClass中不需要设置 mutating标志,是因为类中的方法总是会修改这个类 使用extension为一个已存在的类型增加函数,比如新的方法或者计算属性,你可以使用扩展,保证任意类型具有相同的协议,不管这个类型是从框架还是 类库中来的

1
2
3
4
5
6
7
8
9
extension Int : ExampleProtocol  {
   var simpleDescription : String  {
   return "The number \(self)"
   }
   mutating func adjust ( ) {
     self  + = 42
   }
}
7. simpleDescription

你可以像其他命名类型一样使用协议名称,比如,创建一个由类型不同但使用相同协议的对象组成的集合,当你使用这些类型时,可以当做一个协议类型来使用,协议以外的方法将不可用

1
2
3
let protocolValue : ExampleProtocol  = a
protocolValue.simpleDescription
/ / protocolValue.anotherProperty   / / Uncomment  to see  the error

虽然protocolValue在运行时是SimpleClass类型,但编译器会以ExampleProtocol对待,这意味你可以避免访问协议以外的方法

泛型

尖括号中的名称用于创建一个泛型的函数和类型

1
2
3
4
5
6
7
8
func  repeat ( item : ItemType , times : Int ) - > ItemType[]  {
   var  result = ItemType[] ( )
   for in 0. . times {
     result + = item
   }
   return result
}
repeat ( "knock" , 4 )

也可以用同样的方式在类,枚举,结构中使用泛型

1
2
3
4
5
6
7
/ / Reimplement  the Swift standard library 's optional type
enum OptionalValue  {
   case None
   case Some ( T )
}
var possibleInteger : OptionalValue  = .None
possibleInteger  = .Some ( 100 )

在类型名后使用where指定需求列表,比如指定类型必须实现某个协议,或者要求两种类型相同,或者要求具有某个特定的超类

1
2
3
4
5
6
7
8
9
10
11
func anyCommonElements  < T , where T : Sequence , U : Sequence , T.GeneratorType.Element : Equatable , T.GeneratorType.Element  = = U.GeneratorType.Element > ( lhs : T , rhs : U ) - > Bool  {
   for lhsItem  in lhs  {
     for rhsItem  in rhs  {
       if lhsItem  = = rhsItem  {
         return true
       }
     }
   }
   return false
}
anyCommonElements ( [ 1 , 2 , 3 ] , [ 3 ] )

一般情况下,你可以忽略where,在括号后面简单的写一个协议名或者类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值