苹果近日召开了全球开发者大会(WWDC),推出了一种新的开发语言 Swift,有人说是用来替代Objective-C的,以下是苹果官方文档,第一时间整理了一下,觉得还是很有前景的,有些英文看不懂的就直接复制了,接下来的时间还是要持续关注呢。
官方链接:
https://developer.apple.com/library/prerelease/ios/referencelibrary/GettingStarted/LandingPage/index.html#//apple_ref/doc/uid/TP40014345
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html#//apple_ref/doc/uid/TP40014097-CH2-XID_1
简介
Swift是iOS和OS X应用编程的一种新的编程语言,基于C和Objective-C,却没有C的一些兼容约束。Swift采用了安全的编程模式和添加现代的功能来让编程更加简单、灵活和有趣。界面基于广大人民群众爱戴的Cocoa和Cocoa Touch框架,展示了软件开发的新方向。
Swift已经存在了多年。Apple基于已有的编译器、调试器、框架作为其基础架构。通过ARC(Automatic Reference Counting,自动引用计数)来简化内存管理。我们的框架栈则一直基于Cocoa。Objective-C进化支持了块、collection literal和模块,允许现代语言的框架无需深入即可使用。
Objective-C开发者会感到Swift的似曾相识。Swift采用了Objective-C的命名参数和动态对象模型。提供了对Cocoa框架和mix-and-match的互操作性。基于这些基础,Swift引入了很多新功能和结合面向过程和面向对象的功能。
Swift对新的程序员也是友好的。他是工业级品质的系统编程语言,却又像脚本语言一样的友好。他支持playground,允许程序员实验编写Swift代码功能并立即看到结果,而无需麻烦的构建和运行一个应用。
A Swift Tour
许多编程语言一开始都以hello world来开个好头,用Swift来写,只有一行:
println("Hello, world")
如果你有C/C++,Objective-C的基础,看起来应该会很熟悉,在Swift里,这一行就是一个完整的代码了,不需要再引入其他的库文件了,全局的代码就已经是程序的入口了,所以也不需main方法,甚至也不需要分号结尾
使用let定义常量,var定义变量,常量并不需编译时制定,但至少需要赋值一次,意味着只需定义一次常量,就可在所有地方使用
var myVariable = 42 myVariable = 50 let myConstant = 42
上面例子中,编译自动推断myVariable是个整数类型,不需要显示声明,在以后的赋值也要赋予相同的类型
如果在初始化的时候没有提供足够的信息或根本没有初始化赋值,可以在后面声明,用冒号分割
let implicitInteger = 70 let implicitDouble = 70.0 let explicitDouble: Double = 70
已经赋的值不会隐式转换为其他类型,如果需要转换为其他类型,需要明确写出构造所需类型的实例
let label = "The width is " let width = 94 let widthLabel = label + String(width)还有一种更简单的写法在字符串中显示变量值,变量值写入小括号中,并在前面添加反斜线
let apples = 3 let oranges = 5 let appleSummary = "I have \(apples)apples." let fruitSummary = "I have\(apples + oranges) pieces of fruit."
用方括号[] 创建数据或字典,通过下标或键值访问元素
var shoppingList =["catfish", "water", "tulips", "bluepaint"] shoppingList[1] = "bottle ofwater" var occupations = [ "Malcolm": "Captain", "Kaylee": "Mechanic", ] occupations["Jayne"] ="Public Relations"
如果想要创建一个空的数组或字典,使用初始化语句
let emptyArray = String[]() let emptyDictionary =Dictionary<String, Float>()
如果不确定类型,可以直接写成 [] 表示空数组,[:]表示空字典
shoppingList = [] //Went shopping and bought everything.
使用if和switch作为条件控制,使用for-in,for,while和do-while来循环,小括号不是必须的,但大括号是必须的
let individualScores = [75, 43, 103, 87, 12] var teamScore = 0 for score in individualScores { if score > 50 { teamScore += 3 } else { teamScore += 1 } } teamScore
在if语句中,条件必须是布尔表达式,意味着 if score{…} 是错误的,不能隐式的与0比较
可以同时使用if和let来防止变量值的丢失,这些值是可选的,可选值可包含一个值或一个nil来指定值是否存在,类型后面的(?) 表示值是可选的
var optionalString: String? ="Hello" optionalString == nil var optionalName: String? = "JohnAppleseed" var greeting = "Hello!" if let name = optionalName { greeting = "Hello, \(name)" }
如果可选的值是nil,那么条件为false的语句将会跳过,Otherwise,the optional value is unwrapped and assigned to the constant after let
, which makes the unwrapped value available insidethe block of code.
switch支持多种数据以及多种比较,不局限于整数和测试数相等
let vegetable = "red pepper" switch vegetable { case "celery": let vegetableComment = "Add some raisins and make ants on alog." case "cucumber","watercress": let vegetableComment = "That would make a good tea sandwich." case let x wherex.hasSuffix("pepper"): let vegetableComment = "Is it a spicy \(x)?" default: let vegetableComment = "Everything tastes good in soup." }
switch在匹配之后就会跳出程序块,继续执行下一个逻辑代码,而不需要break语句来跳出
使用for-in来遍历字典中的每一个值,提供一对名称来为每个键值对使用
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) ininterestingNumbers { for number in numbers { if number > largest { largest = number } } } largest
使用while来重复执行一代码块直到条件成立,循环的条件可放在while末尾来确保循环至少执行一次
var n = 2 while n < 100 { n = n * 2 } n var m = 2 do { m = m * 2 } while m < 100 m
可在循环中定义一个索引,通过..来表示索引所表示的范围,以下两个例子做同样的事情
var firstForLoop = 0 for i in 0..3 { firstForLoop += i } firstForLoop var secondForLoop = 0 for var i = 0; i < 3; ++i { secondForLoop += 1 } secondForLoo
使用 .. 构建的范围不包含最大值,使用 … 构建的范围大小值都包含
使用func声明一个函数,调用函数的方法是使用函数名称加括号里的参数列表,使用->分割参数名和返回的类型
func greet(name: String, day: String) -> String { return "Hello \(name), today is \(day)." } greet("Bob","Tuesday")
使用tuple可以返回多个值
func getGasPrices() -> (Double,Double, Double) { return (3.59, 3.69, 3.79) } getGasPrices()
函数参数个数可以不固定,用数组表示
func sumOf(numbers: Int...) -> Int { var sum = 0 for number in numbers { sum += number } return sum } sumOf() sumOf(42, 597, 12)
函数可以嵌套,内嵌函数可访问其定义所在函数的变量
func returnFifteen() -> Int { var y = 10 func add() { y += 5 } add() return y } returnFifteen()
函数是第一类型的,意味着函数也可以返回另一个函数
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)
一个函数可以接收其他函数作为参数
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取参数和方法体的返回值
numbers.map({ (number: Int) -> Int in let result = 3 * number return result })
有很多种方式来写一些更简单的闭包,当一个闭包的类型是已知的,比如说对委托的回调,可以省略类型的参数和返回值,单一语句的闭包可以直接把值返回
numbers.map({ number in 3 * number })
可以用数字取代名字来引用一个参数,对短的闭包是很有用的,A closure passed as the last argument to a function can appearimmediately after the parentheses.
sort([1, 5, 3, 12, 2]) { $0 > $1 }
使用class 加类名来创建一个类,类中属性的声明和常量及变量的声明是一样的,除了这是在类的context中,方法和函数的声明也是一样的
class Shape { var numberOfSides = 0 func simpleDescription() -> String { return "A shape with \(numberOfSides) sides." } }
在类名的后面添加小括号来创建类的实例,使用点操作符访问类的属性和方法
var shape = Shape() shape.numberOfSides = 7 var shapeDescription =shape.simpleDescription()
使用init来创建一个构造器来在创建实例的时候初始化
class NamedShape { var numberOfSides: Int = 0 var name: String init(name: String) { self.name = name } func simpleDescription() -> String { return "A shape with \(numberOfSides) sides." } }
Notice how self is used todistinguish the name property from the name argument to the initializer. Thearguments to the initializer are passed like a function call when you create aninstance of the class. Every property needs a value assigned—either in itsdeclaration (as with numberOfSides) or in the initializer (as with name).
如果需要在对象销毁时进行清理工作,使用deinit创建析构器
子类包含其父类的名称,以冒号分隔,在继承标准子类时不需要声明,所以可以根据需要包括或者忽略父类
子类的方法可通过override重载父类的方法,没有的话会编译错误,编译器也会检查那些没有被重载的方法
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()
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. 改变父类定义的值,Any additional setup work that uses methods, getters, or setters canalso be done at this point.
如果不需要计算属性但却需要赋值后才能继续执行,可以使用willSet和didSet,一下例子保证三角形的边长等于矩形的边长
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
函数和类的方法有一个不同点,函数的参数名仅用于函数,方法中的参数名也可以用来调用方法(除了第一个参数),缺省时,一个方法有一个同名的参数,调用时就是使用参数本身,可以指定第二个名字,在方法内部使用
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,另外,可选值是unwrapped的,所有在?后的代码都作为unwrapped值,在两种情况下,真个表达式都是可选值
let optionalSquare: Square? =Square(sideLength: 2.5, name: "optional square") let sideLength =optionalSquare?.sideLength
使用enum来创建枚举,跟类和其他命名一样,枚举也可以定义方法
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函数可以转换初始值和枚举值
if let convertedRank = Rank.fromRaw(3){ let threeDescription = convertedRank.simpleDescription() }
枚举中成员就是实际使用的值了,而不是其他方式写的初始值,事实上,如果不提供第一个初始值的话,这种情况才是初始值
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()
Notice the two ways that theHearts member of the enumeration is referred to above: When assigning a valueto the hearts constant, the enumeration member Suit.Hearts is referred to byits full name because the constant doesn’t have an explicit type specified.Inside the switch, the enumeration is referred to by the abbreviated form.Hearts because the value of self is already known to be a suit. You can usethe abbreviated form anytime the value’s type is already known.
使用struct来创建结构体,结构体支持多个与类相同的行为,包括方法和构造器,区别是代码之间总是使用拷贝(值传递),而类是传递引用
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()
一个枚举的实例成员可以拥有实例的值。相同枚举成员实例可以有不同的值。你在创建实例时赋值。指定值和原始值的区别:枚举的原始值与其实例相同,你在定义枚举时提供原始值。
例如,假设情况需要从服务器获取太阳升起和降落时间。服务器可以响应相同的信息或一些错误信息
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)" }
注意日出和日落时间实际上来自于对 ServerResponse 的部分匹配来选择的
使用protocol来声明一个协议
protocol ExampleProtocol { var simpleDescription: String { get } mutating func adjust() }
协议可以被类、枚举和结构使用
class SimpleClass: ExampleProtocol { var simpleDescription: String = "A very simple class." var anotherProperty: Int = 69105 func adjust() { simpleDescription += " Now100% 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中并不需要设置mutaing标志,因为类中的方法会修改这个类
使用extension去添加已有的类型,如新方法和计算值,可以使用extension,保证任意类型具有相同的协议,不管这个类型是从框架还是类库中来的
extension Int: ExampleProtocol { var simpleDescription: String { return "The number \(self)" } mutating func adjust() { self += 42 } } 7.simpleDescription
可以像其他命令类型一样使用协议命名,比如创建一个对象集合有不同的类型,但所有类型都符合同一个协议,当使用一些协议类型的值时,在协议外部定义的方法是不可用的
let protocolValue: ExampleProtocol = a protocolValue.simpleDescription // protocolValue.anotherProperty // Uncomment to see the error
Even though the variableprotocolValue has a runtime type of SimpleClass, the compiler treats it as thegiven type of ExampleProtocol. This means that you can’t accidentally accessmethods or properties that the class implements in addition to its protocolconformance.
在尖括号里的名字就定义了一个泛型的函数或类型
func repeat<ItemType>(item:ItemType, times: Int) -> ItemType[] { var result = ItemType[]() for i in 0..times { result += item } return result } repeat("knock", 4)
泛型也可以用于函数和方法,类、枚举及结构
// Reimplement the Swift standardlibrary's optional type enum OptionalValue<T> { case None case Some(T) } var possibleInteger:OptionalValue<Int> = .None possibleInteger = .Some(100)
在类型后面使用where来指定一个需求列表,比如指定某个泛型必须实现某种协议,或要求两种类型必须相同,或要求某个类必须具有一个父类
func anyCommonElements <T, U whereT: 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,在括号后面写一个协议名或泛型 Writing<T: Equatable> is the same as writing <T where T: Equatable>.