Swift<二>

2016-05-17  本文已影响88人  月下独酌灬

构造函数基础

构造函数是一种特殊的函数,主要用来在创建对象时初始化对象,为对象成员变量设置初始值,在 OC 中的构造函数是 initWithXXX,在 Swift 中由于支持函数重载,所有的构造函数都是 init

构造函数的作用

必选属性

class Person: NSObject {

    /// 姓名
    var name: String
    /// 年龄
    var age: Int
}

提示错误 Class 'Person' has no initializers -> 'Person' 类没有实例化器s

原因:如果一个类中定义了必选属性,必须通过构造函数为这些必选属性分配空间并且设置初始值

/// `重写`父类的构造函数
override init() {

}

提示错误 Property 'self.name' not initialized at implicitly generated super.init call -> 属性 'self.name' 没有在隐式生成的 super.init 调用前被初始化

/// `重写`父类的构造函数
override init() {
    super.init()
}

提示错误 Property 'self.name' not initialized at super.init call -> 属性 'self.name' 没有在 super.init 调用前被初始化

/// `重写`父类的构造函数
override init() {
    name = "张三"
    age = 18

    super.init()
}

小结

子类的构造函数

/// 学生类
class Student: Person {

    /// 学号
    var no: String

    override init() {
        no = "001"

        super.init()
    }
}

小结

Optional 属性

class Person: NSObject {
    /// 姓名
    var name: String?
    /// 年龄
    var age: Int?
}

重载构造函数

/// `重载`构造函数
///
/// - parameter name: 姓名
/// - parameter age:  年龄
///
/// - returns: Person 对象
init(name: String, age: Int) {
    self.name = name
    self.age = age

    super.init()
}

注意事项

调整子类的构造函数

/// `重写`父类构造函数
///
/// - parameter name: 姓名
/// - parameter age:  年龄
///
/// - returns: Student 对象
override init(name: String, age: Int) {
    no = "002"

    super.init(name: name, age: age)
}
/// `重载`构造函数
///
/// - parameter name: 姓名
/// - parameter age:  年龄
/// - parameter no:   学号
///
/// - returns: Student 对象
init(name: String, age: Int, no: String) {
    self.no = no

    super.init(name: name, age: age)
}

注意:如果是重载的构造函数,必须 super 以完成父类属性的初始化工作

重载重写

KVC 字典转模型构造函数

/// `重写`构造函数
///
/// - parameter dict: 字典
///
/// - returns: Person 对象
init(dict: [String: AnyObject]) {
    setValuesForKeysWithDictionary(dict)
}
/// 个人模型
class Person: NSObject {

    /// 姓名
    var name: String?
    /// 年龄
    var age: Int?

    /// `重写`构造函数
    ///
    /// - parameter dict: 字典
    ///
    /// - returns: Person 对象
    init(dict: [String: AnyObject]) {
        super.init()

        setValuesForKeysWithDictionary(dict)
    }
}

运行测试,仍然会报错

错误信息:this class is not key value coding-compliant for the key age. -> 这个类的键值 age 与 键值编码不兼容

/// 姓名
var name: String?
/// 年龄
var age: Int = 0

/// `重写`构造函数
///
/// - parameter dict: 字典
///
/// - returns: Person 对象
init(dict: [String: AnyObject]) {
    super.init()

    setValuesForKeysWithDictionary(dict)
}

提示:在定义类时,基本数据类型属性一定要设置初始值,否则无法正常使用 KVC 设置数值

KVC 函数调用顺序

init(dict: [String: AnyObject]) {
    super.init()

    setValuesForKeysWithDictionary(dict)
}

override func setValue(value: AnyObject?, forKey key: String) {
    print("Key \(key) \(value)")

    super.setValue(value, forKey: key)
}

// `NSObject` 默认在发现没有定义的键值时,会抛出 `NSUndefinedKeyException` 异常
override func setValue(value: AnyObject?, forUndefinedKey key: String) {
    print("UndefinedKey \(key) \(value)")
}

