Swift - 类、属性、方法

一、类

Swift类是构建代码所用的一种通用且灵活的构造体。
我们可以为类定义属性(常量、变量)和方法。
与其他编程语言所不同的是,Swift并不要求你为自定义类去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类,系统会自动生成面向其他代码的外部接口。

类和结构体对比

Swift中类和结构体有很多共同点。共同处在于:
1.定义属性用于存储值。
2.定义方法用于提供功能。
3.定义附属脚本用于访问值。
4.定义构造器用于生成初始化值。
5.通过扩展以增加默认实现的功能。
6.符合协议以对某类提供能标准功能。

与结构体相比,类还有如下的附加功能:

1.继承允许一个类继承另一个类的特征。
2.类型转换允许在运行时检查和解释一个类实例的类型。
3.解构器允许一个类实例释放任何其所被分配的资源。
4.引用计数器允许对一个类的多次引用。

语法:

class className {
    Definition 1
    Definition 2
    ......
    Definition N
}

类定义

class student {
    var studentName : String
    var mark1 : Int
    var mark2 : Int
}

实例化类:

let studentRecord = student()

实例:

class MarksStruct {
     var mark : Int
     init(mark : Int) {
         self.mark = mark
    }
}

class studentMarks {
      var mark = 300
}
let marks = studentMarks()
print("成绩为\(marks.mark)")

以上程序的执行结果为:

成绩为 300

作为引用类型访问类属性

类属性可以通过,来访问。格式为:实例化类名.属性名:

class MarksStruct {
    var mark : Int
    init(mark : Int) {
        self.mark = mark
    }
}

class studentMarks {
    var mark1 = 300
    var mark2 = 400
    var mark3 = 900
}
let marks = studentMarks()
print("Mark is \(marks.mark1)")  
print("Mark is \(marks.mark2)")
print("Mark is \(marks.mark3)")

以上程序执行输出结果:

Mark1 is 300
Mark2 is 400
Mark3 is 900

恒等运算符

因为类是引用类型,有可能有多个常量和变量在后台同时引用某一个实例。
为了能够判断两个常量或者变量是否引用同一个类实例,Swift内建了两个恒等运算符。


实例

class SampleClass : EquaTable {
    let myProperty : String
    init(s : Sting) {
        myProperty = s  
    }
}

func == (lhs: SampleClass, rhs: SampleClass) -> Bool {
    return lhs.myProperty == rhs.myProperty
}

let spClass1 = SampleClass(s : "Hello") 
let spClass2 = SampleClass(s : "Hello")

if spClass1 === spClass2 {
    print("引用相同的类实例\(spClass1)")
}

if spClass1 !== spClass2 {
    print("引用不相同的类实例\(spClass2)")
}

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

引用不相同的类实例:SampleClass

二、属性

Swift属性将值与特定的类、结构或枚举关联。
属性可分为存储属性和计算属性:



存储属性和计算属性通常用于特定类型的实例。
属性也可以直接用于类型本身,这种属性称为类型属性。
另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己写的存储属性上,也可以添加到从父类继承的属性上。

存储属性

简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。
存储属性可以是变量存储属性(用关键字var定义),也可以是常量存储属性(用关键字let定义)。
1.可以在定义存储属性的时候指定默认值。
2.也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值。

struct Number {
    var digits : Int
    let pi = 3.1415
}

var n = Number(digits: 12345)
n.digits = 67
print("\(n.digits)")
print("\(n.pi)")

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

67
3.1415

考虑一下代码:

let pi = 3.1415

代码中pi定义存储属性的时候指定默认值(pi = 3.1415),所以不管你什么时候实例化结构体,它都不会改变。如果你定义的是一个常量存储属性,如果尝试修改它就会报错,如下所示:

struct Number {
    var digits : Int
    let numbers = 3.1415
}

var n = Number(digits: 12345)
n.digits = 67
print("\(n.digits)")
print("\(n.numbers)")
n.numbers = 8.7

以上程序执行将会报错,错误如下所示:

error: cannot assign to property: 'numbers' is a 'let' constant
n.numbers = 8.7

意思为‘numbers’是一个常量,你不能修改它。

延迟存储属性

