Swift基础-06(构造函数)

1.关于命名空间

Objective-C 没有命名空间的,在应用开发时,所有的代码和引用的静态库最终都会被编译到同一个域和二进制中。这样的后果是一旦我们有重复的类名的话,就会导致编译时的冲突和失败。为了避免这种事情的发生,Objective-C 的类型一般都会加上两到三个字母的前缀,比如 Apple 保留的 NS和 UI 前缀,各个系统框架的前缀 SK (StoreKit), CG (CoreGraphic) 等。Objective-C 社区的大部分开发者也遵守了这个约定,一般都会将自己名字缩写作为前缀,把类库命名为 AFNetworking 或者 MBProgressHUD 这样。这种做法可以解决部分问题,至少我们在直接引用不同人的库时冲突的概率大大降低了,但是前缀并不意味着不会冲突,有时候我们确实还是会遇到即使使用前缀也仍然相同的情况。另外一种情况是可能你想使用的两个不同的库,分别在它们里面引用了另一个相同的很流行的第三方库,而又没有更改名字。在你分别使用这两个库中的一个时是没有问题的,但是一旦你将这两个库同时加到你的项目中的话,这个大家共用的第三方库就会和自己发生冲突了。
在 Swift中,由于可以使用命名空间了,即使是名字相同的类型,只要是来自不同的命名空间的话,都是可以和平共处的。swift中的命名空间的使用不是一个项目,而是需要跨项目,在一个项目中,都是一个命名空间,在同一个命名空间下,所有全局变量或者函数共享,不需要import,从swift开始,官方更多的建议大家使用pod来管理第三方框架(不然拿进来一个框架,整个项目哪哪都能使)

2.构造函数 以及 override 重写
/**
 1.给自己的属性分配空间并且设置初始值
 2.调用父类的构造函数,给父类的属性分配空间设置初始值
  ONObject 没有属性,只有一个成员变量 �"isa"
 3.调用父类的构造函数,给父类的属性分配空间设置初始值
 上面两步的操作与OC中是相反的,OC中是先调用父类的,然后在为属性赋值
 4.如果重载了构造函数,并且没有实现父类的init方法,系统不再提供init()方法,
 */

class Person: NSObject {
    
    // 'Person' cannot be constructed because it has no accessible initializers
    // person类 没有初始化构造器,构造函数,可以有多个,默认是 init
    var name: String
    
    // 构造函数不需要写 fun
    // override 重写
    // 1. 父类存在相同的方法
    // 2. 子类重新编写父类方法中的实现
    override init() {
        
        // 非 option 属性,都必须在构造函数中设置初始值,从而保证对象在被实例化的时候,属性都被正确初始化,
        // 在调用父类的构造函数之前,必须保证本类的属性都已经初始化完成
        name = "呵呵呵";
        print("Person - init")

        super.init()
    }
    
    // 重载 函数名相同,但是参数和个数不同
    // 重载可以给自己的属性外部设置初始值
    // OC是没有重载的, initWithXXX
    init(name: String) {
        
        // 使用参数的name 设置给属性
        self.name = name
        
        super.init()
    }
} 

3.遍历构造器

指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。
便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入值的实例。”

类的构造器代理规则
  • 规则 1
    指定构造器必须调用其直接父类的的指定构造器。
    指定构造器

  • 规则 2
    便利构造器必须调用同类中定义的其它构造器。

  • 规则 3
    便利构造器必须最终导致一个指定构造器被调用。

  • 规则4
    convenience 的初始化方法是不能被子类重写

一个更方便记忆的方法是:
指定构造器必须总是向上代理
便利构造器必须总是横向代理

4.可失败的构造器

如果一个类、结构体或枚举类型的对象,在构造过程中有可能失败,则为其定义一个可失败构造器。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。

为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在init关关键字后面添加问号(init?)。

注意
可失败构造器的参数名和参数类型,不能与其它非可失败构造器的参数名,及其参数类型相同。

可失败构造器会创建一个类型为自身类型的可选类型的对象。你通过return nil语句来表明可失败构造器在何种情况下应该“失败”。

struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}

注意
严格来说,构造器都不支持返回值。因为构造器本身的作用,只是为了确保对象能被正确构造。因此你只是用return nil表明可失败构造器构造失败,而不要用关键字return来表明构造成功。

    let someCreature = Animal(species: "Giraffe")
        if let giraffe = someCreature {
            print("An animal was initialized with a species of \(giraffe.species)")
        }
        
        let someCreature2 = Animal(species: "")
        // someCreature 的类型是 Animal? 而不是 Animal
        print(someCreature ?? ()) // 输出结果()
        guard let giraffe = someCreature else {
            print("类型不匹配") // 输出结果 类型不匹配
            return
        }
  • 枚举类型的可失败构造器
