Swift学习《Swift3.0从入门到出家》——原创连载

Swift3.0学习笔记(一)

2016-12-16  本文已影响365人  Shaw1211

开篇

接触Swift大概有1个多月的时间了,刚开始学习Swift的那段日子真是苦不堪言,面对着一个陌生的语言,真是不知道该从哪里着手开始学习,所以跌跌撞撞地到处碰壁,踩坑。不过经过了这一段时间自己的摸索,以及向各路大神请教,加上网上各种检索资料,到现在应该算是一脚踏入了Swift的大门了,尽管另外一只脚还在门外,但我会不断地学习的。
因为我是从Objective-C语言转过来学习Swift的,所以刚开始接触Swift的时候,总想着在Swift里怎么实现OC的那些风格习惯,比如说使用OC中的宏定义、PrefixHeader文件等等,我想,会OC的童鞋们在学习Swift时可能也会有和我一样的想法,因此,在这里我将我学习Swift 的一些经验分享给大家,可能有些内容我说的不准确,甚至是错误的,还请大家帮我指出,另外,我有个和大家一起讨论Swift3.0学习的技术交流群185826825,欢迎大家来与我们共同学习!我的本系列其它文章:
Swift3.0学习笔记(二)

写在前面

当尝试着用一个自己陌生的语言,去实现一个自己想要达到的功能,是很令人兴奋的。所以,在文章的开始,我想先用一个极其简单的demo作为Swift学习的开始,这个demo的功能是页面上有一个按钮,点击按钮跳转到另一个页面。

大家看这段代码,是不是发现和OC的风格很像呢?是不是发现自己很容易就能看懂呢?其实我想说的是,世上无难事,只怕有心人,只要你愿意花时间去学习,你就会发现其实他并不难。

基础

在Swift中,同一个工程项目不需要引入各自的类文件,比如我新建了一个工程,里面有两个ViewController,我在vc_A中希望引用vc_B的某个公开属性,这时在我的vc_A中是不需要引入vc_B的文件的。
不在本工程内创建的文件,如一些系统库,或是通过CocoaPods加入到工程的,在使用时则需要引入到工程内,引入时也区别OC,简单使用import 库名即可,例如:

import UIKit
import ReactiveSwift

在Swift中,使用let来表示常量,var来表示变量,所谓常量,即为不可改变的量,比如你声明一个UIButton对象,后面不会给这个对象赋值成别的什么按钮对象,初始化时即在内存中给这个对象开辟了一块空间,后面不会去改变这个对象的地址,因此,你可以这样来创建这个对象:

let btn = UIButton(type: .system)

改变对btn的一些属性设置,不会影响这个对象在内存中的地址变化,因此,也就不需要将btn设置成var类型,这个看你的需求而定。
像在OC中常使用的NSMutableArray,在Swift中没有类似可变数组这样的类,可以直接声明一个数组类型的变量来达到同样的效果,比如这样:

var array: Array<String>

需要说明的是,Array<String>为字符串类型的数组,关于数组后面会介绍。

大体上数据类型和OC也没有什么差别,布尔类型的值从OC的YES/NO换成了true/false,其它值得注意的就是Swift本身是类型安全的语言,因此像在OC中习惯使用的小数,比如CGFloat和Float在使用运算符进行运算时就会报类型不一致的错误了:

修改方法是需要将其中的一个进行类型转换,以保证两个进行运算的值的类型是一致的:

let a: CGFloat = 0.3
let b: Float = 0.4
let sum = Float(a) + b

值得说明的有两点,

  1. Swift的变量和常量在声明时可以不说明它的类型,编译器会通过初始化的值对该变量或常量进行类型推断。

通过这张图我们可以看出来,我在声明常量a时,并没有指定a的数据类型,而是通过给a进行初始化赋了一个值0.3,这时候编译器会根据初始化的值对常量a进行类型推断,推断出的结果是常量a是一个Double类型的常量。

  1. Swift中不需要;作为句尾结束符,因此在Swift中对于空格的使用就要注意一些,比如在赋值符=的左右两边,都必须有至少一个空格才能正常编译通过。

由于Swift可以兼容OC,因此我们仍可以继续使用NSLog输出函数来进行输出,同样,Swift也提供了自己的输出函数,Print,这个函数中不再需要占位符了,你希望输出一个变量类似这样:

