Swift入门

苹果为什么要推出Swift

  • 避免专利诉讼
  • 吸引更多开发者
  • 让应用开发更简单 、 高效 、 稳定

Swift 特色

  • 苹果宣称 Swift 的特点是:快速、现代、安全、语法简单
  • 可以使用现有的 Cocoa 和 Cocoa Touch 框架
  • Swift 取消了 Objective-C 的指针及其他不安全访问的使用
  • Swift 和 C 、 OC 可以混合使用

Swift 发展

  • 2014 年 6 月 苹果在发布 Xcode 6.0 的同时发布了 Swift 1.0
  • 2015 年 2 月 推出了 Swift 1.2 测试版
  • 2015 年 6 月 苹果发布了 Xcode 7.0 和 Swift 2.0 测试版,并且 宣称在年底开源,对 iOS、OS X 和 Linux 都适用
  • 从发布至今,苹果的每一个举措都彰显其大力推广 Swift 的决心

现状

  • 目前App Store 有超过 15,000 个 app 是用 Swift 来开发的
  • 目前国内有些公司的新项目已经直接采用 Swift 开发
  • 目前很多公司都在做 Swift 的人才储备

学习资源


Playground

  • Playground 是 Xcode 6 推出的新功能
  • 使用 Playground 的目的是为了方便:
  • 学习 Swift 代码
  • 验 Swift 代码
  • 试 Swift 代码
  • 并且能够可视化地看到运行结果,不需要将程序运行到模拟器或真机上
  • 另外,使用 Playground只需要一个文件,而不需要创建一个完整的工程

快速体验

//: 输出hello world
print("hello world")

//: 下面来体验一下swift和OC的区别

//: 创建一个UIView
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

//: 设置背景色
view.backgroundColor = UIColor.redColor()

//: 创建一个UIButton
let button = UIButton(type: UIButtonType.ContactAdd)

//: 设置按钮中心位置
button.center = CGPoint(x: 50, y: 50)

//: button添加到view里面
view.addSubview(button)
//: 在swift中访问属性和调用方法都是通过.来进行的
  • 小结
  1. 语句末尾不用使用;
  2. 在 Swift 中使用print()替代 OC 中的NSLog
  3. 在swift中访问属性和调用方法都是通过点语法 . 来进行的
  4. 在 Swift 中要实例化一个对象可以使用 类名()的格式,与 OC 中的alloc/init等价
  5. OC 中的[[类名 alloc] initWithXXX], [类名 类名WithXXX]在 Swift 中通常可以使用类名(XXX: )找到对应的函数
  6. OC 中的 [UIColor redColor] 类方法,在 Swift 中通常可以使用 类名.XXX( ) 找到对应的函数

变量和常量

  • let 定义常量,一经赋值不允许再修改
  • var 定义变量,赋值之后仍然可以修改
/*
    OC中:
       定义变量: int a = 1;
       定义常量: int const a = 1;

    swift:
      定义常量:   let 常量名 = 值
      定义变量:   var 变量名 = 值

    var 修饰的是可变的
    let 修饰的是不可变的

*/

// 定义变量
var a = 10

// 修改变量
a = 20

print("a = \(a)")

// 定义常量
let b = 10

print("b =  \(b)")

//b = 15

// 常量初始化后不能再修改

类型推导和类型装换

自动推导

  • swift 对数据类型要求异常严格
  • swift能够根据右边的代码,推导出变量的准确类型
  • 如果要指定变量,可以在变量名后使用 : 类型
  • Int类型分为好几种,Int8, Int16, Int32, Int64,因为类型严格,Int8和Int16不能进行运算,所以建议以后开发中都使用Int

重要技巧:Option + Click 可以查看变量的类型

Option + Click 可以查看变量的类型

如果要对不同类型的数据进行计算,必须要显式的转换

//: 自动推导
let a = 10

let b = 7.5

// binary operator '+' cannot be applied to operands of type 'Int' and 'Double'
// let c = a + b

// 将a转成Double类型
let c = Double(a) + b

// 将b转成Int类型
let d = a + Int(b)

// 定义一个float类型的常量, 定义时指定类型
let e: Float = 7.5

// 定义时指定类型 左右类型不匹配: 'Double' is not convertible to 'Float'
// let k: Float = c

// 先声明,后定义
var k: Int
// 在没有初始化之前不能使用, variable 'k' used before being initialized
//print(k)
k = 1

