阅读此文章前,您已经有一定的Object-C语法基础了!)

2014年,Apple推出了Swift,最近开始应用到实际的项目中。

首先我发现在编写Swift代码的时候,经常会遇到Xcode不能提示,卡顿,直接闪退等问题,尤其是在Swift和OC混编时。(不知道其他开发者是否也有这样的经历,但是我相信这样的问题,很快会得到解决)

然后感觉Swift并不像网上很多朋友说的那样简单。有很多细节问题是值得注意的,甚至有很多思路是颠覆了传统的开发语言的!又有很多特性是结合了其他编程语言的优点!

Swift,我个人觉得是趋势,是目前最前卫的开发语言,结合了众多开发语言的优点!
网上已经有很多Swift相关的论文和博客了,这里我不做推荐和转载了!我归纳一下类和结构体,以及相关的知识吧!

Swift中,类和结构体都是对数据和方法进行封装的常用做法!首先我们来看看他们的共同之处:

  1. 都可以有属性和方法;

  2. 都有构造器;

  3. 都支持附属脚本;

  4. 都支持扩展;

  5. 都支持协议。

然后我们来看看他们的不同之处:

  1. 类有继承;

  2. 结构体有一个自动生成的逐一初始化构造器;

  3. 在做赋值操作时,结构体总是被拷贝(Array有特殊处理);

  4. 结构体可以声明静态的属性和方法;

  5. 从设计模式的角度来分析,类的设计更侧重于对功能的封装,而结构体的设计更侧重于对数据的封装。(汽车与书架来区分类与结构体)

一、构造过程

1. 默认值

在OC 类和结构的成员属性会隐式的给出默认值,对象的默认值是nil,基本数据类型的默认值是0。但是在 Swift 中属性必须显示的设置默认值,Swift会在调用构造器之前调用默认值构造器对所有在设置了默认值的属性进行初始化。

当一个构造过程完成的时候,被构造的类或者结构体的实例的存储属性都会是有值的,否则编译错误!

    class A : SuperClass {
        var _a = 1      // 默认值 = Int(1)
        var _b: Int?    // 默认值 = nil
        var _c: Int     // 无默认值,必须在构造器中赋值
        var _d: Int!    // 默认值 = nil
        init() {
            _c = 1      // 在调用父类的构造器之前,必须给_c赋值,否则编译错误
            super.init()
        }
        ...
    }

2. 构造器

类似与OC的 -(instance)init 方法。和OC最大的区别是 OC 初始化完成后返回的是这个对象的指针,而Swift初始化完成后返回的是这个对象的引用。

根据构造器的实际构造过程可将构造器分为 便利构造器指定构造器,但只有指定构造器才是真实的构造器,便利构造器只是为指定构造器添加一个便利的参数传入,或者增加一些附加操作。

以下几点在开发的时候需要注意:

  1. 类和结构体都需要在构造器完成对所有存储属性的初始化;

  2. 子类在调用父类的构造器之前必须确保本类声明的所有的存储属性都已经有值了;

  3. 便利构造器必须直接或者间接的调用本类的指定构造器。

class A : SomeClass {
     var _a: Int
     var _b = 1_000
     // 指定构造器
     init() {
         self._a = 5     // 如果是这样声明的 'var _a: Int = 5' or 'var _a = 5',那么init()构造器可以不写而直接调用
         super.init()
     }
     // 指定构造器
     init(a: Int) {      
         self._a = a     // 因为 _a 属性没有默认值,所以在调用 super.init() 前必须给 _a 赋值,否则编译错误!
         super.init() 
     }
     // 便利构造器
     convince init(a: Int, b: Int) {
         self.init(a: a + b)         // 直接调用指定构造器
     }
     // 便利构造器
     convince init(a: Int, b: Int, c: Int) {
         self.init(a: a, b: b + c)   // 间接调用指定构造器
     }   
     ...
 }

3. 析构器

和OC的 dealloc 很像,这里不多做解释了。

class A {
    ...
    deinit {    
        //...  析构器内部处理
    }
    ...
}

二、属性

OC中的类有属性,也有实例变量。但Swift中将类的实例变量和属性统一用属性来实现。

1. 存储属性

简单来说就是一个成员变量/常量。Swift可以为存储属性增加属性监视器来响应属性值被设置时的变化。