enum TemperatureUnit {
    case Kelvin, Celsius, Fahrenheit
    init?(symbol: Character) {
        switch symbol {
        case "K":
            self = .Kelvin
        case "C":
            self = .Celsius
        case "F":
            self = .Fahrenheit
        default:
            return nil
        }
    }
}
5.必要构造器

在类的构造器前添加required修饰符表明所有该类的子类都必须实现该构造器:

class SomeClass {
    required init() {
        // 构造器的实现代码
    }
}

在子类重写父类的必要构造器时,必须在子类的构造器前也添加required修饰符,表明该构造器要求也应用于继承链后面的子类。在重写父类中必要的指定构造器时,不需要添加override修饰符:

class SomeSubclass: SomeClass {
    required init() {
        // 构造器的实现代码
    }
}

注意
如果子类继承的构造器能满足必要构造器的要求,则无须在子类中显式提供必要构造器的实现。

6.通过闭包或者函数设置属性的默认值

如果某个存储型属性的默认值需要一些定制或设置,你可以使用闭包或全局函数为其提供定制的默认值。每当某个属性所在类型的新实例被创建时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性。

这种类型的闭包或函数通常会创建一个跟属性类型相同的临时变量,然后修改它的值以满足预期的初始状态,最后返回这个临时变量,作为属性的默认值。

class SomeClass {
    let someProperty: SomeType = {
        // 在这个闭包中给 someProperty 创建一个默认值
        // someValue 必须和 SomeType 类型相同
       return someValue
    }()
}

注意闭包结尾的大括号后面接了一对空的小括号。这用来告诉 Swift 立即执行此闭包。如果你忽略了这对括号,相当于将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。”

注意
如果你使用闭包来初始化属性,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能在闭包里访问其它属性,即使这些属性有默认值。同样,你也不能使用隐式的self属性,或者调用任何实例方法。

7.使用KVC属性赋值,以及后期使用字典转模型框架,我们的模型应该如何定义?
/**
 1.定义模型属性的时候,如果是对象,通常定义成可选的
 - 在需要的时候创建,避免写构造函数 ,可以简化代码

 2.如果是基本数据类型,不能设置成可选的,必须要设置初始值,否者KVC会崩溃
 
 3.使用KVC设置数值的时候,数值不能是private的
 
 4.使用KVC之前,应该调用super.init 保证对象实例化完成
 
 */

class Person: NSObject {

    var name: String?

    // 这种写法会报错
    // 使用KVC提示无法找到 age这个KEY
    // 原因:Int是一个基本数据类型的结构体,OC中没有,OC中只有基本数据类型
//    var age: Int?
    
    var age: Int = 0
    
    // this class is not key value coding-compliant for the key title.'
    // 因为这个属性是私有的,使用KVC设置值的时候,同样无法设置,会崩溃
//    private var title: String?
    
    var title: String?
        
    init(dict: [String: Any]) {
    
        super.init()
        
        // Use of 'self' in method call 'setValuesForKeys' before super.init initializes self
        // 使用self的方法 setValuesForKeys 之前,应该调用 super.init方法
        // KVC的方法是OC的方法。在运行时给对象发送消息
        // 要求对象已经实例化完成
        setValuesForKeys(dict)
    }
    
    override func setValue(_ value: Any?, forUndefinedKey key: String) {
        // 注意这里不能调用父类的方法,不能将父类的代码完全覆盖,这样才不会崩溃
//        super.setValue(value, forUndefinedKey: key)
    }
}

       // 我们没有定义XXX这个key的属性,所以会崩溃,如果解决这个问题,在对象中,重写
        // override func setValue(_ value: Any?, forUndefinedKey key: String) 这个方法
        let stu2 = Student(dict: ["name": "洋葱", "age": 11, "title": "Boss", "no": "110", "XXX": "yyy"])
        print("\(stu2.name ?? "") \(stu2.age) \(stu2.no ?? "")")

4.使用runtime获取对象的属性,这个和OC中的使用姿势基本一致
class Student: Person {

    var no: String?
    var bithday: String?
    var address: String?
    var height: Int = 180

    class func getpropertiesList() ->[String] {
        
        var count: UInt32 = 0
        
        var arrM: [String] = []
        
        // 获取类的属性列表,返回属性列表的数组,可选项
        let list = class_copyPropertyList(self, &count)
        
        print("属性个数:\(count)")
        // 遍历数组
        for i in 0..
            //转换过程:Int8 -> Byte -> Char -> C语言字符串
            let cName = property_getName(pty!)
            
            //转换成String的字符串
            let name = String(utf8String: cName!)
            
            arrM.append(name!)
            
            print("-----\(name!)")
        }
        free(list) //释放list
        return arrM
    }

    // 使用guard let来实现
    class func propertyList() -> [String] {
        var count: UInt32 = 0
        var arrM: [String] = []

        // 获取类的属性列表,返回属性列表的数组,可选项
        let list = class_copyPropertyList(self, &count)
        print("属性个数:\(count)")
        
        for i in 0..

你可能感兴趣的:(Swift基础-06(构造函数))