字符串 String

  • 是一个结构体,性能高于NSString
  • String 支持直接遍历
  • String 目前具有了绝大多数 NSString 的功能
  • String和NSString转换方便

定义字符串

  • OC定义字符串
  OC的字符串:
        NSString *str = @"hello";
      格式化字符串: [NSString stringWithFormat:@"%.02f", 3.14159]

  swift中定义字符串:
      var 变量名 = "hello"

  • swift定义字符串
//: 定义字符串
var str = "我要飞的更高"
  • 遍历字符串中的字符
// swift 2.0 中遍历字符
for c in str.characters
 {
  print(c)
}
  • 字符串长度
// 返回实际字符的个数
let len2 = str.characters.count

  • 字符串拼接

  • 2个字符串直接相加

  • 直接在 "" 中使用 (变量名) 的方式可以快速拼接字符串

// 字符串拼接
let hello = "Hello"
let world = "World"
let z = "个赞"
let i = 32
let helloworld = hello + " " + world

//: "\()" 把其他类型转成字符串
let title = "\(i) " + z
  • 格式化字符串
  • 在实际开发中,如果需要指定字符串格式,可以使用 String(format:...) 的方式
//: 格式化字符串,保留2位小数
let pi = 3.141592653
let fmtString = String(format: "%.02f", arguments: [pi])

eg. 网络请求返回一个地址,要判断是否是gif图片


let addr1 = "wwww.baidu.com/aa/bb/aa.Gif"
  • 方法1: 统一转成小写
let addr4 = addr1.lowercaseString
  • 判断地址是否是以gif结尾
addr4.hasSuffix("gif")
  • 方法2: 截取字符串最后3位,判断是否等于gif

  • 1.将String 转成NSString

let subString = (addr4 as NSString).substringWithRange(NSMakeRange(addr4.characters.count - 3, 3))
  • 2.对比字符串是否相等
subString == "gif"

Optional可选

  • 定义变量时,在类型后面添加一个 ?,表示该变量是可选类型
  • 可选类型, 表示该变量可能有值,也可能是 nil ,默认值是 nil
  • 在变量后添加一个!,可以强行解包

注意:强行解包必须要确保解包后的值不是nil,否则会报错

/*:
    定义一个电话号码? int num = 10086; int num = 0;经常会忽略对没有电话号码的判断

    swift推出可选类型(Optional), 表示一个变量或常量可能有值,也有可能没有值.时刻提醒我们变量可能没有值
        可选类型的定义: 类型的后面加?
*/

// 定义可选类型
let num: Int? = 10086

// Optional(10086)
print(num)

// value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
// !表示强制拆包. 需要确定可选一定有值.
let sum = num! + 10

// 定一个可选,没有赋值
var num2: Int?

// 可选没有值用nil表示
print(num2)

// 可选没有值,进行强制拆包: fatal error: unexpectedly found nil while unwrapping an Optional value
//let sum3 = num2!

// 在对可选操作的时候需要判断是否有值.
if num2 != nil {
    let sum3 = num2! + 10
    print("sum3: \(sum3)")
} else {
    print("可选没有值")
}

// 可选绑定, 当可选有值的时候,会把num2!赋值给 num3
if let num3 = num2 {
    print(num3)
} else {    // 如果num2没有值.
    print("num2没有值")
}

if条件分支

  • 在swift中没有非0既真、0为假,swift中Bool类型只有2个值:true真,false
  • 条件必须是Bool类型,或返回Bool类型的表达式
  • 条件可以省略括号
  • { }不能省略
/*
    在C和OC中非0既真,0为假
        int a = 1;
        if (a)
         {
            NSLog(@"");
         }

    1. 在swift中没有非0既真、0为假,swift中Bool类型只有2个值:true 真,false 假
    2. 条件必须是Bool类型,或返回Bool类型的表达式
    3. 条件可以省略括号
    4. { }不能省略
*/
var a = 1
//: type 'Int' does not conform to protocol 'BooleanType'
//if (a) 
//{
//    print(a)
//}

let iosNB = true

if iosNB 
{
    print("ios 牛x")
}

var pass = "及格"
var fail = "不及格"

let score = 59

//: 运算符结果返回Bool
if score > 60 
{
    print(pass)
} 
else 
{
    print(fail)
}

