数据结构
变量与常量
常量和变量把一个名字
//基本使用
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
//定义变量
var myVariable = 123
//定义常量
let myConstantVariable = 123
// 隐式指定整数类型
var anInteger = 2
// 明确指定整数类型
let anExplicitInteger :Int = 2
你可以在一行中声明多个常量或者多个变量,用逗号隔开:
var x = 0.0, y = 0.0, z = 0.0
常量与变量名不能包含数学符号,箭头,保留的
类型标注
当你声明常量或者变量的时候可以加上类型标注(type annotation),说明常量或者变量中要存储的值的类型。如果要添加类型标注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。
类型安全与类型推测
Swift 是一个类型安全(type safe )的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个String,你绝对不可能不小心传进去一个Int。由于 Swift 是类型安全的,所以它会在编译你的代码时进行类型检查(type checks),并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误。当你要处理不同类型的值时,类型检查可以帮你避免错误。然而,这并不是说你每次声明常量和变量的时候都需要显式指定类型。如果你没有显式指定类型,Swift 会使用类型推测(type inference)来选择合适的类型。有了类型推测,编译器可以在编译代码的时候自动推测出表达式的类型。原理很简单,只要检查你赋的值即可。
因为有类型推测,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。当你声明常量或者变量并赋初值的时候类型推测非常有用。当你在声明常量或者变量的时候赋给它们一个字面量(literal value或literal)即可触发类型推测。(字面量就是会直接出现在你代码中的值,比如42和3.14159。)
别名
类型别名(type aliases)就是给现有类型定义另一个名字。你可以使用typealias关键字来定义类型别名。当你想要给现有类型起一个更有意义的名字时,类型别名非常有用。假设你正在处理特定长度的外部资源的数据:
typealias AudioSample = UInt16
定义了一个类型别名之后,你可以在任何使用原始名的地方使用别名:
var maxAmplitudeFound = AudioSample.min // maxAmplitudeFound 现在是 0
本例中,AudioSample被定义为UInt16的一个别名。因为它是别名,AudioSample.min实际上是UInt16.min,所以会给maxAmplitudeFound赋一个初值0。
类型判断与转换
as、as?、as!
在as
关键字来进行类型的转换,而是否是强制转换则是根据上下文而定,在as
关键字拆分为了as!
与as?
这两个单独的操作符,其具体的用法可以参考如下:
class Animal {}
class Dog: Animal {}
let a: Animal = Dog()
a as Dog // now raises the error: "'Animal is not convertible to 'Dog';
// ... did you mean to use 'as!' to force downcast?"
a as! Dog // forced downcast is allowed
let d = Dog()
d as Animal // upcast succeeds
class Animal {}
class Cat: Animal {}
class Dog: Animal {
var name = "Spot"
}
let dog: Dog? = nil
dog?.name // evaluates to nil
dog!.name // triggers a runtime error
let animal: Animal = Cat()
animal as? Dog // evaluates to nil
animal as! Dog // triggers a runtime error
基本类型
AnyObject
数值类型
整数就是没有小数部分的数字,比如
- 整数范围
你可以访问不同整数类型的
let minValue = UInt8.min // minValue 为 0,是 UInt8 类型的最小值 let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型的最大值
类型 | 长度 | 说明 |
---|---|---|
Int | -2147483648~2147483647 | |
UInt | 注意:尽量不要使用 |
|
浮点数 |
随机数
科学计算
类型转换
布尔类型
Swift 有一个基本的布尔(Boolean)类型,叫做Bool。布尔值指逻辑上的(logical),因为它们只能是真或者假。Swift 有两个布尔常量,true和false:
let orangesAreOrange = true let turnipsAreDelicious = false
orangesAreOrange和turnipsAreDelicious的类型会被推测为Bool,因为它们的初值是布尔字面量。就像之前提到的Int和Double一样,如果你创建变量的时候给它们赋值true或者false,那你不需要将常量或者变量声明为Bool类型。初始化常量或者变量的时候如果所赋的值类型已知,就可以触发类型推测,这让 Swift 代码更加简洁并且可读性更高。
当你编写条件语句比如if语句的时候,布尔值非常有用:
if turnipsAreDelicious {
println("Mmm, tasty turnips!")
} else {
println("Eww, turnips are horrible.")
}
// 输出 "Eww, turnips are horrible."
条件语句,例如
let i = 1 if i { // 这个例子不会通过编译,会报错 }
然而,下面的例子是合法的:
let i = 1 if i == 1 { // 这个例子会编译成功 }
i == 1的比较结果是Bool类型,所以第二个例子可以通过类型检查。类似i == 1这样的比较,请参考基本操作符。和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的意图总是清晰的。
空类型
可选类型(Optional)
var stringValue : String
//error: variable 'stringValue' used before being initialized
//let hashValue = stringValue.hashValue
let hashValue = stringValue.hashValue
enum
,里面有None
和Some
两种类型。其实所谓的Optional.None
Optional.Some
Some(T)
包装{Some "hello world"}
的原因,这里是
enum Optional<T> : LogicValue, Reflectable {
case None
case Some(T)
init()
init(_ some: T)
/// Allow use in a Boolean context.
func getLogicValue() -> Bool
/// Haskell's fmap, which was mis-named
func map<U>(f: (T) -> U) -> U?
func getMirror() -> Mirror
}
声明为?
即可。如
var strValue: String? //?相当于下面这种写法的语法糖
var strValue: Optional<String>
上面这个
Optional 的声明,意思不是”我声明了一个Optional 的String 值”, 而是”我声明了一个Optional 类型值,它可能包含一个String 值,也可能什么都不包含”,也就是说实际上我们声明的是Optional 类型,而不是声明了一个String 类型,这一点需要铭记在心。
一旦声明为
if strValue {
//do sth with strValue
}
缘由
为什么要这么设计呢?苹果官方给出的解释是,因为
- (NSString *)findStockCode:(NSString *)company {
if ([company isEqualToString:@"Apple"]) {
return @"AAPL";
} else if ([company isEqualToString:@"Google"]) {
return @"GOOG";
}
return nil;
}
在上面的方法里,你可以用
NSString *stockCode = [self findStockCode:@"Facebook"]; // nil is returned
NSString *text = @"Stock Code - ";
NSString *message = [text stringByAppendingString:stockCode]; // runtime error
NSLog(@"%@", message);
这段代码在编译时不会有任何问题,但是如果输入的是
func findStockCode(company: String) -> String? {
if (company == "Apple") {
return "AAPL"
} else if (company == "Google") {
return "GOOG"
}
return nil
}
var stockCode:String? = findStockCode("Facebook")
let text = "Stock Code - "
let message = text + stockCode // compile-time error
println(message)
在上面的代码里,
强制解析
注意,一旦声明为可选类型,就不再是原来的普通类型了,虽然只是简单的加了个
//error: 'String?' does not have a member named 'hashValue'
//let hashValue = strValue.hashValue
// ^ ~~~~~~~~~
let hashValue = strValue.hashValue
如果需要获取到可选类型中的值,就要用到了
let hashValue = strValue!.hashValue
//对于上面的股票代码
var stockCode:String? = findStockCode("Facebook")
let text = "Stock Code - "
if stockCode {
let message = text + stockCode!
println(message)
}
可选绑定
除了强制解析,可选绑定
var stockCode:String? = findStockCode("Facebook")
let text = "Stock Code - "
if let tempStockCode = stockCode {
let message = text + tempStockCode
println(message)
}
代码中的
可选链
可选链用于在类包裹的多个可选类型的层次调用中,譬如上面的获取股票的函数如果放到一个类中:
class Stock {
var code: String?
var price: Double?
}
func findStockCode(company: String) -> Stock? {
if (company == "Apple") {
let aapl: Stock = Stock()
aapl.code = "AAPL"
aapl.price = 90.32
return aapl
} else if (company == "Google") {
let goog: Stock = Stock()
goog.code = "GOOG"
goog.price = 556.36
return goog
}
return nil
}
接下来,我们先用
if let stock = findStockCode("Apple") {
if let sharePrice = stock.price {
let totalCost = sharePrice * 100
println(totalCost)
}
}
函数的返回值是可选类型,我们通过可选绑定来检测是否有值,显然股票的价格也是一个可选类型,于是我们继续使用
if let x = xxx() {
if let x = xxx() {
if let x = xxx() {
if let x = xxx() {
if let x = xxx() {
if let x = xxx() {
if let x = xxx() {
if let x = xxx() {
if let x = xxx() {
if let x = xxx() {
if let x = xxx() {
if let x = xxx() {
}
}
}
}
}
}
}
}
}
}
}
}
除了使用
if let sharePrice = findStockCode("Apple")?.price {
let totalCost = sharePrice * 100
println(totalCost)
}
可选链提供了访问变量的另一种方式,代码现在看上去也更加的干净整洁。上面只是一个基础的使用,更加深入的学习可以参考官方文档。
Examples
可选类型用于Protocol 可选方法
可选类型用于未实例化控件
考虑下这一种情况,我们有一个自定义的MyViewController
类,类中有一个属性是myLabel
,var myLabel : UILabel
,因为非var myLabel: UILabel?
!
来强制拆包,比如
myLabel!.text = "text"
myLabel!.frame = CGRectMake(0, 0, 10, 10)
...
时间日期
NSDate
在
NSDate *currentUTCDate = [NSDate date]
但是在
let date = NSDate()
获取到的会是本地时间。
UTC 时间与本地化时间
import UIKit
let date = NSDate();
// "Apr 1, 2015, 8:53 AM" <-- local without seconds
var formatter = NSDateFormatter();
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ";
let defaultTimeZoneStr = formatter.stringFromDate(date);
// "2015-04-01 08:52:00 -0400" <-- same date, local, but with seconds
formatter.timeZone = NSTimeZone(abbreviation: "UTC");
let utcTimeZoneStr = formatter.stringFromDate(date);
// "2015-04-01 12:52:00 +0000" <-- same date, now in UTC
时间比较
如果需要比较两个日期,可以使用如下方法,在如下代码中已经展示了如何处理不同的返回结果:
// Date comparision to compare current date and end date.
var dateComparisionResult:NSComparisonResult = NSDate().compare(endDate)
if dateComparisionResult == NSComparisonResult.OrderedAscending
{
// Current date is smaller than end date.
}
else if dateComparisionResult == NSComparisonResult.OrderedDescending
{
// Current date is greater than end date.
}
else if dateComparisionResult == NSComparisonResult.OrderedSame
{
// Current date and end date are same.
}
TimeStamp
如果需要将某个
extension NSDate {
convenience init?(jsonDate: String) {
let prefix = "/Date("
let suffix = ")/"
let scanner = NSScanner(string: jsonDate)
// Check prefix:
if scanner.scanString(prefix, intoString: nil) {
// Read milliseconds part:
var milliseconds : Int64 = 0
if scanner.scanLongLong(&milliseconds) {
// Milliseconds to seconds:
var timeStamp = NSTimeInterval(milliseconds)/1000.0
// Read optional timezone part:
var timeZoneOffset : Int = 0
if scanner.scanInteger(&timeZoneOffset) {
let hours = timeZoneOffset / 100
let minutes = timeZoneOffset % 100
// Adjust timestamp according to timezone:
timeStamp += NSTimeInterval(3600 * hours + 60 * minutes)
}
// Check suffix:
if scanner.scanString(suffix, intoString: nil) {
// Success! Create NSDate and return.
self.init(timeIntervalSince1970: timeStamp)
return
}
}
}
// Wrong format, return nil. (The compiler requires us to
// do an initialization first.)
self.init(timeIntervalSince1970: 0)
return nil
}
}
使用的例子如下:
if let theDate = NSDate(jsonDate: "/Date(1427909016000-0800)/")
{
println(theDate)
}
else
{
println("wrong format")
}
NSDateFormatter:格式化时间
var dataString = "April 1, 2015" as String
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy"
dateFormatter.timeZone = NSTimeZone.localTimeZone()
// convert string into date
let dateValue = dateFormatter.dateFromString(dataString) as NSDate!
println(dateValue)

NSCalendar
// Playground - noun: a place where people can play
import UIKit
// Setup the calendar object
let calendar = NSCalendar.currentCalendar()
// Set up date object
let date = NSDate()
// Create an NSDate for the first and last day of the month
//let components = calendar.components(NSCalendarUnit.CalendarUnitYear |
// NSCalendarUnit.CalendarUnitMonth |
// NSCalendarUnit.WeekdayCalendarUnit |
// NSCalendarUnit.WeekCalendarUnit |
// NSCalendarUnit.CalendarUnitDay,
// fromDate: date)
// Create an NSDate for the first and last day of the month
let components = NSCalendar.currentCalendar().components(NSCalendarUnit.CalendarUnitMonth, fromDate: date)
components.month
// Getting the First and Last date of the month
components.day = 1
let firstDateOfMonth: NSDate = calendar.dateFromComponents(components)!
components.month += 1
components.day = 0
let lastDateOfMonth: NSDate = calendar.dateFromComponents(components)!
var unitFlags = NSCalendarUnit.WeekOfMonthCalendarUnit |
NSCalendarUnit.WeekdayCalendarUnit |
NSCalendarUnit.CalendarUnitDay
let firstDateComponents = calendar.components(unitFlags, fromDate: firstDateOfMonth)
let lastDateComponents = calendar.components(unitFlags, fromDate: lastDateOfMonth)
// Sun = 1, Sat = 7
let firstWeek = firstDateComponents.weekOfMonth
let lastWeek = lastDateComponents.weekOfMonth
let numOfDatesToPrepend = firstDateComponents.weekday - 1
let numOfDatesToAppend = 7 - lastDateComponents.weekday + (6 - lastDateComponents.weekOfMonth) * 7
let startDate: NSDate = calendar.dateByAddingUnit(NSCalendarUnit.CalendarUnitDay, value: -numOfDatesToPrepend, toDate: firstDateOfMonth, options: nil)!
let endDate: NSDate = calendar.dateByAddingUnit(NSCalendarUnit.CalendarUnitDay, value: numOfDatesToAppend, toDate: lastDateOfMonth, options: nil)!
Array(map(0..<42) {
calendar.dateByAddingUnit(NSCalendarUnit.CalendarUnitDay, value: $0, toDate: startDate, options: nil)!
})
"\(components.year)"
var dateString = "2014-10-3" // change to your date format
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "YYYY-MM-dd"
var someDate = dateFormatter.dateFromString(dateString)
println(someDate)
字符串
let string1 : String = "Hello"
let string2 : String = "Hel" + "lo"
if string1 == string2 {
println("The strings are equal")
}
创建增删
字符串插值
println("The current value of friendlyWelcome is \(friendlyWelcome)")
// 输出 "The current value of friendlyWelcome is Bonjour!
索引遍历
存在判断
if string1.hasPrefix("H") {
println("String begins with an H")
}
if string1.hasSuffix("llo") {
println("String ends in 'llo'")
}
类型编码
编解码
let stringToConvert = "Hello, Swift"
let data = stringToConvert.dataUsingEncoding(NSUTF8StringEncoding)
Indexed Collections
数组(Array)
var arrayOfIntegers : [Int] = [1,2,3]
// 隐式指定
var implicitArrayOfIntegers = [1,2,3]
// 也可以创建空数组,但必须提供其类型
let anotherArray = [Int]()
//使用 append 函数向数组的末尾追加对象
myArray.append(4)
//数组中的任意位置插入对象
myArray.insert(5, atIndex: 0)
元组(Tuples)
元组
let http404Error = (404, "Not Found")
// http404Error 的类型是 (Int, String),值是 (404, "Not Found")
(404, “Not Found”
你可以将一个元组的内容分解
let (statusCode, statusMessage) = http404Error
println("The status code is \(statusCode)")
// 输出 "The status code is 404"
println("The status message is \(statusMessage)")
// 输出 "The status message is Not Found"
如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线
let (justTheStatusCode, _) = http404Error
println("The status code is \(justTheStatusCode)")
// 输出 "The status code is 404"
索引遍历
你还可以通过下标来访问元组中的单个元素,下标从零开始:
println("The status code is \(http404Error.0)")
// 输出 "The status code is 404"
println("The status message is \(http404Error.1)")
// 输出 "The status message is Not Found"
你可以在定义元组的时候给单个元素命名:
let http200Status = (statusCode: 200, description: "OK")
给元组中的元素命名后,你可以通过名字来获取这些元素的值:
println("The status code is \(http200Status.statusCode)")
// 输出 "The status code is 200"
println("The status message is \(http200Status.description)")
// 输出 "The status message is OK"
作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个
序列操作
let ints = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]。
对集合中的每个值执行闭包映射
let strings = ints.map { return String($0) }
print("strings: \(strings)") // prints: strings: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
对数组中的每个值执行函数筛选
let evenInts = ints.filter { return ($0 % 2 == 0) }
print("evenInts: \(evenInts)") // prints: evenInts: [0, 2, 4, 6, 8]
let reducedInts = ints.reduce(0, combine: +)
print("reducedInts: \(reducedInts)") // prints: reducedInts: 45
// defined another way:
let reducedIntsAlt = ints.reduce(0) { (previousValue: Int, currentValue: Int) -> Int in
return previousValue + currentValue
}
print("reducedIntsAlt: \(reducedIntsAlt)") // prints: reducedIntsAlt: 45
通过
Keyed Collections
字典
字典是一种将键映射到值的类型,类似
var crew = [
"Caption": "Jean-Luc Picard",
"First officer": "William Riker",
"Second Officer": "Data"
];
crew["Captain"]
// = "Jean-Luc Picard"