swift2.docx
《swift2.docx》由会员分享,可在线阅读,更多相关《swift2.docx(22页珍藏版)》请在冰豆网上搜索。
swift2
在if语句中,条件必须是一个布尔表达式——这意味着像ifscore{...}这样的代码将报错,而不会隐形地与0做对比。
你可以一起使用if和let来处理值缺失的情况。
这些值可由可选值来代表。
一个可选的值是一个具体的值或者是nil以表示值缺失。
在类型后面加一个问号来标记这个变量的值是可选的。
varoptionalString:
String?
="Hello"
print(optionalString==nil)
varoptionalName:
String?
="JohnAppleseed"
vargreeting="Hello!
"
ifletname=optionalName{
greeting="Hello,\(name)"
}
如果变量的可选值是nil,条件会判断为false,大括号中的代码会被跳过。
如果不是nil,会将值赋给let后面的常量,这样代码块中就可以使用这个值了。
另一种处理可选值的方法是通过使用?
?
操作符来提供一个默认值。
如果可选值缺失的话,可以使用默认值来代替。
letnickName:
String?
=nil
letfullName:
String="JohnAppleseed"
letinformalGreeting="Hi\(nickName?
?
fullName)"
TheSwiftProgrammingLanguage中文版
时间简史
本页内容包括:
简单值(SimpleValues)
控制流(ControlFlow)
函数和闭包(FunctionsandClosures)
对象和类(ObjectsandClasses)
枚举和结构体(EnumerationsandStructures)
协议和扩展(ProtocolsandExtensions)
泛型(Generics)
通常来说,编程语言教程中的第一个程序应该在屏幕上打印“Hello,world”。
在Swift中,可以用一行代码实现:
print("Hello,world!
")
如果你写过C或者Objective-C代码,那你应该很熟悉这种形式——在Swift中,这行代码就是一个完整的程序。
你不需要为了输入输出或者字符串处理导入一个单独的库。
全局作用域中的代码会被自动当做程序的入口点,所以你也不需要main()函数。
你同样不需要在每个语句结尾写上分号。
这个教程会通过一系列编程例子来让你对Swift有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解。
注意:
为了获得最好的体验,在Xcode当中使用代码预览功能。
代码预览功能可以让你编辑代码并实时看到运行结果。
下载Playground
简单值
使用let来声明常量,使用var来声明变量。
一个常量的值,在编译的时候,并不需要有明确的值,但是你只能为它赋值一次。
也就是说你可以用常量来表示这样一个值:
你只需要决定一次,但是需要使用很多次。
varmyVariable=42
myVariable=50
letmyConstant=42
常量或者变量的类型必须和你赋给它们的值一样。
然而,你不用明确地声明类型,声明的同时赋值的话,编译器会自动推断类型。
在上面的例子中,编译器推断出myVariable是一个整数(integer)因为它的初始值是整数。
如果初始值没有提供足够的信息(或者没有初始值),那你需要在变量后面声明类型,用冒号分割。
letimplicitInteger=70
letimplicitDouble=70.0
letexplicitDouble:
Double=70
练习:
创建一个常量,显式指定类型为Float并指定初始值为4。
值永远不会被隐式转换为其他类型。
如果你需要把一个值转换成其他类型,请显式转换。
letlabel="Thewidthis"
letwidth=94
letwidthLabel=label+String(width)
练习:
删除最后一行中的String,错误提示是什么?
有一种更简单的把值转换成字符串的方法:
把值写到括号中,并且在括号之前写一个反斜杠。
例如:
letapples=3
letoranges=5
letappleSummary="Ihave\(apples)apples."
letfruitSummary="Ihave\(apples+oranges)piecesoffruit."
练习:
使用\()来把一个浮点计算转换成字符串,并加上某人的名字,和他打个招呼。
使用方括号[]来创建数组和字典,并使用下标或者键(key)来访问元素。
最后一个元素后面允许有个逗号。
varshoppingList=["catfish","water","tulips","bluepaint"]
shoppingList[1]="bottleofwater"
varoccupations=[
"Malcolm":
"Captain",
"Kaylee":
"Mechanic",
]
occupations["Jayne"]="PublicRelations"
要创建一个空数组或者字典,使用初始化语法。
letemptyArray=[String]()
letemptyDictionary=[String:
Float]()
如果类型信息可以被推断出来,你可以用[]和[:
]来创建空数组和空字典——就像你声明变量或者给函数传参数的时候一样。
shoppingList=[]
occupations=[:
]
控制流
使用if和switch来进行条件操作,使用for-in、for、while和repeat-while来进行循环。
包裹条件和循环变量括号可以省略,但是语句体的大括号是必须的。
letindividualScores=[75,43,103,87,12]
varteamScore=0
forscoreinindividualScores{
ifscore>50{
teamScore+=3
}else{
teamScore+=1
}
}
print(teamScore)
在if语句中,条件必须是一个布尔表达式——这意味着像ifscore{...}这样的代码将报错,而不会隐形地与0做对比。
你可以一起使用if和let来处理值缺失的情况。
这些值可由可选值来代表。
一个可选的值是一个具体的值或者是nil以表示值缺失。
在类型后面加一个问号来标记这个变量的值是可选的。
varoptionalString:
String?
="Hello"
print(optionalString==nil)
varoptionalName:
String?
="JohnAppleseed"
vargreeting="Hello!
"
ifletname=optionalName{
greeting="Hello,\(name)"
}
练习:
把optionalName改成nil,greeting会是什么?
添加一个else语句,当optionalName是nil时给greeting赋一个不同的值。
如果变量的可选值是nil,条件会判断为false,大括号中的代码会被跳过。
如果不是nil,会将值赋给let后面的常量,这样代码块中就可以使用这个值了。
另一种处理可选值的方法是通过使用?
?
操作符来提供一个默认值。
如果可选值缺失的话,可以使用默认值来代替。
letnickName:
String?
=nil
letfullName:
String="JohnAppleseed"
letinformalGreeting="Hi\(nickName?
?
fullName)"
switch支持任意类型的数据以及各种比较操作——不仅仅是整数以及测试相等。
letvegetable="redpepper"
switchvegetable{
case"celery":
print("Addsomeraisinsandmakeantsonalog.")
case"cucumber","watercress":
print("Thatwouldmakeagoodteasandwich.")
caseletxwherex.hasSuffix("pepper"):
print("Isitaspicy\(x)?
")
default:
print("Everythingtastesgoodinsoup.")
}
练习:
删除default语句,看看会有什么错误?
注意let在上述例子的等式中是如何使用的,它将匹配等式的值赋给常量x。
运行switch中匹配到的子句之后,程序会退出switch语句,并不会继续向下运行,所以不需要在每个子句结尾写break。
你可以使用for-in来遍历字典,需要两个变量来表示每个键值对。
字典是一个无序的集合,所以他们的键和值以任意顺序迭代结束。
letinterestingNumbers=[
"Prime":
[2,3,5,7,11,13],
"Fibonacci":
[1,1,2,3,5,8],
"Square":
[1,4,9,16,25],
]
varlargest=0
for(kind,numbers)ininterestingNumbers{
fornumberinnumbers{
ifnumber>largest{
largest=number
}
}
}
print(largest)
练习:
添加另一个变量来记录现在和之前最大数字的类型。
使用while来重复运行一段代码直到不满足条件。
循环条件也可以在结尾,保证能至少循环一次。
varn=2
whilen<100{
n=n*2
}
print(n)
varm=2
repeat{
m=m*2
}whilem<100
print(m)
你可以在循环中使用..<来表示范围,也可以使用传统的写法,两者是等价的:
varfirstForLoop=0
foriin0..<4{
firstForLoop+=i
}
print(firstForLoop)
varsecondForLoop=0
forvari=0;i<4;++i{
secondForLoop+=i
}
print(secondForLoop)
使用..<创建的范围不包含上界,如果想包含的话需要使用...。
函数和闭包
使用func来声明一个函数,使用名字和参数来调用函数。
使用->来指定函数返回值的类型。
funcgreet(name:
String,day:
String)->String{
return"Hello\(name),todayis\(day)."
}
greet("Bob",day:
"Tuesday")
练习:
删除day参数,添加一个参数来表示今天吃了什么午饭。
使用元组来让一个函数返回多个值。
该元组的元素可以用名称或数字来表示。
funccalculateStatistics(scores:
[Int])->(min:
Int,max:
Int,sum:
Int){
varmin=scores[0]
varmax=scores[0]
varsum=0
forscoreinscores{
ifscore>max{
max=score
}elseifscoremin=score
}
sum+=score
}
return(min,max,sum)
}
letstatistics=calculateStatistics([5,3,100,3,9])
print(statistics.sum)
print(statistics.2)
函数可以带有可变个数的参数,这些参数在函数内表现为数组的形式:
funcsumOf(numbers:
Int...)->Int{
varsum=0
fornumberinnumbers{
sum+=number
}
returnsum
}
sumOf()
sumOf(42,597,12)
练习:
写一个计算参数平均值的函数。
函数可以嵌套。
被嵌套的函数可以访问外侧函数的变量,你可以使用嵌套函数来重构一个太长或者太复杂的函数。
funcreturnFifteen()->Int{
vary=10
funcadd(){
y+=5
}
add()
returny
}
returnFifteen()
函数是第一等类型,这意味着函数可以作为另一个函数的返回值。
funcmakeIncrementer()->(Int->Int){
funcaddOne(number:
Int)->Int{
return1+number
}
returnaddOne
}
varincrement=makeIncrementer()
increment(7)
函数也可以当做参数传入另一个函数。
funchasAnyMatches(list:
[Int],condition:
Int->Bool)->Bool{
foriteminlist{
ifcondition(item){
returntrue
}
}
returnfalse
}
funclessThanTen(number:
Int)->Bool{
returnnumber<10
}
varnumbers=[20,19,7,12]
hasAnyMatches(numbers,condition:
lessThanTen)
函数实际上是一种特殊的闭包:
它是一段能之后被调取的代码。
闭包中的代码能访问闭包所建作用域中能得到的变量和函数,即使闭包是在一个不同的作用域被执行的-你已经在嵌套函数例子中所看到。
你可以使用{}来创建一个匿名闭包。
使用in将参数和返回值类型声明与闭包函数体进行分离。
numbers.map({
(number:
Int)->Intin
letresult=3*number
returnresult
})
练习:
重写闭包,对所有奇数返回0。
有很多种创建更简洁的闭包的方法。
如果一个闭包的类型已知,比如作为一个回调函数,你可以忽略参数的类型和返回值。
单个语句闭包会把它语句的值当做结果返回。
letmappedNumbers=numbers.map({numberin3*number})
print(mappedNumbers)
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。
当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。
当一个闭包是传给函数的唯一参数,你可以完全忽略括号。
letsortedNumbers=numbers.sort{$0>$1}
print(sortedNumbers)
对象和类
使用class和类名来创建一个类。
类中属性的声明和常量、变量声明一样,唯一的区别就是它们的上下文是类。
同样,方法和函数声明也一样。
classShape{
varnumberOfSides=0
funcsimpleDescription()->String{
return"Ashapewith\(numberOfSides)sides."
}
}
练习:
使用let添加一个常量属性,再添加一个接收一个参数的方法。
要创建一个类的实例,在类名后面加上括号。
使用点语法来访问实例的属性和方法。
varshape=Shape()
shape.numberOfSides=7
varshapeDescription=shape.simpleDescription()
这个版本的Shape类缺少了一些重要的东西:
一个构造函数来初始化类实例。
使用init来创建一个构造器。
classNamedShape{
varnumberOfSides:
Int=0
varname:
String
init(name:
String){
self.name=name
}
funcsimpleDescription()->String{
return"Ashapewith\(numberOfSides)sides."
}
}
注意self被用来区别实例变量。
当你创建实例的时候,像传入函数参数一样给类传入构造器的参数。
每个属性都需要赋值——无论是通过声明(就像numberOfSides)还是通过构造器(就像name)。
如果你需要在删除对象之前进行一些清理工作,使用deinit创建一个析构函数。
子类的定义方法是在它们的类名后面加上父类的名字,用冒号分割。
创建类的时候并不需要一个标准的根类,所以你可以忽略父类。
子类如果要重写父类的方法的话,需要用override标记——如果没有添加override就重写父类方法的话编译器会报错。
编译器同样会检测override标记的方法是否确实在父类中。
classSquare:
NamedShape{
varsideLength:
Double
init(sideLength:
Double,name:
String){
self.sideLength=sideLength
super.init(name:
name)
numberOfSides=4
}
funcarea()->Double{
returnsideLength*sideLength
}
overridefuncsimpleDescription()->String{
return"Asquarewithsidesoflength\(sideLength)."
}
}
lettest=Square(sideLength:
5.2,name:
"mytestsquare")
test.area()
test.simpleDescription()
练习:
创建NamedShape的另一个子类Circle,构造器接收两个参数,一个是半径一个是名称,在子类Circle中实现area()和simpleDescription()方法。
除了储存简单的属性之外,属性可以有getter和setter。
classEquilateralTriangle:
NamedShape{
varsideLength:
Double=0.0
init(sideLength:
Double,name:
String){
self.sideLength=sideLength
super.init(name:
name)
numberOfSides=3
}
varperimeter:
Double{
get{
return3.0*sideLength
}
set{
sideLength=newValue/3.0
}
}
overridefuncsimpleDescription()->String{
return"Anequilateraltriaglewithsidesoflength\(sideLength)."
}
}
vartriangle=EquilateralTriangle(sideLength:
3.1,name:
"atriangle")
print(triangle.perimeter)
triangle.perimeter=9.9
print(triangle.sideLength)
在perimeter的setter中,新值的名字是newValue。
你可以在set之后显式的设置一个名字。
注意EquilateralTriangle类的构造器执行了三步:
设置子类声明的属性值
调用父类的构造器
改变父类定义的属性值。
其他的工作比如调用方法、getters和setters也可以在这个阶段完成。
如果你不需要计算属性,但是仍然需要在设置一个新值之前或者之后运行代码,使用willSet和didSet。
比如,下面的类确保三角形的边长总是和正方形的边长相同。
classTriangleAndSquare{
vartriangle:
EquilateralTriangle{
willSet{
square.sideLength=newValue.sideLength
}
}
varsquare:
Square{
willSet{
triangle.sideLength=newValue.sideLength
}
}
init(size:
Double,name:
String){
square=Square(sideLength:
size,name:
name)
triangle=EquilateralTriangle(sideLength:
size,name:
name)
}
}
vartriangleAndSquare=TriangleAndSquare(size:
10,name:
"anothertestshape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square=Square(sideLength:
50,name:
"largersquare")
print(triangleAndSquare.triangle.sideLength)
处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加?
。
如果?
之前的值是nil,?
后面的东西都会被忽略,并且整个表达式返回nil。
否则,?
之后的东西都会被运行。
在这两种情况下,整个表达式的值也是一个可选值。
letoptionalSquare:
Square?
=Squar