//: 注意问号前一定要有空格, 条件也只能是Bool类型
var c = (score > 60) ? pass : fail
print(c)


循环

  • OC for循环
//OC的for:
for (int i = 0; i < 5; i++) 
{
    NSLog(i)
}
  • swift for循环常规写法
for var i = 0; i < 5; i++ 
{
    print(i)
}
  • swift 常用写法
// swift 常用写法
for i in 0...5
{
    print(i)
}
  • 范围运算符
// 范围运算符
// ... 闭合范围运算符 表示 a 到 b, [a, b] 0-5, 包含a,也包含b
// ..< 半闭合范围运算符 表示 a 到 b, [a, b) 0-4, 包含a,但是不包含b
  • 省略下标

  • _ 表示忽略对应位置的值

for _ in 0...5
{
       print("hello")
}

switch

判断学生分数

/*:
    判断分数:
        90-99: 优秀
        80-89: 良好
        70-79: 中等
        60-69: 及格
        60以下: 不及格
*/

let score = 99

// swift的 swith执行完一个case后默认不会执行下一个case
switch (score / 10) 
{
    case 9 :
        print("优秀")
    case 8 :
        print("良")
    case 7 :
        print("中")
    case 6 :
        print("及格")
    default:
        print("不及格")
}  

如果要执行下一个case需要用fallthrough关键字

switch (score / 10) 
{
    case 9 :
        print("优秀")
    case 8:
        print("走我了吗?")
        fallthrough
    case 7:
        print("良")
    case 6 :
        print("及格")
    default:
        print("不及格")
}

switch通过区间运算符匹配

//: switch通过区间运算符匹配
switch (score) 
{
    case 90...100 :
        print("优秀")
    case 70..<90 :
        print("良好")
    case 60..<70 :
        print("及格")
    default:
        print("不及格")
}

匹配字符串

/*
Monday 星期一
Tuesday 星期二
Wednesday 星期三
Thursday 星期四
Friday 星期五
Saturday 星期六
Sunday 星期日
*/

//: 可以匹配字符串
let weekDay = "Tuesday"
switch weekDay 
{
    case "Monday":
        print("星期一,该上班了")
    case "Friday":
        print("星期五,明天不上班")
    default:
        print("忙的忘记星期几了")
}
  • 小结
  • switch 可以对Float,Bool, String, Int, 枚举 进行判断
  • 不再需要break
  • 要处理所有可能的情况,不然编译器直接报错,不处理的条件可以放在 default分支中

数组

  • OC 中定义数组
/*:
  OC中定义数组:
      NSArray *arr = @[元素1, 元素1, 元素1];

  在swift中定义数组:
      var 数组名 = [元素1, 元素2, ...]
*/
  • swift定义数组
//: swift定义数组
var array = ["bmw", "benz", "byd"]
var array1 = [3, 5, 7, 3, 2] // 类型是[Int],表示Int类型的数组,数组里面的元素都是Int类型
var array2 = ["liudehua", 54]  // 类型是[NSObject],数组里面的元素都是NSObject类型,用的很少
  • 初始化空数组
var array3: [Int] = []
var array4 = [Int]()
定义数组时指定数组类型
var array5: [Int] = [5, 6]
  • 遍历数组中的所有元素(传统写法)
//: 遍历数组中的所有元素
for var i = 0; i < array1.count; i++ {
  let n = array1[i]    //取出数组中的某个元素
  print(n)
}
  • 遍历数组中的所有元素(swift写法)
//: 遍历数组中的所有元素
for n in array1 {
  print("i = \(n)")
}
  • 单独取出数组中的某个值
print(array[0])
print(array[1])

数组的常用操作

//: 取钱
var persons = ["liudehua", "zhangxueyou", "guofucheng"]

//: 添加元素到数组末尾
persons.append("liming")

//: 向数组中指定位置插入一个元素
persons.insert("chenglong", atIndex: 0)

//: 修改数组中元素的值
persons[0] = "fangzuming"
persons

//: 获取数组个数
persons.count

//: 删除数组最后位置的元素
persons.removeLast()
persons

//: 删除数组中指定位置的元素
persons.removeAtIndex(2)
persons

//: 删除数组中的所有元素
persons.removeAll()

// 判断数组是否为空
persons.isEmpty

