美文网首页
Swift基础-06(构造函数)

Swift基础-06(构造函数)

作者: 思考的快与慢 | 来源:发表于2017-05-10 17:02 被阅读13次
    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..<Int(count) {
                
                //根据下标获取属性
                let pty = list?[i]
                
                //获取属性的名称<C语言字符串>
                //转换过程: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..<Int(count) {
                // 使用guard语法,一次判断每一项是否有值,只要有一项为nil,就不再执行后续的代码
                guard let pty = list?[i],
                    let cName = property_getName(pty),
                    let name = String(utf8String: cName)
                    else {
                        //继续遍历下一个
                        continue
                }
                print(name)
                arrM.append(name)
            }
            free(list)
            return arrM
        }
    }
    
    

    相关文章

      网友评论

          本文标题:Swift基础-06(构造函数)

          本文链接:https://www.haomeiwen.com/subject/ycvwtxtx.html