Object-C老鸟3天速成Swift基础

前言

很庆幸有golang 这样的语言面世。语义清晰的同时概念又特别简洁,去掉了很多无谓传统编程语言繁杂又无效率的写法,作者们通过了实现表达了我们这些广大程序员敢想不敢言的心声。
我也算是在IOS领域上摸爬滚打接近十年的老人了。要说Object-C 语法思维属于反人类的绝对错不了,但只要掌握了它的消息机制就会觉得不花巧又实用。 没想到作为后继者的Swift,反人类的写法一点都不逊色于前辈,同时要说起语法卖弄,它敢称第二绝对不会有其他语言敢占这排名。

以下是我从Object-C 转型到Swift 后的初期最需要注意的功能点:

-对变量常量的定义:let 和var,可选项? 、!

  • 控制流
  • 函数和闭包
  • 结构体和类
  • 下标脚本
  • 可选链
  • 扩展
  • 泛型

Swift对变量常量的定义:let 和var,可选项? !

let代表不可变对象,即对象首地址不能发生变更,也就是说内存地址不能改变,但也可以修饰可变对象。相当于其它语言的const 或者#define 表达。与传统语言比较,Swift 不允许直接对变量、常量定义为nil,也不允许执行过程中赋值nil。

let someInt: Int = 100

someInt = 50    // 报错误 ,因为let修饰的变量不能改变指针指向

//let 可以允许定义时不赋值,第一次使用再赋值

let otherInt: Int

otherInt = 80 // 不会报错。

let nilStr: String = nil //不允许设置为nil,报错:cannot initialize specified type 'String'

var代表可变对象,即内存地址可以发生改变,可以修饰指针重新指向的变量。

var someStr: String = "Hello "

someStr = "Hello world"    // 不会报错,因为var修饰的变量指针可以重新指向

someStr = nil  //不允许设置为nil,报错:cannot initialize specified type 'String'

为了允许变量支持空值赋值,在定义变量时增加可选项: ? !

var some2Str: String? = nil;
var other2Str: String! = nil;

在Swift5 后,不论是添加! 还是添加?变量的定义都等价于

//类型已经从String 转变为Optional<String>
var some2String: Optional<String>
var other2String: Optional<String>

而变量访问的方式也相应产生变化:

print(some2String ?? "this is default value")
/*
添加?? 等价于以下写法:
if some2String == nil{
     print("this is default value")
}
else{
     print(some2String)
}
*/

print(some2String!)
/*
添加! 对变量进行访问的写法等价于:
if some2String == nil{
     throw ('Fatal error: Unexpectedly found nil while unwrapping an Optional value')
}
else{
     print(some2String)
}
*/

同时访问的方式也可以变成另一种写法:

if let defString = some2String {
    print(defString)
}
//把some2String 进行展开,把真实的类型赋值到defString

Swift的控制流

与传统语言对比,Swift 中的 if 和 switch 作为条件控制变化不大。而 作为循环体的 for-in 、 while 、 repeat-while 是需要注意的。其中for-in 循环的写法为:

let scores = [34, 21, 3, 99, 101]

//写法一
for score in scores {
      //语句执行
}

//写法二
for (key,item) in scores.enumerated(){
    //key 为当前数组枚举的索引值
   //item 为值
}

//写法三 :通过 ".." 来表示索引范围或明确声明一个初始值、条件、增量
for index in 0...scores.count{
      //语句执行
}

//写法四:通过..< 来表示索引范围到scores.count-1 后结束。
for index in 0..<scores.count {
         //语句执行
}

while 循环可以有两种写法:

/*
while condition
{
   statement(s)
}
*/
while index <100{
  print(index)
}

/*
repeat
{
   statement(s);
}while( condition );
*/

var index = 0
repeat{
    print( "index 的值为 \(index)")
    index = index + 1
}while index < 20

Swift的函数和闭包

Swift 函数语法定义:
/*
func funcname(外部形参名 内部形参名 类型, ...) -> returntype {
   Statement1
   Statement2
   ……
   Statement N
   return parameters
}
*/
//其中内部形参名非必填

//示例一:只有外部形参
func runoob(name: String, site: String) -> String {
    return name + site
}
funoob(name:"Hello",site:"world")

//示例二:外部形参 +内部形参
func runoob(name _name: String, site _site: String) -> String {
    return _name + _site
}
funoob(name:"Hello",site:"world")

//示例三:匿名外部形参
func runoob(_ _name: String, _ _site: String) -> String {
    return _name + _site
}
funoob("Hello","world")