/*
    let修饰的数组是不可变数组或者说常量数组
    var修饰的数组是可变数组
*/
let arr7 = [1, 2, 3]
//arr7[0] = 1
//arr7.removeAll()
//arr7.append(1)

// 数组合并
var arr8 = [1]
var arr9 = [2, 3, 5]

arr9 += arr8
print(arr9)

//: 数组不能越界访问,Array index out of range
//arr9[20]

元组


  • 元组是由2个或以上元素组成的复合类型,而且元组中每个元素的数据类型都可以不同

  • 元组定义好后不能添加或减少元素

  • var 修饰的元组可以修改元素的内容

  • let 修饰的元组不可以修改元素的内容

  • 定义一个元组

//: 刘德华花5万美元买了一辆白色的宝马轿车
//: 定义元组类型为 (String, Int, String, String)
let car = ("liudehua", 50000, "white", "bmw")
  • 元组访问
//:访问元组中的元素
car.0
car.1
  • 定义元组时,指定元素名称
//: 定义元组时,指定元素名称
var car2 = (name: "liudehua", price: 50000, brand: "bmw", color: "white")
  • 元组通过元素名称访问
//: 元组通过元素名称访问
car2.price
car2.brand
  • 修改元组中元素的值
car2.brand = "byd"
  • 元组分解(值绑定)
//: 元组的分解(值绑定)
let name1 = car1.name
let price1 = car1.price
let color1 = car1.color
let brand1 = car1.brand
//
let (name2, price2, color2, brand2) = car1
name2
brand2
//
let (name3, price3, color3, _) = car1
name3

字典


  • 定义
  • 同样使用 [] 定义字典
  • let 不可变字典
  • var 可变字典
/*
    OC定义字典:
        NSDictionary *dict = @{key1: value1, key2: value2, ...};

    swift定义字典:
        var 数组名 = [key1: value1, key2: value2, ...]

    let 不可变
    var 可变
*/

//: 定义字典
var dict1 = ["name": "liudehua", "age": 54]    //[String : NSObject]
var dict2 = ["name": "liudehua", "nickName": "huazai"]   //: [String : String]
  • 定义空字典
var dict4 = [NSString: Int]()
var dict5: [NSString: Int] = [:]
  • 字典的遍历
//: 遍历字典的所有key
for n in dict1.keys {
  print(n)
//    print(dict1[n])
}
//: 遍历字典所有value
for k in dict1.values {
  print("k === \(k)")
}
//: 遍历字典
for n in dict6 {
//    print(n)
//    print(n.0)
//    print(n.1)
}
//: 开发中用的最多的遍历字典的方式
//: k, v 可以随便写,前面是 key,后面是 value
for (k, v) in dict1 {
  print(k + "--- \(v)")
}
//: 跳过不关心的值
for (_, v) in dict1 {
  print("对key不关心--- \(v)")
}

字典常用操作

  • 赋值直接使用 dict[key] = value 格式
  • 如果 key 不存在,会设置新值
  • 如果 key 存在,会覆盖现有值
var dict7 = ["name": "liudehua", "age": 53]
//: 访问字典中的元素
dict7["name"]

//: 当字典的key存在时,修改字典中的元素
dict7["age"] = 54
dict7

//: 当字典的key不存在时,会新增key和value
dict7["title"] = "king"
dict7

//: 删除字典中指定的key和对应的value
var dict8 = ["name": "liudehua", "age": 54, "height": 1.74]
dict8.removeValueForKey("age")
dict8

//: let修饰的字典不能改变 cannot assign through subscript: 'dict9' is a 'let' constant
let dict9 = ["name": "liudehua", "age": 54, "height": 1.74]
//dict9["name"] = "lisi"

枚举


  • OC定义和使用枚举
/*:
  OC 枚举:
      enum Season {
          Spring,
          Summer,
          Autumn,
          Winter
      };

      //: 定义枚举变量
      enum Season season = Spring;
*/
  • swift定义枚举类型
//: 定义枚举类型,枚举的每个成员前面加case
enum Season {
  case Spring
  case Summer
  case Autumn
  case Winter
}
  • 定义一个枚举变量
//: 定义一个枚举变量
var season = Season.Spring
//: 再次修改值的时候可以直接 .成员, 是因为类型推导.
season = Season.Summer
season = .Summer
print(season)

switch判断枚举类型

//: switch判断枚举类型
switch season {
case Season.Spring:
    print("春天")
case Season.Summer:
    print("夏天")
case Season.Autumn:
    print("秋天")
case Season.Winter:
    print("冬天")
}