延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。
在属性声明前使用lazy来标示一个延迟存储属性。
注意:
必须将延迟存储属性声明成变量(使用var关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
延迟存储属性一般用于:
1.延迟对象的创建。
2.当属性的值依赖于其他未知类

class sample {
    lazy var no = number()
}

class number {
    var name = "Apple"
}

var firstSample = sample()
print(firstSample.no.name)

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

Apple

实例化变量

如果您有过OC经验,应该知道OC为类实例存储值和引用提供两种方法。对于属性来说,也可以使用实例变量作为属性值的后端存储。
Swift编程语言中把这些理论统一用属性来实现。Swift中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。
一个类型中属性的全部信息一一包括命名、类型和内存管理特征--都在唯一一个地方(类型定义中)定义。

计算属性

除存储属性外,类、结构体和枚举可以定义计算属性,计算属性不直接存储值,而是提供一个getter来获取值,一个可选的setter来间接设置其他属性或变量。

class sample {
    var nol = 0.0, no2 = 0.0
    var length = 300.0, breadth = 150.0
    var middle: (Double, Double) {
        get {
            return (length / 2, breadth / 2)
        }
        set(axis) {
            no1 = axis.0 - (length / 2)
            no2 = axis.1 - (length / 2)
        }
    }
}

var result = sample()
print(result.middle)
result.middle = (0.0, 10.0)
print(result.no1)
print(result.no2)

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

(150.0,75.0)
-150.0
-65.0

如果计算属性的setter没有定义表示新值参数名,则可以使用默认名称newValue。

只读计算属性

只有getter没有setter的计算属性就是只读计算属性。
只读计算属性总是返回一个值,可以通过点(.)运算符访问,单但不能设置新的值。

class film {
    var head = ""
    var duration = 0.0
    var metaInfo: [String : String] {
        return [
            "head" : self.head,
            "duration" : "\(self,duration)"
        ]
    }
}
var movie = film()
movie.head = "Swift属性"
movie.duration = 3.09
print(movie.metaInfo["head"]!)
print(movie.metaInfo["duration"]!)

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

Swift属性
3.09

注意:
必须使用var关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。let关键字只用来声明常量属性,表示初始化后再也无法修改的值。

属性观察器

属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。
可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。
注意:
不需要为无法重载的计算属性添加属性观察器,因为可以通过setter直接监控和响应值的变化。
可以为属性添加如下的一个或全部观察器:
1.willSet在设置新的值之前调用。
2.didSet在新的值被设置之后立即调用。
3.willSet和didSet观察器在属性初始化过程中不会被调用。

class Samplepgm {
    var counter : Int = 0 {
        willSet(newTotal) {
            print("计数器:\(newTotal)")
        }
        didSet {
            if counter > oldValue {
                print("新增数\(counter - oldValue)")
            }
        }
    }
}
let NewCounter = Samplepgm()
NewCounter.counter = 100
NewCounter.counter = 800

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

计数器:100
新增数:100
计数器:800
新增数:700

全局变量和局部变量

计算属性和属性观察器所描述的模式也可以用于全局变量和局部变量。


类型属性

类型属性是作为类型定义的一部分写在类型最外层的花括号({})内。
使用关键字static来定义值类型的类型属性,关键字class来为类定义类型属性。

  struct StructName {
      static var storedTypeProperty = " "
      static var computedTypeProperty: Int {
          //返回一个Int值
      }
  }

  enum Enumname {
      static var storedTypeProperty = ""
      static var computedTypeProperty: Int {
          //返回一个Int值
      }
  }

 class Classname {
     class var computedTypeProperty: Int {
         //返回一个Int值
     }
 }

注意:
例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟实例计算属性的语法类似。

获取和设置类型属性的值

类似于实例的属性,类型属性的访问也是通过点运算符(.)来进行。但是,类型属性是通过类型本身来获取和设置,而不是通过实例。实例如下:

struct StudentMarks {
    static let markCount = 97
    static var totalCount = 0
    var InternalMarks: Int = 0 {
        didSet {
            if InternalMarks > StudentMarks.markCount {
                InternalMarks = StudentMarks.markCount
            }

            if InternalMarks > StudentMarks.totalCount {
                StudentMarks.totalCount = InternalMarks
            }
        }
    }
}
var studentMark1 = StudentMarks()
var studentMark2 = StudentMarks()

student1Mark1.InternalMarks = 98
print(studentMark1 > InternalMarks)

student1Mark2.InternalMarks = 87
print(studentMark2 > InternalMarks)

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

97
87

三、方法

Swift方法是与某些特定类型相关联的函数。
在OC中,类唯一能定义方法的类型。但在Swift中,你不仅能选择是否要定义一个类、结构体、枚举,还能灵活的在你穿件的类型(类、结构体、枚举)上定义方法。

实例方法

在Swift语言中,实例方法是属于某个特定类、结构体或者枚举类型实例的方法。
实例方法提供以下方法:
1.可以访问和修改实例属性。
2.提供与实例目的相关的功能。

实例方法

在Swift语言中,实例方法是属于缪戈特定类、结构体或者枚举类型实例的方法。
实例方法提供以下方法:
1.可以访问和修改实例属性。
2.提供与实例目的相关的功能。
实例方法要写在它所属的类型的前后大括号({})之间。
实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。
实例方法只能被它所属的类的某个特定实例调用。
实例方法不能脱离于现存的实例而被调用。

语法:

func funcName(Parameters) -> returnType {
    Statement1
    Statement2
    ......
    StatementN
    return parameters
}

实例:

class Counter {
    var count = 0
    func increment() {
        count += 1
    }
    func incrementBy(amount: Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}
//初始计数值是0
let counter = Counter()
//计数值现在是1
counter.increment()
//计数值现在是6
counter.incrementBy(amount : 5)
print(counter.count)
//计数值现在是0
counter.reset()
print(counter.count)

以上程序的执行结果为:

 6
 0

Counter类定义了三个实例方法:
1.increment让计数器按1递增。
2.incrementBy(amount : Int)让计数器按一个指定的整数值递增。
3.reset将计数器重置为0。
Counter这个类还声明了一个可变属性count,用它来保持对当前计数器值的追踪。

方法的局部参数名称和外部参数名称

Swift函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用Swift中的方法和OC中的方法极其相似。像在OC中一样,Swift中方法的名称通常用一个介词指向方法的第一个参数,比如:with、for、by等等)。
Swift默认仅给方法的第一个参数名称一个局部参数名称,默认同时给第二个和后续参数名称全局参数名称。
以下实例中‘no1’在Swift中声明为局部参数名称。‘no2’用于全局的声明并通过外部程序访问。

class division {
    var count: Int = 0
    func incrementBy(no1: Int, no2: Int) {
        count = no1 / no2
        print(count)
    }
}

let counter = division()
counter.incrementBy(no1: 1800, no2: 3)
counter.incrementBy(no1: 1900, no2: 2)
counter.incrementBy(no1: 11100, no2: 2)

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

600
950
5550

是否提供外部名称设置

我们强制在第一个参数添加外部名称把这个局部名称当做外部名称使用(Swift2.0前是使用#号)。
相反,我们呢也可以使用下划线(_)设置第二个及后续的参数不提供一个外部名称。

class multiplication {
    var count: Int = 0
    func incrementBy(first no1: Int, no2: Int) {
        count = no1 * no2
        print(count)
    }
}
let counter = multipication()
counter.incrementBy(first: 800, no2: 3)
counter.incrementBy(first: 100, no2: 5)
counter.incrementBy(first: 15000, no2: 3)

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

2400
500
45000

Self属性

类型的每一个实例都有一个隐含属性叫做self,self完全等同于该实例本身。
你可以在一个实例的实例方法中使用这个隐含的self属性来引用当前实例。

class calculations {
    let a : Int
    let b : Int
    let res : Int 

    init(a: Int, b: Int) {
        self.a = a
        self.b = b
        res = a+b
        print("Self 内:\(res)")
    }

    func tot(c : Int) -> Int {
        return res - c
    }

    func result() {
        print("结果为:\(tot(c : 20))")
        print("结果为:\(tot(c : 50))")
    }
}
let pri = calculations(a: 600, b: 300)
let sum = calculations(a: 1200, b: 300)
pri.result()
sum.result()

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

Self内:900
Self内:1500
结果为:880
结果为:850
结果为:1480
结果为:1450

在实例方法中修改值类型

Swift语言中结构体和枚举是值类型。一般情况下,值类型的属性不能在它的实例方法中被修改。
但是,如果你确实需要在某个具体的方法中修改结构体或者枚举的属性,你可以选择变异(mutating)这个方法,然后方法就可以从方法内部改变它的属性;并且它做的任何改变在方法结束时还会保留在原始结构中。
方法还可以给它隐含得到self属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。

struct area {
    var length = 1
    var breadth = 1
    func area() -> Int {
        return length * breadth
    }
    mutating func scaleBy(res: Int) {
        length *= res
        breadth *= res
        print(length)
        print(breadth)
    }
}
var val = area(length: 3, breadth: 5)
var.scaleBy(res: 3)
var.scaleBy(res: 30)
var.scaleBy(res: 300)

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

9
15
270
450
81000
135000

在可变方法中给self赋值

可变方法能够赋给隐含属性self一个全新的实例。

struct area {
    var length = 1
    var breadth = 1
    func area() -> Int {
        return length * breadth
    }
    mutating func scaleBy(res: Int) {
          self.length *= res
          self.breadth *= res
          print(length)
          print(breadth)
    }  
}
var val = area(length: 3, breadth: 5)
var.scaleBy(res: 13)

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

39
65

类型方法

实例方法是被类型的某个实例调用的方法,你也可以定义类型本身调用的方法,这种方法就叫做类型方法。
声明结构体和枚举的类型方法,在方法的func关键字之前加上关键字static。类可能会用关键字class来允许子类重写父类的实现方法。
类型方法和实例方法一样用点号(.)语法调用。

class Math {
    class func abs(number: Int) -> Int {
        if number < 0 {
            return (-number)
        } else {
            return number
        }
    }
}

struct absno {
    static func abs(number : Int) -> Int {
        if number< 0 {
            return (-number)
        } else {
            return number
        }
    }
}

let no = Math.abs(number: -35)
let num = absno.abs(number: -5)
print(no)
print(num)

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

35
5

下一篇:“Swift - 继承” http://www.jianshu.com/p/39b5348f1f1b

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

推荐阅读更多精彩内容