基础部分
值的声明
在Swift中对值的声明使用的是var、let. 使用var声明的值,是一个变量,值是可以被修改的,let声明的值,是一个常量,值一旦被指定就不可以被修改
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
在Swift中可以自动去推断数据的类型 无需指定,上面的两个数据类型都是一个整数。这一点在OC中是没有的
类型标注
声明数据的时候为了指定数据的类型,可以在变量名或常量名的后面加上“: ”(冒号和一个空格)然后在跟上类型名。如:
let nameExplicit: String = "Whoopie Cushion"
常量和变量的名字
常量与变量名不能包含数学符号,箭头,保留的(或者非法的)Unicode 码位,连线与制表符。也不能以数字开头,但是可以在常量与变量名的其他地方包含数字。可以用包括unicode以及其他的字符来作为变量名。
//变量名
let π = 3.14159
let 你好 = "你好啊"
let 🐶🐮 = "dogcow"
布尔值
在Swift中的bool值是true和false,而不是OC中的YES和NO
let onSaleInferred = true
let onSaleExplicit: Bool = false
打印输出
在Swift中的打印输出使用的是print,也可以兼容使用NSlog
print(10)
print("this is \(10)") //将值转化为字符串,需要将值写在括号中并在前面加上斜杠”\”
//格式化打印
print(String.init(format: "%@ %@ %.2f", "asd","dd",10.123))
print("this is \(String.init(format: "%@ %@ %.2f", "asd","dd",10.123))")
也可以使用
NSLog("this is %@ %.f", "asd","1.12")
基本运算符
除了OC中已有的运算符:赋值运算符、比较运算符、逻辑运算符、组合赋值运算符、三目运算符之外,在Swift中还多了一些运算符:
空合运算符(Nil Coalescing Operator)
空合运算符(a ?? b)将对可选类型 a 进行空判断,如果 a 包含一个值就进行解封,否则就返回一个默认值 b。表达式 a 必须是 可选类型。默认值 b 的类型必须要和 a 存储值的类型保持一致。如:
let defaultColorName = "red"
var userDefinedColorName: String? //默认值为 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
区间运算符
闭区间运算符”a...b”定义一个包含从 a 到 b(包括 a 和 b)的所有值的区间。a 的值不能超过 b。
for i in 1...5 {
print("\(i)")
}
半开区间运算符
半开区间运算符(a..<b)定义一个从 a 到 b 但不包括 b 的区间。 之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值
for i in 5..<10{
print("\(i)")
}
区间运算符和半开区间运算符主要用在for-in循环中
数据类型
Swift 包含了 C 和 Objective-C 上所有基础数据类型,Int表示整型值; Double 和 Float 表示浮点型值; Bool 是布尔型值;String 是文本型数据。 Swift 还提供了三个基本的集合类型,Array ,Set 和 Dictionary 。在使用的时候必须指定集合中的数据类型
//Array
var shoppingList: [String] = ["Eggs", "Milk"]
//Dictionary
var dic = Dictionary<NSObject,AnyObject>()
//Set
var letters = Set<Character>()
可变性
swift
如果创建一个Array、String、Set或Dictionary并且把它分配成一个变量,这个是可变的。这意味着我们可以在创建之后添加更多或移除已存在的数据项,或者改变其中的数据项。如果我们把Array、String、Set或Dictionary分配成常量,那么它就是不可变的,它的大小和内容都不能被改变。
OC
字符串、数组、字典、集合都有专门的不可变的类NSArray; NSDictionary; NSSet; NSString;以及可变类 NSMutableArray;NSMutableDictionary; NSMutableSet; NSMutableString;
可以看到swift更加简化了数据的类型,不会专门的去设置可变类和不可变类。(目前为了兼容性,在Swift的代码中还是可以使用NSArray、NSMutableArray等类)
字符串
Swift中字符串初始化
var emptyString = "" // 空字符串字面量
var anotherEmptyString = String() // 初始化方法
其它的初始化方法…
//判断是否为空
if emptyString.isEmpty {
print("Nothing to see here")
}
字符串拼接的拼接可以使用”+”来完成
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
数组
//创建数组
var shoppingList: [String] = ["Eggs", "Milk"]
shoppingList[0...1] = ["Bananas", "Apples"]
//判断数组是否为空
if shoppingList.isEmpty{
print("kong")
}else{
print("feikong")
}
//添加数据
shoppingList += ["haha"]//添加数据
//遍历
for item in shoppingList{
print(item)
}
for (index, value) in shoppingList.enumerated() {
print("Item \(String(index + 1)): \(value)")
}
包含任意类型的数组
var array2: [Any] = [Any]()
array2.append(1)
array2.append(2.0)
array2.append([3,4])
array2.append("asdasd")
array2.append(["key1":"value1","key2":"value2"])
array2.append(sunClass2)
for iterm in array2{
print("this is \(iterm)")
}
Any:可以表示任何类型,包括函数类型
AnyObject:可以表示任何类类型的实例
字典
字典不再是像OC中那样用大括号括起来字典不再是像OC中用大括号括起来{"key1":"value1","key2":"value2"},而是使用中括号 ["key1":"value1","key2":"value2"]
字典的创建
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
//字典中某个value的访问
airports["DUB"]
//增加
airports["asd"] = "asd"
//更新
airports.updateValue("Dublin Airport", forKey: "DUB")
//删除
airports.removeValue(forKey: "DUB")
类型转换
as? as!
某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,用类型转换操作符(as? 或 as!)。
条件形式as? 返回一个你试图向下转成的类型的可选值。强制形式 as! 把试图向下转型并强制解包。
当你不确定向下转型可以成功时,用类型转换的条件形式(as?)。条件形式的类型转换总是返回一个可选值,并且若下转是不可能的,可选值将是 nil。这使你能够检查向下转型是否成功。
只有你可以确定向下转型一定会成功时,才使用强制形式(as!)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。
as
向上类型转换,从一个派生类转换为基类
var num1 = 1 as Double//num1是一个值为1的浮点数变量
var num2 = 2 as Int//num2是一个值为2的整型变量
class father{
let f = 1
}
class sun: father{
let s = 2
}
var sunClass = sun()
sunClass is sun
var sunClass2 = sunClass as father
print("this is \(sunClass2.s)") //报错
print("this is \(sunClass2.f)") //不会报错,打印2
可选类型以及隐式可选类型
可选类型
在Swift中使用可选类型来处理值有可能缺失的情况,这是C语音和OC都没有的,但这有点像Objective-C 中的一个特性,一个方法要不返回一个对象要不返回nil,nil表示“缺少一个合法的对象”
被声明为可选类型的变量或者常量只可能有两种值:一、有值,等于xxx 二、没有值,即Swift中的nil
如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil,如果给它们赋值失败,它们的值还是nil:
var surveyAnswer: String?
// surveyAnswer 被自动设置为 nil
如果给可选常量或变量赋值成功,它们的值不是nil了
surveyAnswer = “haha”
强制解析
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(!)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的强制解析(forced unwrapping):
let assumedString: String? = "An implicitly unwrapped optional string."
let str1 = assumedString!
如果上面的这个可选类型常量没有值 那么就会报错
隐式可选类型
有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。这种情况下就不用去每次解析和判断可选类型了,因此Swift还提供了隐式可选类型来处理一个可选类型第一次被复制后就总会有值得情况。
在获取隐式可选类型的值的时候,不需要再后面加上”!”来强制解析
let fullName: String! = "John Appleseed"
let str2 = fullName
下面的例子展示了可选类型 String 和隐式解析可选类型 String 之间的区别:
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感叹号来获取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
注意:在使用可选类型或隐式可选类型的常量或变量之前都要将它们初始化,不然会报错
控制流
if语句
在Swift中if语句后的判断条件无需用括号括起来,判断的类型是可选类型的时候 使用if let来绑定判断
for-in循环
在Swift中的for循环是这样的
for index in 0...3 {
print("index is \(index)")
}
//index的值不需要声明, 只需要将它包含在循环的声明中,就可以对其进行隐式声明。如果不需要它的值,还可以将其用”_”来代替
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
answer *= base
}
而不是OC、C中的
for(int i = 0 ; i < 5; i++){
}
While循环
while循环从计算一个条件开始。如果条件为true,会重复运行一段语句,直到条件变为false。
下面是 while 循环的一般格式:
while condition {
statements
}
Repeat-While
while循环的另外一种形式是repeat-while,它和while的区别是在判断循环条件之前,先执行一次循环的代码块。然后重复循环直到条件为false。
Swift语言的repeat-while循环和其他语言中的do-while循环是类似的。
下面是 repeat-while循环的一般格式:
repeat {
statements
} while condition
Switch
与 C 和 Objective-C 中的switch语句不同,在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止switch语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用break语句。这使得switch语句更安全、更易用,也避免了因忘记写break语句而产生的错误。
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a":
print("The letter a")
case "A":
print("The letter A")
default:
print("Not the letter A")
}
在OC中,Swtich只支持int类型,char类型作为匹配。而在Swift中作为匹配的类型更加丰富多样,不仅仅支持 int类型和char类型,还可以是浮点型、字符串、区间、元组、枚举值等。在Swift中Switch可以匹配的类型很多,这样就可以将一些繁杂的if判断语句简化
匹配字符串
let carName = "asd"
switch carName{
case "Six","asd":
print("this is Six")
case "Seven":
print("this is Seven")
case "Eight":
print("this is Eight")
case "Night":
print("this is Night")
default:
print("nothing")
}
//打印this is Six
//匹配浮点数
let value = 5.0;
switch value {
case 1.0:
print("this is 1.0")
case 2.0:
print("this is 2.0")
case 5.0:
print("this is 5.0")
default:
print("others")
}
//打印this is 5.0
区间匹配
case 分支的模式也可以是一个值的区间。
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
var naturalCount: String
switch approximateCount {
case 0:
naturalCount = "no"
case 1..<5:
naturalCount = "a few"
case 5..<12:
naturalCount = "several"
case 12..<100:
naturalCount = "dozens of"
case 100..<1000:
naturalCount = "hundreds of"
default:
naturalCount = "many"
}
复合匹配
当多个条件可以使用同一种方法来处理时,可以将这几种可能放在同一个case后面,并且用逗号隔开。当case后面的任意一种模式匹配的时候,这条分支就会被匹配。并且,如果匹配列表过长,还可以分行书写:
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
print("\(someCharacter) is a consonant")
default:
print("\(someCharacter) is not a vowel or a consonant")
}
// 输出 "e is a vowel"
使用 Switch 语句匹配枚举值
你可以使用switch语句匹配单个枚举值:
directionToHead = .south
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
函数
对于在OC中,方法有两种类型,类方法与实例方法。方法的组成由方法名,参数,返回值组成。
在Swift中函数的定义基本与OC一样。
主要区别为:
1 通过func关键词定义函数
2 返回值在->关键词后标注
各举一个类方法与实例方法例子。
- (UIColor*)blackColor
- (void)addSubview:(UIView *)view
对应的swift版本
class func blackColor() -> UIColor //类方法, 通过 class func或者static func 关键词声明,class func声明的方法可以被子类重写
func addSubview(view: UIView) //实例方法
忽略参数标签
如果你不希望为某个参数添加一个标签,可以使用一个下划线()来代替一个明确的参数标签。
func someFunction( firstParameterName: Int, secondParameterName: Int) {
// 在函数体内,firstParameterName 和 secondParameterName 代表参数中的第一个和第二个参数值
}
someFunction(1, secondParameterName: 2)
如果一个参数有一个标签,那么在调用的时候必须使用标签来标记这个参数。
函数中参数的修改
oc中参数在函数体内是可以修改其值的,因为其作用类似于局部变量。但是在swift中,参数默认都是常量参数,不能在函数体内修改参数值。如果需要在函数体内修改参数值,则需要定义变量参数,即在参数名前加var关键字即可。
闭包
闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)比较相似。
闭包表达式语法
闭包表达式语法有如下的一般形式:
{ (parameters) -> returnType in
statements
}
在OC中我们更多的会使用block来进行完成复杂的操作,比如异步网络请求得到Json数据后,需要将Json转化为数据模型的对象、又或者是跨视图层级来完成数据处理、视图动画的执行等等。在Swift中闭包能够实现block所能实现的功能。
尾随闭包
如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签:
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函数体部分
}
//不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure(closure: {
// 闭包主体部分
})
//使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
// 闭包主体部分
}
逃逸闭包
当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 @escaping,用来指明这个闭包是允许“逃逸”出这个函数的。
一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多启动异步操作的函数接受一个闭包参数作为 异步操作完成的处理。这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用。在这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。例如:
var x:String?
var completionHandlers:() -> Void
func someFunctionWithEscapingClosure(completionHandler: @escaping (String) -> Void) {
//异步操作
print("这里可以执行异步操作")
//返回时调用
completionHandler("123456")
}
someFunctionWithEscapingClosure(completionHandler: {(str: String) -> Void
in
x = str
})
print(x)
指针
在Swift这门语言中,会尽量去避免你去直接使用指针。但是还是会提供各种与c语言相对应的指针以供开发者在需要的是时候使用。具体的可以看https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html
协议
语法:
在Objective-C中我们这么声明Protocol:
@protocol SampleProtocol
- (void)someMethod;
@end
而在Swift中:
protocol SampleProtocol
{
func someMethod()
}
在Swift遵循协议:
class AnotherClass: SomeSuperClass, SampleProtocol
{
func someMethod() {}
}
那么之前Objective-C的protocol中,我们可以标志optional。在Swift中没有optional这样的标志。
在Swift中protocol很强大,class、enum、struct、extention都可以遵守协议
让枚举遵守一个协议
enum testEnum:MyProtocol {
internal func test() {
print("this is enum")
}
case one
case two
}
let testEnum1 = testEnum.one
testEnum1.test()
//输出this is enum
结构体遵守一个协议
struct testStruct:MyProtocol{
func test() {
print("this is struct")
}
}
let testStruct1 = testStruct()
testStruct1.test()
//输出this is struct
类
在OC中定义一个类是这样的,需要在创建.h .m文件
@interface class name : superclass
@end
@implementation class
methods
@end
在Swift中定义一个类,只需要一个.swift文件,变量、方法的声明 以及实现都在同一个类中完成
class SomeClass {
//
}
属性
OC中的属性声明:
@property (strong,nonatomic) NSString *string;
Swift中的属性声明:
class Shape {
var string = " string "
}
OC中的getter setter方法
- (NSString *)string{
return @"str"
}
- (void)setString:(NSString *)string{
_string = string;
}
Swift中的setter getter方法
var string:String{
set{
}
get{
return "string"
}
}
Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。
Objective-C 为类实例存储值和引用提供两种方法。除了属性之外,还可以使用实例变量作为属性值的后端存储。
Swift 编程语言中把这些理论统一用属性来实现。
Swift中的只读属性
var redOnly:String{
get{
return "asd"
}
}//get 和括号都可以去掉
存储属性
存储属性就是存储在特定类或结构体实例里的一个常量或变量。
struct storeProperty{
var value1:Int//存储属性
let value2:Int//存储属性
}
var store = storeProperty(value1: 10,value2: 11)
print("vale1:\(store.value1) value2:\(store.value2)")
计算属性
计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter,来间接获取和设置其他属性或变量的值。
//计算属性
struct caculateProperty{
var value1:Int = 2
var caculate:Int{
set{
value1 = newValue + 1
}
get{
return value1*2
}
}
}
var test = caculateProperty.init(value1: 2)
print("value1: \(test.value1)")
test.caculate = 2
print("value1: \(test.value1)")
var value2 = test.caculate
print("value1: \(value2)")
扩展
Swift的扩展和 不同于Objective-C 中的扩展 ,但和Objective-C 中的分类类似。与Objective-C 不同的是,Swift 的扩展没有名字。
扩展语法
使用关键字 extension 来声明扩展:
extension SomeType {
// 为 SomeType 添加的新功能写到这里
}
可以通过扩展来扩展一个已有类型,使其实现一个或多个协议。在这种情况下,无论是类还是结构体,协议名字的书写方式完全一样:
extension SomeType: SomeProtocol, AnotherProctocol {
// 协议实现写到这里
}
extension ZTTestKvoAndProtocolViewController:UITextViewDelegate{
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
textStr = text
return true;
}
}
Swift中的扩展很强大,具体的可以参考Extentions
注意
扩展可以为一个类型添加新的功能,但是不能重写已有的功能。
多线程
NSThread
在OC和Swift中NSthread的方法基本一样
GCD
//let queue1 = DispatchQueue(label:"queue1")//默认创建串行队列
let queue1 = DispatchQueue(label:"queue1",attributes:.concurrent)//创建并发队列
for i in 0...10{
queue1.async {
print("this is async1 \(i)")
}
}
在OC中dispatch_once会被开发者用来实现一个单例,应为它只会执行一次,安全、简洁
但在Swift3.0中dispatch_once已经被废除。
NSOperation & NSOperationQueue
在OC中可以使用NSInvocationOperation将方法封装后提交到队列,也可以使用NSBlockOperation将代码块封装后提交到队列来完成任务的执行
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
NSBlockOperation *bloperation = [NSBlockOperation blockOperationWithBlock:^{
for(int i = 0;i< 20;i++){
NSLog(@"子线程6:%d\n",i);
}
}];
[queue addOperation:bloperation];
[queue addOperation:operation];
在Swift中已经废除了使用NSInvocationOperation,不再将方法封装后提交到队列,保留了NSBlockOperation(在swift3中对应的类是BlockOperation)将代码块封装后提交到队列。
let queue = OperationQueue.init()
let operation1 = BlockOperation.init {
for i in 0...20{
print("this is operation1 \(i)")
}
}
queue.addOperation(operation1)
开发模式
单例
OC中创建单例
+ (CCTVUserManager *)shareObject
{
static CCTVUserManager *__manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__manager = [[self alloc] init];
});
return __manager;
}
Swift中创建单例
class ZTRequest{
static let sharedInstance = ZTRequest()
private init(){}//防止其它地方调用初始化方法
}
KVO
dynamic var textStr:String!//需要在监听的值得前面加上dynamic
private var myContext = 0
self.addObserver(self, forKeyPath: "textStr", options: NSKeyValueObservingOptions.new, context: &myContext)
//MARK:响应键值监听
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if &myContext == context {
let newValue = change?[.newKey]
print("this is kvo test newValue : \(newValue)")
}else{
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
通知
//注册通知
NotificationCenter.default.addObserver(self, selector: #selector(reciveNotificaTion(noti:)), name: NSNotification.Name(rawValue: "testNotification"), object: nil)
//接收通知
@objc private func reciveNotificaTion(noti:NSNotification){
print("notifiacation : \(noti.userInfo)")
}
//MARK:析构函数,进行解除观察,通知等操作
deinit {
self.removeObserver(self, forKeyPath: "textStr")
NotificationCenter.default.removeObserver(self)
}
代理
和OC中一样,Swift中代理和协议都是紧密相连的。
@objc
protocol MyProtocol3{
func test()
@objc optional func test3()
}
class runClass{
var delegate:MyProtocol3?
func run() -> Void {
delegate?.test()
}
}
class testProtocol:MyProtocol3{
//MARK:Delegate
func test() {
print("this is delegate")
}
}
var testClass = runClass()
testClass.delegate = testProtocol()
testClass.delegate?.test()
testClass.delegate?.test3?()
在OC中我们检测某个类是否实现了协议方法是这样的
if ([_delegate respondsToSelector:@selector(numberOfImagesInAdView:)]) {
_imageCount = [_delegate numberOfImagesInAdView:self];
}
在Swift3.0中,检测某个类是否实现了协议方法是这样的
delegate?.test3?()
学习网站
The Swift Programming Language (Swift 3.0.1)
Start Developing iOS Apps (Swift)
Using Swift with Cocoa and Objective-C (Swift 3.0.1)
推荐学习的开源项目
firefox-ios
编译时的环境:cocoapods 1.1.1 Homebrew 1.1.1 xcode8.0