class SomeClass {
    let _a = 100        // 常量
    var _b: Int         // 没有默认值的变量
    var _c = 200        // 默认值=200的Int变量
    var _d: Int = 200 { // 默认值=200的Int变量,如果增加了属性监视器,必须显示的声明变量类型
        didSet {
            println("A 的 _c 属性 didSet = old:\(oldValue) -> \(self._c)")   // oldValue 表示曾经的值
        }
        willSet {
            println("A 的 _c 属性 willSet = \(self._c) -> new:\(newValue)")  // newValue 表示将会被设置的值
        }
    }
}

2. 计算属性

计算属性并不存储任何数据,他只是提供 set/get 的便利接口。

class A {
    class var a: Int {  // 这是类的计算属性 (区别于下面类的实例的计算属性)
        return 1001
    }
    private(set) var _b = 100   // 外部可以访问,但不能修改
    private var _a = 100        // 私有变量,外部不能访问
    var a: Int {                // 只读计算属性
        return _a
    }
    var b: Int {                // 可读可写的计算属性
        get {
            retrun _a
        }
        set {
            _a = newValue       // newValue 表示的是输入的值
        }
    }
}

3. 延迟存储属性


信大家都听说过延迟加载(懒加载),就是为了避免一些无谓的性能开销,在真正需要该数据的时候,才真正执行数据加载操作。 Swift可以使用关键字
lazy 来声明延迟存储属性,延迟存储属性在默认值构造器中不会被初始化,而是在第一次使用前进行初始化!
虽然没被初始化,但是编译器会认为他是有值的。

全局的常量或者变量都是延迟加载的, 包括结构体的静态属性也是延迟加载的。

let some = A()
class A {
    lazy var view = UIView(frame: CGRectZero) // 定义了一个延迟存储属性      ...
}

4. 静态属性

结构体可以使用关键字 static 来声明静态存储属性。(枚举也可以有) Swift的类不支持静态属性,也不支持静态临时变量。 这可以作为Swift中声明单例的一种实现方:

class Tools {
    class func sharedInstance() -> Tools {
        struct Static {
            static let sharedInstance = QNTools()
        }
        return Static.sharedInstance
    }
}

三、方法

Swift的类和结构体都可以定义自己的方法!(OC的结构体是没有方法的)

1. 参数

一个方法的参数有局部参数名称和外部参数名称,一样时写一个即可! Swift的方法的参数非常灵活,可以是值,可以是引用,可以是闭包,可以是元组... Swift的方法的返回值跟参数一样灵活

这里给出一个简单的加法方法来展示方法的参数:

class A {
    // eg. 一个简单的相加方法
    // 完整版
    class func sum1(let a/*外部参数名称*/ aValue/*内部参数名称*/: Int, let b/*外部参数名称*/ bValue/*内部参数名称*/: Int) -> Int {
        return aValue + bValue
    }
    // 当函数参数的外部和内部参数相同的时候,可以只写一个,  此时第一个参数的名称在调用时是隐藏的
    class func sum2(a: Int, b: Int) -> Int {
        return a + b
    }
    // 使用 _ 符号,可以在调用的时候隐藏函数参数名称,第一个参数默认就是隐藏的
    class func sum3(a: Int, _ b: Int) -> Int {
        // 内嵌函数的参数名称,都是可以隐藏的。而不用使用 _ 符号声明
        func sum4(a: Int, b: Int) -> Int {
            return a + b
        }
        return sum4(a, b)
    }
    // 可使用 let/var 关键字来声明参数是作为常量还是变量被传入,(如果是常量,可以不用显示的写 let)
    class func sum4(let a: Int, _ b: Int) -> Int {
        // 内嵌函数的参数名称,都是可以隐藏的。而不用使用 _ 符号声明
        func sum4(a: Int, b: Int) -> Int {
            return a + b
        }
        return sum4(a, b)
    }
    // 可使用 let/var 关键字来声明参数是作为常量还是变量被传入,(如果是常量,可以不用显示的写 let)
    class func sum5(let a: Int, let _ b: Int) -> Int {
        // 内嵌函数的参数名称,都是可以隐藏的。而不用使用 _ 符号声明
        return a + b
    }
    class func sum6(var a: Int, var _ b: Int) -> Int {
        // 内嵌函数的参数名称,都是可以隐藏的。而不用使用 _ 符号声明
        a++
        b++
        return a + b
    }
    // 可使用 inout 关键字,传引用
    class func add(inout value: Int) {
        value++
    }
}
A.sum1(a: 1, b: 2)          // result: 3
A.sum2(1, b: 2)             // result: 3
A.sum3(1, 2)                // result: 3
var aTemp: Int = 1001       // aTemp = 1001
A.add(&aTemp)
aTemp                       // aTemp = 1002
A.sum5(1, 2)               // result: 3
A.sum6(1, 2)               // result: 5