//: 可以省略枚举名
var season2 = Season.Spring
switch season2 {
case .Spring:
    print("春天")
case .Summer:
    print("夏天")
case .Autumn:
    print("秋天")
case .Winter:
    print("冬天")
}

枚举成员类型

//: 定义枚举,成员类型为Int
enum Direction: Int {
    case North = 0
    case South = 1
    case East = 2
    case West = 3
}

//var direction = Direction.North

//: 使用初始值来定义枚举变量
var direction = Direction(rawValue: 1)
print(direction)

//: 可选绑定
if let dir = direction {
    switch dir {
    case .North:
        print("北")
    case .South:
        print("南")
    case .East:
        print("东")
    case .West:
        print("西")
    }
}

函数


  • 函数的定义
func 函数名(形参名1: 形参类型1, 形参名2: 形参类型2, ...) `->` 返回值 {
  // 代码实现
}
/*函数如果没有返回值:
      1. 省略
      2. -> Void
      3. -> ()

  外部参数名,作用能够方便调用人员更好地理解函数的语义
  带外部参数名的参数列表格式:
      (外部参数名1 形式参数名1: 参数类型1, 外部参数名2 形式参数名2: 参数类型2, ...)*/
  • 函数定义和调用
//: 定义函数
func sum(a: Int, b: Int) -> Int {
  return a + b
}
//: 调用函数, b表示外部参数名
sum(10, b: 20)
//: 没有返回值
func sayHello() -> () {
  print("hello")
}
sayHello()
  • 外部参数名

  • 在形参名前再增加一个外部参数名,能够方便调用人员更好地理解函数的语义

  • swift2.0默认帮我们生成除第一个参数外的所有外部参数名

//: 为什么有外部参数名呢?
func addStudent(name: String, age: Int, number: Int) {
    print("name = \(name), age = \(age), number = \(number)")
}

//: 如果没有外部参数名,我们很难判断每个参数的作用
//addStudent("liudehua", 54, 53)

//: 有外部参数名,每个参数的作用一目了然,swift2.0默认帮我们生成除第一个参数外的所有外部参数名
addStudent("liudehua", age: 54, number: 53)

//: 指定外部参数名
func addStudent2(stu_name name: String, stu_age age: Int, stu_number number: Int) {
    print("name = \(name), age = \(age), number = \(number)")
}

addStudent2(stu_name: "liudehua", stu_age: 54, stu_number: 53)
  • 函数返回元组类型
/*:
在c语言中要返回多个值:
  1.传参数时传入指针,在函数内部修改该指针指向的值
  2.返回一个结构体
  3.返回数组
*/
//: 在swift中可以通过返回元组来方便的返回多个值
func getStudent() -> (String, Int, Int) {
  return ("liudehua", 54, 53)
}
//: student是一个(String, Int, Int)的元组
let student = getStudent()
//: 可以通过.0 .1 来访问
student.0
student.1
//: 函数通过元组返回多个值,并且为元组中的元素取名成,方便调用
func getStudent2() -> (name: String, age: Int, num: Int) {
  return ("liudehua", 54, 53)
}
let student2 = getStudent2()
student2.name
student2.age

闭包


  • 闭包类似于 OC 中的 Block,是一段预先定义好的代码,在需要时执行

定义

  • 体验UIView动画闭包
//: () -> Void 表示一个 `不需要参数,返回值为空` 的闭包类型
UIView.animateWithDuration(0) { () -> Void in
//
}
  • 闭包表达式格式:
  { (形参名称1: 形参类型1, 形参名称2: 形参类型2, ...) -> 返回值 `in`
          //要执行的代码
  }
  • in 用于区分闭包定义和代码实现
  • 定义闭包
// 定义闭包
var closure = { (text: String) -> Void in
}
  • 调用闭包
// 调用闭包
closure("学习闭包")

闭包使用场景(回调)

  • 模拟耗时操作
  // 闭包的使用场景: 回调
  // 在子线程执行完任务后通知调用者
  func loadData(finished: (result: String) -> Void) {
      dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
          print("拼命加载数据: \(NSThread.currentThread())")

          dispatch_async(dispatch_get_main_queue(), { () -> Void in
              print(": \(NSThread.currentThread())")
              // 通知调用者
              finished(result: "获取到20条数据")
          })
      }
  }
  • 调用