//示例四:可变形参
func vari<N>(members: N...){
    for i in members {
        print(i)
    }
}
vari(members: 4,3,5)
vari(members: 4.5, 3.1, 5.6)
vari(members: "Hello", " ", "World")

//示例五:可通过函数内变更参数值 的地址引用 可选项:inout
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
      a = a ^ b
      b = b ^ a
      a = a ^ b
}
var x = 1
var y = 5
swapTwoInts(&x, &y)
print("x 现在的值 \(x), y 现在的值 \(y)")

//示例六:返回类型可以是一个元组
func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print("最小值为 \(bounds.min),最大值为 \(bounds.max)")
}

闭包

就是把函数该简化的省略,该匿名则匿名,去其糟粕留其精华。自己写好了就是美如画,写烂了接手方如同恶梦。
以下定义了一个接收参数并返回指定类型的闭包语法:

/*
{(parameters) -> return type in
   statements
   return parameters
}
*/

//简单的示例:
let closure = {(val1: Int, val2: Int) -> Int in 
   return val1 / val2 
}
let result = closure(100, 50)
print (result)

闭包常用在函数回调传参的写法上,以标准库sorted方法为示例来做一个闭包逐步简化的演示:

//在不使用闭包的写法上,可以使用定义传统函数的方式:
let names = ["AT", "AE", "D", "S", "BE"]

// 使用普通函数(或内嵌函数)提供排序功能,闭包函数类型需为(String, String) -> Bool。
func backwards(s1: String, s2: String) -> Bool {
    return s1 > s2
}
var reversed = names.sorted(by: backwards)
print(reversed)

//利用闭包简写后,从关键字in 后是具体的执行命令
var reversed = names.sorted(by:{(s1:String, s2: String) ->Bool in
     return s1 > s2
 })
print(reversed)

//上述写法还是复杂,再简约一点,参数名、返回结构都不要了
//$0、$1 .....$n 去表示参数的顺序
var reversed = names.sorted(by:{
     return $0 > $1
})
print(reversed);
//甚至在主函数只有一个函数形参时,可以去掉形参以及return,把代码书写到外面。 
var reversed = names.sorted(){
    $0 > $1
}
print(reversed);

//这样够了吗? 不,还可以写得更“简约”: 
var reversed = names.sorted(by:>)

Swift的结构体和类

结构体的关键词是struct ,可以拥有自己的构造函数,没有释构函数。格式如下:

/*
struct nameStruct { 
   Definition 1
   Definition 2
   ……
   Definition N
   init(parameters){
   }
}
*/

var aStruct = MarksStruct(mark: 100)
var bStruct = aStruct // aStruct 和 bStruct 是使用相同值的结构体!
bStruct.mark = 20
print(aStruct.mark) // 100
print(bStruct.mark) // 20

类(Class),相比起结构体,有以下不同点:

  • 继承允许一个类继承另一个类的特征
  • 类型转换允许在运行时检查和解释一个类实例的类型
  • 解构器允许一个类实例释放任何其所被分配的资源
  • 引用计数允许对一个类的多次引用
    语法:
/*
class classname {
   Definition 1
   Definition 2
   ……
   Definition N
}
*/
class MarksStruct {
    var mark: Int
    //构造函数
    init(mark: Int) {
        self.mark = mark
    }
   //释构函数
    deinit{
              print("MarksStruct is deinit");
        }
}

class studentMarks {
    var mark = 300
}

var p_mask: MarksStruct? = MarksStruct(mark: 500)
if let let_p_mask = p_mask {
    print("成绩为 \(let_p_mask.mark)")
}
p_mask = nil;

let a_marks = studentMarks()
print("成绩为 \(a_marks.mark)")

下标脚本

给结构体定义subscript 方法,显式声明入参(一个或多个)和返回类型后,则结构体可以达成类似someDictionary[key]方式的读写:

/*
subscript(index: Int) -> Int {
    get {
        // 用于下标脚本值的声明
    }
    set(newValue) {
        // 执行赋值操作
    }
}
*/

class daysofaweek {
    private var days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Tsday", "Friday", "saturday"]
    subscript(index: Int) -> String {
        get {
            return days[index]   // 声明下标脚本的值
        }
        set(newValue) {
            self.days[index] = newValue   // 执行赋值操作
        }
    }
}
var p = daysofaweek()

print(p[0])
print(p[1])
print(p[2])
print(p[3])

p[4] = "Thursday"
print(p[4])