2. 实例方法

类或者结构体的实例所拥有的方法!

class A {
    private(set) var a: Int = 100
    func reset() -> Void {
        self.a = 100
    }
}
struct S {
    var a: Int = 100
    mutating func reset() -> Void { // 注意: 在结构体和枚举的实例方法中如果需要修改属性,则需要增加 mutating 字段
        self.a = 100
    }
}

3. 类方法

Swift 中类的本身也是一个实例。他没有存储属性、但拥有计算属性和方法!

参考 “1.参数” 中的示例

4. 静态方法

结构体可以使用关键字 static 来声明静态方法。

struct S {
    static func name() -> String {
        return "Liuyu"
    }
}

四、附属脚本

Swift 提供了附属脚本的语法来简化类似查询的行为。如访问一个数组的第n个元素,array[n], 访问一个字典的值 dictionary[“key”],这些都可以通过附属脚本来实现。 这里给出一个通过字符串类型的索引来查询数组中的元素的例子。

// 扩展一个通过字符串类型的位置来访问数据的附属脚本
extension Array {
    subscript(index: String) -> T? {
        if let iIndex = index.toInt() {
            return self[iIndex]
        }
        return nil
    }
}
let array = ["Liu0", "Liu1", "Liu2"]
array[1]    // result : Liu1
array["1"]! // result : Liu1

五、继承

和OC一样,Swift类可以通过继承来获取父类的属性和方法(有限制)。 Swift的类的在继承上增加了很多安全措施

class SomeSuperClass {
    func someFunc1() {
        ...
    }
    // 定义不能被子类重写的方法,需要加上 final 关键字
    final func someFunc2() {
        ...
    } 
}
class SomeClass : SomeSuperClass {
    // 重载父类的方法,需要加上 override 关键字
    override func someFunc1() {
        ...
        super.someFunc1()
    }
    // 不能被子类重写的方法
    override func someFunc2() {     // Error
        ...
        super.someFunc2()
    }
}

六、扩展

扩展就是向一个已有的类、结构体或枚举类型添加新功能。 这包括在没有权限获取原始源代码的情况下扩展类型的能力(即逆向建模)。 扩展和OC中的类别(categories)类似,但又有很多细微的区别:

  1. 扩展没有名字,一旦扩展就会应用到整个工程(模块)

  2. 在扩展中如果要重载现有的方法,需加上override关键字 (不建议修改现有的方法)

  3. 可定义新的嵌套类型

这里给出一个数学项目中计算距离时的一个扩展

typealias Distance = Double
extension Distance {
    var km: Double { return self/1_000.0 }
    var m : Double { return self }
    var cm: Double { return self*100.0 }
    var mm: Double { return self*1_000.0 }
}
let distance = Distance(1000)   // 1000m的距离
distance.km     // result : 1.0     (km)
distance.m      // result : 1000.0  (m)
distance.cm     // result : 100.0   (cm)