loadData { (result) -> Void in
  print("网络请求完成: \(result)")
}

闭包的简写

// 当闭包的参数和返回都为空时可以省略 () -> Void in
UIView.animateWithDuration(1) {
}

// 闭包是最后一个参数,可以把闭包放在()后面,尾随闭包
UIView.animateWithDuration(1) { () -> Void in
}

类的定义


  • OC中的类
在OC中一个类包涵.h和.m两个文件
  .h:
      @interface 类名: 父类名
      @end

  .m:
      @implement 类名
      @end

  实例化类:
      类名 *变量名 = [[类名 alloc] init];
  • swift 定义类格式

  • ()里面是空,表示调用类的默认构造函数 = [[类名 alloc] init];

  • swift中访问属性通过点语法来访问, 方法也是通过点语法来调用

  • 继承和OC一样,通过 : 来继承, swift也只有单继承

class 类名: 父类名 {
      //: 一些属性
      //: 一些方法
  }

  实例化一个类:
  var 变量名 = 类名()

注意:
  1swift的继承和oc一样是通过:, swift也只有单继承
  2.访问类的属性和调用类的方法都是通过.来进行的
  3.覆盖父类方法, 需要加关键字 override

  • 定义Person类:
//: 定义一个类, 没有继承任何类的类,称之为 基类,超类
class Person: NSObject {
  // 名称属性
  var name = "liudehua"
  // 年龄
  var age = 54
  // 身高
  var height = 1.78
  // 体重
  var weight = 70.0
  // 方法
  func sleep() {
      print("累了,睡觉")
  }
  func eat() {
      print("饿了,吃饭")
  }
}
  • 实例化Person
// 实例化类Person
var p = Person()
// 访问属性
print(p.name)
// 修改属性
p.name = "zhangsan"
print("名称修改后:\(p.name)")
// 调用方法
p.sleep()
  • 定义Student继承Person
//: 继承 和OC 一样通过 : 来实现继承
class Student: Person {
  // 班级属性
  var grade = "ios05期"

  // 定义学习方法
  func study() {
      print("开开心心学习,高薪就业")
  }

  // 覆盖父类方法, 需要加关键字 override
  override func sleep() {
      print("学习累了,睡觉")
  }
}
  • 实例化Student
// 实例化Student
let s = Student()
print(s.grade)
// 调用Student的sleep
s.sleep()

类的属性


  • oc中的属性:
  • @property(noatomic, copy) NSString *name;

1.在.h声明gettersetter方法
2.在.m实现gettersetter 方法
3.在.m生成 _成员变量

  • 存数型属性: 存储数据
  • 属性监视器: 当存储型属性发生改变的时候,通知我们属性发生了改变

