Swift 类和结构

结构的mutating 函数

结构体中,除了init 函数外,其他函数中如果有修改自身属性,必须声明为 mutating

struct s {
    var str : String = "" 
    mutating func(s1 : String) {
	self.str = s1
    }
}

类方法和 Static 方法

类方法和static方法都是通过 类名(结构名) 直接调用的方法。 但 类方法可以被子类重写,而static 不行.

class EqualClass {
    class func canOverride() {
        print("EqualClass canOverride")
    }
    static func cannotOverride() {
	print("EqualClass cannotOverride")
    }
}

class SubEqualClass : EqualClass {
    override class  func canOverride() {
        print("SubEqualClass canOverride")
    }
    //下面代码无法编译
    override static func cannotOverride() {
	print("SubEqualClass cannotOverride")
    }
}

下标脚本(subscript)

在类,结构或是枚举 声明中,可以定义下标脚本,通过一个或多个索引来访问实例里的数据。

class EqualClass {
    subscript(first : Int, second : String) -> String {
        return "Int=\(first) second=\(second)"
    }
}
let object = EqualClass()
print("subscript = "+object[1,"bb"]) //用两个索引参数来访问上面声明的下标脚本

实例构造相关

类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值。可选类型的不用。 存储属性在构造器中赋值时,它们的值是被直接设置的,不会触发任何属性观测器(也就是didSet,willSet等)。case 1 同时,属性在声明处的初始化也是不会触发属性观测器的。 Case 2

class SubCls {
    
    override init(v : String) {
        self.sss = v + "sss" //case 1
    }
    
    var sss : String? = "" { //case 2
        willSet {
            print("\(newValue)")
        }
    }
}

image

可失败构造器

在某些自定义条件不满足时,在 可失败构造器内返回nil,表示构造失败. 你可以用一个非可失败构造器覆盖一个可失败构造器,但反过来却行不通。 一个非可失败的构造器永远也不能代理调用一个可失败构造器。

Class Failure {
    	init?(v : Int)  {
		if v < 0 { return nil }
	}
	init(v1 : Int) {
    	}
}
class SubFailure : Failure {
    	override init?(v : Int) {
        	super.init(v1: v) //可失败构造器 可以调用父类的 非可失败构造器
    	}
	init(v2 : Int) { //编译错误! 非可失败构造器不能调用父类的 可失败构造器
        	super.init(v: v2)
    	}
}

关联类 associatedtype

用 associatedtype 关键字来设置关联类型实例。 声明的关联类型可以替代任何其他类型,类似泛型。

protocol Cont {
    associatedtype ItemType // 该协议声明关联类型,可以替代任何其他类型
    mutating func append(_ item: ItemType)
}
extension Int : Cont {
    func append(_ item: Int) { //关联类型 Int
        print("Int append")
    }
}
extension Double : Cont {
    func append(_ item: Double) { //关联类型Double
        print("Double append")
    }
}

类型约束 Where语句

类型约束语句可以确保 指定的类型符合 泛型或是类的定义。 可以在参数列表中定义泛型参数的约束,以保证参数类型符合你的预期。 Where 放在函数的所有参数类型最后,可以接多个约束语句,来满足自己对参数类型的需求。

样例代码(自定义一个Stack容器类,实现一个compareStack函数,对比两个容器里元素是否完全一致):

//先声明协议,声明一下 compareStack 里用到的容器类的方法
protocol Cont {
    associatedtype ItemType
    var count : Int {get}
    // 添加一个新元素到容器里
    mutating func append(_ item: ItemType)
    subscript(i : Int) -> ItemType {get}
}
//实现自定义Stack 容器类
class MyStack<Element> : Cont {
    var myItems : [Element] = []
    init(v : Element...) {
        for value in v {
            myItems.append(value)
        }
    }
    var count: Int {
        get {myItems.count}
    }
    func append(_ item: Element) {
        myItems.append(item)
    }
    subscript(i : Int) -> Element {
        return myItems[i]
    }
}
// 扩展,将 Array 当作 Cont 来使用, 因为Array本身也实现了 Cont里声明的方法
extension Array: Cont {}
//容器对比函数
func compareStack<C1 : Cont, C2 : Cont>(stack1 : C1, stack2 : C2) -> Bool //C1 和 C2 类型都必须实现Cont 协议
            where C1.ItemType == C2.ItemType, //约束1,C1的Element和C2的必须相同
                  C1.ItemType : Equatable { //约束2,C1的Element 必须实现Equatable 协议
                      guard  stack1.count == stack2.count else {
                          return false
                      }
                      for i in 0..<stack2.count {
                          if stack1[i] != stack2[i] { return false }
                      }
                      return true
}

let arrr1 = [1,2,3] //一个Array
let stack1 = MyStack.init(v: 1,2,3) //一个MyStack容器
if compareStack(stack1: arrr1, stack2: stack1) { //对比Array和MyStack容器元素是否完全一致
        print("the same!") //会进入这里
}