以上程序执行输出结果为:

Sunday
Monday
Tuesday
Wednesday
Thursday

下标脚本允许任意数量的入参索引,并且每个入参类型也没有限制。返回值也可以是任何类型。同时也可以使用变量参数和可变参数。

struct Matrix {
    let rows: Int, columns: Int
    var print: [Double]
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        print = Array(repeating: 0.0, count: rows * columns)
    }
    subscript(row: Int, column: Int) -> Double {
        get {
            return print[(row * columns) + column]
        }
        set {
            print[(row * columns) + column] = newValue
        }
    }
}
// 创建了一个新的 2 行 2 列的Matrix实例
var mat = Matrix(rows: 2, columns: 2)

// 通过下标脚本设置值
mat[0,0] = 3.0
mat[0,1] = 6.0
mat[1,0] = 9.0
mat[1,1] = 12.0

// 通过下标脚本获取值
print("\(mat[0,0])")
print("\(mat[0,1])")
print("\(mat[1,0])")
print("\(mat[1,1])")
可选链

回顾最初变量常量所定义的 可选项(! ?),同样适用在类和结构体上。通过在属性、方法、或下标脚本的可选值后面放一个问号(?),即可定义一个可选链。

class SubTree {
    var numberOfTress = 1
}

class Root {
    var subTree: SubTree?     //定义属性为可选项
}

使用感叹号(!)访问可选链实例,强制检测属性是否存在,不存在则抛出异常。

let root = Root()

//将导致运行时错误
let numberOfTress = root.subTree!.numberOfTress;
//输出结果:fatal error: unexpectedly found nil while unwrapping an Optional value


 /* 以上语句,等价于传统写法:

  try {
      let numberOfTress = root.subTree.numberOfTress
  }catch{
        throw ('Fatal error: Unexpectedly found nil while unwrapping an Optional value')
   }
  */

使用问号(?)可选链实例,检测属性是否存在,不存在则正常返回nil值,不报异常,这处理方式非常接近Object-C 的非空检测。

// 链接可选residence?属性,如果residence存在则取回numberOfRooms的值
if let numberOfTress = root.subTree?.numberOfTress {
    print("树的 节点总量有 \(numberOfTress)。")
} else {
    print("不存在节点")
}
扩展

扩展就是向一个已有的类、结构体或枚举类型添加新功能。扩展可以对一个类型添加新的功能,但是不能重写已有的功能。
语法:

extension SomeType {
    // 加到SomeType的新功能写到这里
}

示例:

extension Int {
   var add: Int {return self + 100 }
   var sub: Int { return self - 10 }
   var mul: Int { return self * 10 }
   var div: Int { return self / 5 }
}
    
let addition = 3.add
print("加法运算后的值:\(addition)")
    
let subtraction = 120.sub
print("减法运算后的值:\(subtraction)")
    
let multiplication = 39.mul
print("乘法运算后的值:\(multiplication)")
    
let division = 55.div
print("除法运算后的值: \(division)")

let mix = 30.add + 34.sub
print("混合运算结果:\(mix)")

泛型

Swift 提供了泛型的标准模板,来让开发者写出可重用的函数和类型。
一个简单的示例:

// 定义一个交换两个变量的函数
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}
 
var numb1 = 100
var numb2 = 200
 
print("交换前数据:  \(numb1) 和 \(numb2)")
swapTwoValues(&numb1, &numb2)
print("交换后数据: \(numb1) 和 \(numb2)")
 
var str1 = "A"
var str2 = "B"
 
print("交换前数据:  \(str1) 和 \(str2)")
swapTwoValues(&str1, &str2)
print("交换后数据: \(str1) 和 \(str2)")

以结构体来实现一个栈示例:

struct Stack<Element> {
    var items = [Element]()
    mutating func push(_ item: Element) {
        items.append(item)
    }
    mutating func pop() -> Element {
        return items.removeLast()
    }
}
 
var stackOfStrings = Stack<String>()
print("字符串元素入栈: ")
stackOfStrings.push("google")
stackOfStrings.push("runoob")
print(stackOfStrings.items);
 
let deletetos = stackOfStrings.pop()
print("出栈元素: " + deletetos)
 
var stackOfInts = Stack<Int>()
print("整数元素入栈: ")
stackOfInts.push(1)
stackOfInts.push(2)
print(stackOfInts.items);

入门学习的资料参考:
Swift教程| 菜鸟教程
Swift 5 从零到精通IOS开发训练营

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容