willSet: 在属性将要发生改变时调用
didSet: 在属性已经发生改变时调用

  • 计算型属性: 不存储任何数据,通过get方法来返回一个计算好的数据通过set方法来设置一个存储型属性的值,当只提供get方法时,称之为只读计算属性.必须要有get方法

  • 计算型属性 相当于 OC 中的@property生成的gettersetter方法,只不过setter方法没有给 _成员变量 赋值

  @interface Person ()

  @property (nonatomic, assign) CGFloat heightCM;

  @end

  @implementation Person

  // getter
  - (CGFloat)heightCM {
      return ;
  }

  // setter
  - (void)setHeightCM:(CGFloat)heightCM {
      // 没有 _heightCM = heightCM
  }
  • 类属性: 不管创建多少个实例,都共享这个属性

  • 定义:在类属性前面加class关键字,类属性只能是计算型属性

  • 访问方法:类名.属性名

  • `存储型属性、计算型属性

class Person {
  //: 存储型属性
  var name = "liudehua"
//
  //: 存储型属性,单位是m
  var height = 1.74
//
  //: 计算型属性
  var heightCM: Double {
      get {
          return height * 100
      }

      set {
          height = newValue / 100
      }
  }
//
  //: 存储型属性
  var age = 54
//
  //: 存储型属性
  var weight: Float = 70 {
      willSet {
          print("weight willSet")
      }

      didSet {
          print("weight didSet")
      }
  }
//
  //: 定义一个sleep方法
  func sleep() {
      print("person 累了, sleep...")
  }
//
  //: 定义一个eat方法
  func eat() {
      print("person 饿了, eat...")
  }
}
// 实例化Person
var person = Person()
person.height
person.heightCM = 178
person.height
person.weight = 72
var person2 = Person()
//: person的weight属性和person2的weight没有关系,相互独立的.
person2.weight = 60
print(person2.weight)

类属性

//: 定义圆
class Circle {
  //: 只要是圆就有圆周率,圆周率的值固定不变,不管创建多少个实例,都共享这个圆周率属性
  //: 类型属性: 只读计算型属性
  class var pi: Double {
      return 3.141592653
  }
//
  var radius = 20.0
//
  //: 周长,只读计算型属性
  var perimeter: Double {
      return 2 * Circle.pi * radius
  }
}
//
//Circle.pi = 1
//
var pingpong = Circle()
pingpong.perimeter
//
pingpong.radius = 40
pingpong.perimeter

构造函数


自定义Car对象

class Person {
    var name: String
}
  • 以上代码会报:“存储性属性没有初始化值”,原因是在swift中类实例化后,所有的存储型属性必须有值

  • 解决方法1:定义属性时赋值

class Person {
  // 直接赋值
  var name: String = "liudehua"
}
// 实例化
var p1 = Person()
p1.name
// 实例化
var p2 = Person()
p2.name
//: 实例化出来对象 name属性都一样,显然不合理.
  • 解决方法2:将对象属性类型设置为Optional
class Person {
  var name: String?
}
var p = Person()
p.name = "liudehua"
print(p.name)
//: 用可选的,打印出来都带Optional
  • 输出结果:Optional("liudehua")
  • 有没有什么办法,在类的实例化过程中,给存储型属性设置指定的值?,实例化后直接拿来用.或者在类实例化时指定存储型属性的值

利用 init 函数为属性初始化

`

  • 在swift中对象是通过构造函数来实例化的.构造函数的作用:在对象实例化过程中给所有的存储型属性设置初始值,并且执行必要的准备和初始化任务.
class Person: NSObject {
   var name: String

    var age: Int

    //: 重写父类构造函数
    override init() {
        print("init")
        name = "liudehua"
        age = 22
    }
}

重载构造函数

  • swift 中支持函数重载,同样的函数名,不一样的参数类型
//: 重载构造函数
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

子类构造函数


  • 自定义子类时,需要在构造函数中,首先为本类定义的属性设置初始值
  • 再调用父类的构造函数,初始化父类中定义的属性
  • 如果子类没有去实现构造函数,会继承父类的构造函数
  • 如果子类实现了构造函数,不会继承父类的构造函数
class Stuent: Person {
    var grade: String

    // 子类构造函数需要调用父类构造函数
    // 需要先初始化子类属性,在调用父类构造函数
    init(name: String, age: Int, grade: String) {
        self.grade = grade
        super.init(name: name, age: age)
    }
}

convenience 构造函数

便利构造函数: 它是辅助性的构造函数.方便创建对象

  • 默认情况下,所有的构造函数都是指定构造函数Designated
  • convenience 关键字修饰的构造方法就是便利构造函数
  • 便捷构造函数可以返回nil
  • 需要调用本类的一个指定构造函数
    /// 方便创建ios05期学生
    convenience init?(stuName: String, stuAge: Int) {

        // 判断age是否合法
        if stuAge < 0 || stuAge > 130 {
            print("年龄不合法")
            return nil
        }

        self.init(name: stuName, age: stuAge, grade: "ios05期")
    }

构造函数小结

  1. 不需要func关键字.名称是固定的,都叫 init
  2. 当类没有实现构造函数时,系统会添加一个默认的构造函数.
  3. 如果实现了构造函数,系统就不会添加默认构造函数
  4. 如果子类没有实现构造函数.会继承父类的构造函数
  5. 子类构造函数需要调用父类构造函数
  6. 需要先初始化子类属性,在调用父类构造函数
  7. 子类一旦实现了构造函数.就不会继承父类的构造函数

懒加载

在 iOS 开发中,懒加载是无处不在的

  • 方法1
lazy var p = Person()
  • 方法2
// 方法2, 需要指定p的类型
lazy var p: Person = {
  let person = Person()
  return person
}()
  • 测试调用
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
  p.name = "lisi"
  print("p.name: \(p.name)")
}
  • Person
class Person: NSObject {
  var name: String?

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

推荐阅读更多精彩内容