前言:网上一直没有找到用Swift开发IOS的好的教程,所以找了官网的文档翻译一下算了。如有错误欢迎指正。博主首发CSDN,mcf171专栏。
博客链接:mcf171的博客
原文链接:Learn the Essentials of Swift
——————————————————————————————
上一篇文章讲的是Swift里面的控制语句接下来说的是函数和方法
函数和方法
函数是一个可重复使用的具有名分的一块代码,能在程序的多个地方进行引用。
使用关键字 func 来声明一个函数,一个函数的声明包括0个或者多个参数,格式为 参数名:参数类型 ,这个是一个函数必备的部分。可选的部分是一个函数可以有返回值,接在符号->后面。函数体在花括号里面{}
func greet(name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
函数的调用只需要写下函数名和它的参数即可。当调用函数时,第一个参数可以不写名字,但是后面的参数必须写上名字。
greet("Anna", day: "Tuesday")
greet("Bob", day: "Friday")
greet("Charlie", day: "a nice day")
定义在一个特定类型内的函数称为方法。方法和被定义的类型紧密联系,只能在定义的类型或者子类中被调用,比如说早期的switch语句,或者String类型中的hasSuffix()方法。
let exampleString = "hello"
if exampleString.hasSuffix("lo") {
print("ends in lo")
}
var array = ["apple", "banana", "dragonfruit"]
array.insert("cherry", atIndex: 2)
array
类和初始化
在面向对象编程中,程序的行为主要是基于对象之间的相互调用。对象是类的实例,类可以看做是对象的蓝图。类以属性的形式存储了关于类本身的额外信息,通过方法定义了类的行为。
使用class关键字来定义类。类中属性声明方式和常量或者变量声明的方式一样,除了这些声明在类的内部之外。同样方法和函数的声明也是相同的意思。下面的例子声明了Shape类,有着numberOfSides的属性以及一个simpleDescription()的方法。
class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
创建类的实例——对象,则通过使用类名之后接一个括号。使用点语法来访问实例的属性和方法。在下述实例中,shape是类Shape的实例。
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
上述Shape类缺少了一个非常重要的部分:初始化。一个初始化器是将一个类进行实例化过程中进行准备的方法,在这个方法中可以设置每个属性的初始值,已经任何其他步骤,通过init关键字来进行创建。
class NamedShape {
var numberOfSides = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
注意 self 是用来区分属性名name和参数名name的,每一个属性都需要赋予一个值,不管是在声明的时候还是在init函数里面。
我们不需要通过直接写init来调用init方法,可以直接在创建对象的时候在括号中写入合适的参数就行。
let namedShape = NamedShape(name: "my named shape")
子类如果要重写父类的方法,必须加上关键字 override。没有这个关键字,编译器会认为是一个错误。编译器同样也会探测写了override的关键字但是父类没有这个方法的错误。
下面是一个Square类,是NamedShape的子类
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 testSquare = Square(sideLength: 5.2, name: "my test square")
testSquare.area()
testSquare.simpleDescription()
注意这个Square初始化有三个不同的步骤:
1、这是子类Square的属性值。
2、调用父类的初始化器
3、改变父类中定义的属性值。一些额外的步骤比如说调用方法,getter或者setter方法也可以在这里调用。
有时候初始化一个对象会失败,比如传入的参数超出了某个范围,或者传入参数过少。初始化器失败也能成功的初始化一个对象,这种情况叫做允许失败的初始化器(failable initializers)。一个可失败的初始化器肯以返回一个nil值。使用init?来声明一个可失败的初始化器。
class Circle: NamedShape {
var radius: Double
init?(radius: Double, name: String) {
self.radius = radius
super.init(name: name)
numberOfSides = 1
if radius <= 0 {
return nil
}
}
override func simpleDescription() -> String {
return "A circle with a radius of \(radius)."
}
}
let successfulCircle = Circle(radius: 4.2, name: "successful circle")
let failedCircle = Circle(radius: -7, name: "failed circle")
(下面这一段博主也没太理解)
初始化器可以有一些列关键字来进行修饰,一个特定的初始化器不要求任何关键字。这个初始化器表现的像一个雷的主要初始化器之一,任何类中初始化器都需要被特定的初始化器最后调用。
在初始化器后跟convenience关键字表明是一个convenience的初始化器。便利的初始化器是第二的初始化器。这种初始化器医科院增加一些行为或者定制,但是最终都会被特定的初始化器进行调用。
初始化器后跟required的关键字表明这个类的每个子类都必须实现他自己的初始化器。
类型转换是检查一个实例类型的方式。
向下转型可能会失败,因此类型转换符号有两种形式。可选的形式是关键字 as?,这个将返回一个想向下转型类型的optional值。强转形式为 as!,这个关键字将尝试向下转换并且强行拆包这个结果这两个行为作为一个组合动作。
当不确定向下转型是否成功的时候,可以使用optional类型转换操作符号as?。这个形式的操作符总能得到一个optional的值,当转型不可能的时候会返回nil值。这样能让我们通过返回值知道是否转型成功。
当我们确定向下转型肯定会成功的时候可以使用符号as!。如果转型失败的话,这种形式的操作符将会报一个运行时错误。
下面的例子将展示as?操作符来检测数组中的shape是否是一个square还是一个triangele。每次统计两个的个数,最后打印结果。
class Triangle: NamedShape {
init(sideLength: Double, name: String) {
super.init(name: name)
numberOfSides = 3
}
}
let shapesArray = [Triangle(sideLength: 1.5, name: "triangle1"), Triangle(sideLength: 4.2, name: "triangle2"), Square(sideLength: 3.2, name: "square1"), Square(sideLength: 2.7, name: "square2")]
var squares = 0
var triangles = 0
for shape in shapesArray {
if let square = shape as? Square {
squares++
} else if let triangle = shape as? Triangle {
triangles++
}
}
print("\(squares) squares and \(triangles) triangles.")
ps:可以将 as? 替换为 as! 看报什么样的错误
枚举和结构体
在Swift中,类不是唯一一种定义数据类型的方式,枚举和结构体也有相似的能力,但是是在不同的应用场景下使用。
枚举类型使用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.rawValue)
}
}
}
let ace = Rank.Ace
let aceRawValue = ace.rawValue
上述例子中,raw-value类型就是Int,所以必须指定第一个raw value,剩下的raw values将按顺序进行赋值。同时也可以使用string或者 floating-point数字作为枚举类型的raw-value。可以使用raw value属性来访问枚举类型的raw值
使用 init?(rawValue:)初始化器来通过一个raw值创建一个枚举类型的实例。
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}
一个枚举类的成员值是实际的值,不仅仅是写出他们raw值的另外一种方式,事实上如果没有有意义的raw值的时候,没必要一定提供一个。
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来创建结构体
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()
接口
(讲道理楼主还是明白接口的意义,但是不太明白Swift的接口描述,目前来看个人建议看英语原文)
接口定义了方法,属性和其他适合特定任务或者块的函数蓝图。接口实际上并没有提供上述的实现,只是说明了实现应该长什么样。接口可以被类、结构体或者枚举类型进行具体实现。任何满足接口需求的类型都称为服从接口。
使用关键字protocol来描述接口
protocol ExampleProtocol {
var simpleDescription: String { get }
func adjust()
}
ps:在simpleDescription属性后跟着{get},表明这个属性是只读的,也就是这个属性只可以读取不可以修改。
类、结构体和枚举通过在名称后面接一个冒号然后写上接口名。一个类型能实现任何数量的接口,通过逗号分割。如果一个类有一个父类,那么父类首先出现在这个列表的第一个,后面接的都是接口。我们可以通过实现所有接口的要求来实现所有的接口。
下面是SimpleClass实现了ExampleProtocol的接口,通过实现simpleDescription属性和adjust()方法来实现接口。
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
接口是一等类别,也就意味着可以像其他命名类别一样对待。比如说我们可以创建一个ExampleProtocol数组并且调用数组中每一个元素的adjust()方法。
class SimpleClass2: ExampleProtocol {
var simpleDescription: String = "Another very simple class."
func adjust() {
simpleDescription += " Adjusted."
}
}
var protocolArray: [ExampleProtocol] = [SimpleClass(), SimpleClass(), SimpleClass2()]
for instance in protocolArray {
instance.adjust()
}
protocolArray
Swift 和 Cocoa Touch
Swift 可以和Cocoa Touch进行无缝的互操作,因此Apple 一系列用来开发apps的框架都可以用来开发iOS。当我们完成后续课程的时候,将帮助我们对于Swift和CocoTouch的交互有一个基本的了解。
到目前为止我们学习的都是来自Swift标准库里面的数据类型。Swift标准库是一系列数据类型的集合,并且是针对Swift功能进行设计。比如说String和Array就是标准库里面的数据类型。
let sampleString: String = "hello"
let sampleArray: Array = [1, 2, 3.1415, 23, 42]
ps:通过在Xcode中使用 Optiona + 单击来阅读标准库中的数据类型。
当编写iOS apps的时候,我们可能会使用不仅仅是Swift的标准库内容。在iOS app开发中最常用的框架之一是UIKit。UIKit包含了在App中非常有用的UI类。
为了访问UIKit,只需要通过import关键字即可。
import UIKit
在引入UIKit之后,就可以通过Swift语法来使用UIKit类和其中的方法、属性等等。
let redSquare = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
redSquare.backgroundColor = UIColor.redColor()
接下来课程将会学习很多UIKit中的类,因此我们将能明白import的重要性。
随着本课程对于Swift知识的了解,接下来将进入编写一个五脏俱全的app课程。