let name = "Shaw"
print("Hello \(name)")

或者这样

let name = "Shaw"
print("Hello" + name)

这个可以算是Swift相较OC变化较大的内容了,这就是你在阅读Swift的代码时经常能够看到的在一个变量的后面,跟了一个?或者!,这就跟可选类型相关。
声明一个可选类型的变量,表示这个变量可以被赋值为nil,这个不同于OC,在OC里,所有的对象都可以被赋值为nil,在编译时不会报错,但是Swift不可以,如果一个对象在声明时没有声明成可选类型,那么这个对象在编译时是不允许被赋值为nil的。比如下面这样:


这样就是不允许的,这时,我们发现报的这个错误编译器可以帮我们自动修正,修正后就是这个样子了:
var x: UIImageView? = nil

可以发现,编译器只是帮我们在数据类型的后面增加了一个?,这样就可以将变量x赋值为nil了。接下来我们给这个UIImageView对象赋一张图片,像这样:

x = UIImageView.init(image: UIImage.init(named: "abc"))

接下来,我们再声明一个UIImage类型的常量y,并将变量x的image属性的值赋给y,这时候我们不允许y为nil,我们这样做:


你突然发现编译器给我们报了两个错误,先不要着急,让我们来一一看这两个错误都是什么,

  1. 第一个错误点在x的下面,说可选类型的UIImageView没有打开,你是要使用'!'或者'?'么?,这个错误发生的原因和之前我们在说Float和CGFloat那部分的问题是一样的,由于Swift是类型安全的语言,因此一旦你声明确定了一个变量或常量的类型,那么这个变量或常量无论在编译时还是运行时都只能是这个类型的,对于刚才变量x下面的那个错误点,因为只有真正的UIImageView对象,才会有image属性,而由于我们在声明变量x的类型时,将x声明成了可选类型,也就是允许x = nil,如果在访问x的image属性前,x的值是nil的话,程序运行就会崩溃,所以编译器为了避免这个直接导致崩溃的问题发生,在编译时会要求我们将可选类型的变量进行解包操作,只有解包后的变量x的数据类型才是真正的UIImageView类型,解包的方式就是在可选类型变量的后面加上?或者!即可,那么这二者的区别又是什么呢?
    ?表示尝试将这个变量或常量进行解包,如果解包后x的值是nil,那么程序将不再去访问x的image属性;!则不同,其表示强制对x进行解包,如果发现解包后的x的值是nil,则程序会崩溃,因此需要慎用!
  2. 明白了第一个错误是如何产生的,第二个问题也就迎刃而解了,同样,我们在给一个UIImage类型的常量y赋值时,编译器不允许y被赋值为nil,因此会强制要求你将x.image后面加上!的,注意,这时候后面不可用?,原因还是由于Swift是类型安全的语言,不能尝试对x.image进行解包,如果你解出来是个nil怎么办?因此编译器直接让你强制解包,解出来是nil的话就搞崩溃你。
    下面有两种对于刚才这个问题的正确写法,
let y: UIImage = x!.image!
let y: UIImage = (x?.image)!

对于这两种写法,都是正确的,只不过解包的思路有些不同,上面那种是先将x强制解包成UIImageView对象,再对他的image属性进行强制解包;下面那种是先尝试将x解包,然后访问他的image属性,最后对访问的这个属性进行强制解包。
说到这也许你会问,虽然大概明白了什么是可选类型,以及什么是解包,为什么要解包,但是你在开发时,仍然不可避免的忽略掉这些,不要捉急,机智的编译器已将替大家考虑到这个问题了,他会在你写代码的时候,悄悄的自动为你加上这些符号,如果你真的写错了的话,他还能帮你自动修正,是不是发现这时候的Xcode真的挺可爱的呢?

基本的运算符还都和OC一致,不过在使用运算符进行运算的时候需要注意类型一致,另外,在Swift3之前的版本中,支持对浮点数进行求余操作,但是Swift3不再支持了,系统提示使用一个方法进行代替:

var  a: Float = 10.5
//a = a % 3
a = a.truncatingRemainder(dividingBy: 3)

输出结果1.5
以下表格列出了在Swift3中支持的基本运算符,举例: x = 10, y = 20