子类的 KVC 函数

/// 学生类
class Student: Person {

    /// 学号
    var no: String?
}

convenience 便利构造函数

/// `便利构造函数`
///
/// - parameter name: 姓名
/// - parameter age:  年龄
///
/// - returns: Person 对象,如果年龄过小或者过大,返回 nil
convenience init?(name: String, age: Int) {
    if age < 20 || age > 100 {
        return nil
    }

    self.init(dict: ["name": name, "age": age])
}

注意:在 Xcode 中,输入 self.init 时没有智能提示

/// 学生类
class Student: Person {

    /// 学号
    var no: String?

    convenience init?(name: String, age: Int, no: String) {
        self.init(name: name, age: age)

        self.no = no
    }
}

便利构造函数应用场景

构造函数小结

懒加载

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

lazy var person: Person = {
    print("懒加载")
    return Person()
}()
lazy var demoPerson: Person = Person()

只读属性

getter & setter

private var _name: String?
var name: String? {
    get {
        return _name
    }
    set {
        _name = newValue
    }
}

存储型属性 & 计算型属性

var title: String {
    get {
        return "Mr " + (name ?? "")
    }
}
var title: String {
    return "Mr " + (name ?? "")
}

计算型属性与懒加载的对比

var title2: String {
    return "Mr" + (name ?? "")
}
lazy var title: String = {
    return "Mr " + (self.name ?? "")
}()

准备工作

class ViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

创建模型加载数据

知识点

加载数据 —— extension

/// 个人信息
class Person: NSObject {
    var name: String?
    var age: Int = 0

    init(dict: [String: AnyObject]) {
        super.init()

        setValuesForKeysWithDictionary(dict)
    }
}
// MARK: - 数据处理
extension ViewController {
    /// 加载数据
    private func loadData() {

        dispatch_async(dispatch_get_global_queue(0, 0)) {

            print("后台加载数据...")

            var array = [Person]()

            // 填充数据
            for i in 0..<50 {
                let name = "张三 \(i)"
                let age = random() % 20 + 10

                array.append(Person(dict: ["name": name, "age": age]))
            }

            print(array)
        }
    }
}

小结

对象描述信息

override var description: String {
    let keys = ["name", "age"]
    return dictionaryWithValuesForKeys(keys).description
}

加载数据 —— 完成回调

/// 加载数据
private func loadData(finished: (array: [Person])->()) {

    dispatch_async(dispatch_get_global_queue(0, 0)) {

        print("后台加载数据...")

        var array = [Person]()

        // 填充数据
        for i in 0..<50 {
            let name = "张三 \(i)"
            let age = random() % 20 + 10

            array.append(Person(dict: ["name": name, "age": age]))
        }

        dispatch_async(dispatch_get_main_queue()) {
            finished(array: array)
        }
    }
}

在 iOS 开发中,闭包(block)最常用的应用场景就是异步执行完成后,通过参数传递异步执行的结果

/// 个人数据数组
private var persons: [Person]?
loadData { (array) -> () in
    print(array)
}

绑定表格

// MARK: - 表格数据源方法
extension ViewController {
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return persons?.count ?? 0
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)

        cell.textLabel?.text = persons![indexPath.row].name

        return cell
    }
}
loadData { (array) -> () in
    // 1. 记录异步结果
    self.persons = array

    // 2. 刷新表格
    self.tableView.reloadData()
}

自定义 Cell

知识点

演练

class PersonCell: UITableViewCell {

    var person: Person? {
        didSet {
            nameLabel.text = person?.name
            ageLabel.text = String(person?.age ?? 0)
        }
    }

    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var ageLabel: UILabel!
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! PersonCell

    cell.person = persons![indexPath.row]

    return cell
}
上一篇下一篇

猜你喜欢

热点阅读