站在OC的基础上快速理解Swift的类与结构体的更多相关文章

  1. Go语言基础之8--面向对象编程1之结构体(struct)

    一.结构体详解 1.1 声明和定义 1.Go中面向对象是通过struct来实现的, struct是用户自定义的类型 2.Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数 ...

  2. C#基础回顾(二)—页面值传递、重载与重写、类与结构体、装箱与拆箱

    一.前言 -孤独的路上有梦想作伴,乘风破浪- 二.页面值传递 (1)C#各页面之间可以进行数据的交换和传递,页面之间可根据获取的数据,进行各自的操作(跳转.计算等操作).为了实现多种方式的数据传递,C ...

  3. C#基础知识系列三(类和结构体、String和StringBuilder、equals和==)

    前言 这一节主要来了解一下类和结构体之间的异同点.以及针对String和StringBuilder的用法.equals和==,其实可以看出很多地方都用到了上一节的值类型和引用类型.堆栈和装箱拆箱操作吧 ...

  4. c语言学习之基础知识点介绍(十二):结构体的介绍

    一.结构体的介绍 /* 语法: struct 结构体名{ 成员列表; }; 切记切记有分号! 说明:成员列表就是指你要保存哪些类型的数据. 注意:上面的语法只是定义一个新的类型,而这个类型叫做结构体类 ...

  5. JDBC基础:JDBC快速入门,JDBC工具类,SQL注入攻击,JDBC管理事务

    JDBC基础 重难点梳理 一.JDBC快速入门 1.jdbc的概念 JDBC(Java DataBase Connectivity:java数据库连接)是一种用于执行SQL语句的Java API,可以 ...

  6. 站在巨人的肩膀上---重新自定义 android- ExpandableListView 收缩类,实现列表的可收缩扩展

    距离上次更新博客,时隔略长,诸事繁琐,赶在去广州答辩之前,分享下安卓 android 中的一个 列表收缩 类---ExpandableListView 先上效果图: 如果想直接看实现此页面的代码请下滑 ...

  7. [易学易懂系列|rustlang语言|零基础|快速入门|(11)|Structs结构体]

    [易学易懂系列|rustlang语言|零基础|快速入门|(11)] 有意思的基础知识 Structs 我们今天来看看数据结构:structs. 简单来说,structs,就是用来封装相关数据的一种数据 ...

  8. Swift面向对象基础(上)——Swift中的类和结构体(下)

    学习来自<极客学院> import Foundation class User { var name:String var age:Int init(name:String,age:Int ...

  9. Swift面向对象基础(上)——Swift中的类和结构体(上)

    学习来自<极客学院> import Foundation //1.定义类和结构体 /* [修饰符]calss 类名{ 零到多个构造器 零到多个属性 零到多个方法 零到多个下标 } 修饰符可 ...

随机推荐

  1. 如何判断是否安装了VC RUNTIME

    先得说下GUID,它是Globally Unique Identifier的简称,中文翻译为“全球唯一标示符”,在Windows系统中也称之为Class ID,缩写为CLSID.对于不同的应用程序,文 ...

  2. WPF Multi-Touch 开发:基础触屏操作(Raw Touch)

    原文 WPF Multi-Touch 开发:基础触屏操作(Raw Touch) 多点触控(Multi-Touch)就是通过与触屏设备的接触达到人与应用程序交互的操作过程.例如,生活中经常使用的触屏手机 ...

  3. CSS实现强制换行-------Day 78

    事实上最早的时候也考虑过这个问题,当时还在想须要判定文字的长度么,实在是傻到极点了,原来CSS中本来就有这个样式设置的.而今天正好看到了有这么一篇介绍.细致看了下,感觉还不错,这里也把实验的结果记录下 ...

  4. MySql连接问题

    今天想通过命令连接到另外一台主机的Mysql 命令: mysql -h ip -u username -p EnterPassWord: password 连接成功

  5. HDU 1251 统计难题 (字符串-Trie树)

    统计难题 Problem Description Ignatius近期遇到一个难题,老师交给他非常多单词(仅仅有小写字母组成,不会有反复的单词出现),如今老师要他统计出以某个字符串为前缀的单词数量(单 ...

  6. Python Challenge 第四题

    这一题没有显示提示语,仅仅有一幅图片,图片也看不出什么名堂,于是直接查看源代码,源代码例如以下: <html> <head> <title>follow the c ...

  7. URAL 1180. Stone Game (博弈 + 规律)

    1180. Stone Game Time limit: 1.0 second Memory limit: 64 MB Two Nikifors play a funny game. There is ...

  8. SQL SERVER 2008- 字符串函数

    /* 1,ASCII返回字符表达式中最左侧字符的ASCII代码值 仅返回首字母的ASCII码值 parameter char或varchar returns integer */ SELECT ASC ...

  9. MSSQL - 存储过程Return返回值

    1.存储过程中不使用外部参数. 存储过程: SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ========================== ...

  10. Mac 安装配置启动Tomcat

    Tomcat Mac 下的安装: TomCat 下载地址,例如: http://tomcat.apache.org/download-70.cgi 在Mac 上下载的时候,下载tar.gz包 下载完成 ...