运算符 运算 结果
+ x + y 30
- x - y -10
* x * y 200
/ x / y 0
% x % y 10

对于OC和Swift3之前的版本所支持的++--运算,在Swift3中只支持这样的形式:

实例 等价 结果
x += 10 x = x + 10 20
y -= 1 y = y - 1 19

Swift3中的逻辑运算符和位运算符都与OC没有什么差异,需要注意的是,像这样在OC中可以用位运算符中的逻辑或囊括的多个枚举值:

[UIView animateWithDuration:0.1 delay:0 options:
UIViewAnimationOptionAutoreverse
| UIViewAnimationOptionAllowUserInteraction  
animations:^ {
       
} completion: nil];

在Swift中写起来要麻烦一些:

UIView.animate(withDuration: 0.1, delay: 0, options: 
(UIViewAnimationOptions(rawValue: 
UIViewAnimationOptions.autoreverse.rawValue 
| UIViewAnimationOptions.allowUserInteraction.rawValue)), 
animations: {
            
}, completion: nil)

另外,在Swift3中,增加了一个符号??,该符号的用法如下:

实例 等价
let a = b ?? c let a = b != nil ? b! : c

举例说明,一个函数的功能是接收一个可选字符串类型的参数,返回一个字符串,如果传进来的是nil,就将参数重新赋值成一个既定的字符串并返回,代码如下:

func showMessage(msg: String? = nil) -> String {
      let msg = msg ?? "默认字符串"
      return msg
}

Swift3.0的区间运算符:

实例 等价 说明
0...10 0 <= x <= 10 从0到10的闭区间
0..<10 0 <= x < 10 从0到10的左闭右开区间
  1. 枚举####

你可以声明一个枚举,像这样:

enum ControlCMD {
          case up, right, down, left
}

比如我们现在有个需求,滑动手指时输出一个手指滑动的方向的英文,在Swift里我们只需要这样实现:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let leftGR = UISwipeGestureRecognizer
            .init(target: self, action: #selector(swipe(_:)))
        leftGR.direction = .left
        let rightGR = UISwipeGestureRecognizer
            .init(target: self, action: #selector(swipe(_:)))
        rightGR.direction = .right
        let upGR = UISwipeGestureRecognizer
            .init(target: self, action: #selector(swipe(_:)))
        upGR.direction = .up
        let downGR = UISwipeGestureRecognizer
            .init(target: self, action: #selector(swipe(_:)))
        downGR.direction = .down
        self.view.addGestureRecognizer(leftGR)
        self.view.addGestureRecognizer(rightGR)
        self.view.addGestureRecognizer(upGR)
        self.view.addGestureRecognizer(downGR)
    }
    
    enum ControlCMD: String {
        case up, right, down, left
    }
    
    func swipe(_ sender: UISwipeGestureRecognizer) {

        switch sender.direction {
        case UISwipeGestureRecognizerDirection.up:
            self.sendMessage(cmd: .up)
        case UISwipeGestureRecognizerDirection.down:
            self.sendMessage(cmd: .down)
        case UISwipeGestureRecognizerDirection.left:
            self.sendMessage(cmd: .left)
        case UISwipeGestureRecognizerDirection.right:
            self.sendMessage(cmd: .right)
        default: break
        }

    }
    
    func sendMessage(cmd: ControlCMD) {
        print("滑动的方向" + cmd.rawValue)
    }
}

注意,我在声明枚举时,将枚举类型指定为String类型的枚举,这样,我在发送消息时就可以使用cmd.rawValue来访问枚举值的字符串了。
Swift中的枚举还有一些更高级的用法,因为我暂时还没有用过,所以也不在此描述了,以后遇上时再补充上。

  1. 元组####

对于元组这个概念,这是OC中所没有的,我理解的元组是将多个值组合成为一个值,感觉有点像数组,但是元组中的值的类型可以是任意类型的,举个例子:

let tuple = ("abc", 1, 0.5, [UIImage()])

这就是一个元组,元组中可以有多个元素,也可以只有一个元素,当然,由于元组就是为了存储多个值的,如果只有一个值也就不需要元组,多个值的时候,我们可以像数组那样,通过使用序号来访问元组中的元素,比如这样:

上一篇下一篇

猜你喜欢

热点阅读