类的声明和定义
- 类在定义时,可以带默认值也可以不带。
- 类的创建有无参数均可。
- 类的属性访问,采用点语法。
类的相互引用
- 类之间可以相互引用,注意循环引用的问题
// 存在循环引用的问题
class BankCard{
var user:User
}
class User{
var bankCard:BankCard
}
解决循环引用:
在实例的生命周期中,如果某些时候引用没有值,那么弱引用可以避免循环强引用。如果引用总是有值,则可以使用无主引用。
1. 弱引用,使用 weak 修饰
- 注意弱引用必须被声明为变量,表明其值能在运行时被修改。
- 弱引用不能被声明为常量。
- 弱引用不会保持所引用的实例,即使引用存在,实例也有可能被销毁。因此,ARC 会在引用的实例被销毁后自动将其赋值为nil。
- 弱引用可以没有值,你必须将每一个弱引用声明为可选类型
2. 无主引用,使用 unowned 修饰
- 可以在声明属性或者变量时,在前面加上关键字unowned
- 无主引用是非可选类型,你不需要在使用它的时候将它展开。无主引用总是可以被直接访问。
- ARC 无法在实例被销毁后将无主引用设为nil,因为非可选类型的变量不允许被赋值为nil,因为非可选类型的变量不允许被赋值为nil。
- 注意如果你试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。使用无主引用,你必须确保引用始终指向一个未销毁的实例。还需要注意的是如果你试图访问实例已经被销毁的无主引用,Swift 确保程序会直接崩溃,而不会发生无法预期的行为。所以你应当避免这样的事情发生。
类的嵌套
- 可以在一个类中声明另一个类
- 两个不同的类,可以在其内部声明相同的类
class IOS{
class Student {
var name = ""
}
}
class Android{
class Student {
var name = ""
}
}
var stu1 = IOS.Student()
var stu2 = Android.Student()
// stu1/stu2是两个不同的对象
类是引用类型
- 类是引用类型,类的赋值也是指针的赋值,指向的依旧是同一片内存空间。
class IOS{
class Student {
var name = ""
}
}
var stu1 = IOS.Student()
stu1.name = "Li"
var stu2 = stu1
stu2.name = "wang"
print(stu1.name) // wang
恒等操作符(===/!===)
- 判断两个对象是否指向同一片内存空间,返回值为布尔值
class IOS{
class Student {
var name = ""
}
}
class Android{
class Student {
var name = ""
}
}
var stu1 = IOS.Student()
var stu2 = Android.Student()
print(stu1 === stu2) // false
类的哈希
- 由于字典的key值必须可以被哈希化,如果将类作为字典的key值属性类型,且类没有继承自NSObject,即将类的对象作为字典的key值,此时必须实现hash方法。
- 如果类继承自NSObject,则不需要进行hash化,因为根类内存在hash化方法。
class IOS:NSObject{
// override class func hash() ->Int {
// return "hello".hash
// }
}
var ios1 = IOS()
var ios2 = IOS()
var dict:Dictionary<IOS,String> = [ios1:"hello",ios2:"hi"]
集合类型对象之间的赋值和拷贝
字典的赋值和拷贝
- 字典的key值或者Value值如果其类型为引用类型,则不会进行拷贝。
- 如果key/value均不是引用类型,则都进行拷贝。
数组的赋值和拷贝
- 经过测试,数组的赋值都经过了值拷贝。
属性
- 类可以定义三种属性:对象属性、计算属性和类属性
对象属性
常量属性
- 常量属性值为基本数据类型时,不可以在对象创建后更改。
- 常量属性为结构体或者枚举类型时,不可以在对象创建后更改,包括结构体或者枚举内的值。
struct Class {
var numberOfPerson = 100
}
class Student{
let classInfo = Class()
}
var stu = Student()
stu.classInfo.numberOfPerson = 200 // 不可以更改
stu.classInfo = Class() // 不可以更改
- 常量属性为引用类型时,可以更改引用类型内的属性值,而不可以更改该常量属性。
class Class {
var numberOfPerson = 100
}
class Student{
let classInfo = Class()
}
var stu = Student()
stu.classInfo.numberOfPerson = 200
print(stu.classInfo.numberOfPerson) // 200(可以更改)
stu.classInfo = Class() // 不可以更改
常量属性的赋值方法
- 结构体的常量属性需要在初始化时进行赋值
- 类的常量属性,由于没有构造方法,需要自己实现构造方法。
class Student{
let name:String
let age:Int
init(name:String,age:Int) {
self.name = name
self.age = age
}
}
var stu = Student(name: "wang", age: 28)
print(stu.name) // wang
变量属性
- 变量属性可以在任何时候进行修改
懒加载
- 懒加载又称延迟加载,只有正式使用时才使用某个属性的时候,才会进行加载。
- 懒加载关键字:lazy
class CityInfo{
init() {
sleep(10)
}
}
class Metro{
var name:String?
lazy var city = CityInfo()
}
var line1 = Metro()
line1.name = "一号线"
print(line1.name!) // 一号线
var city = line1.city
print("加载城市信息") // 输出“一号线” 10秒后输出本语句
监测属性值得变化
- 利用willSet和didSet对属性值进行观察
- willSet内存在新属性值:newValue
- didSet内存在旧属性值:oldValue
class LoginUser{
init(name:String) {
self.name = name
}
var name:String{
willSet{
print("新值:\(newValue)")
}
didSet{
print("旧值:\(oldValue)")
}
}
}
var user = LoginUser(name: "wang")
/* 使用初始化方法,不会调用属性值观察
对属性值进行修改,将会调用属性值观察
*/
user.name = "Li"
// 新值:Li
// 值:wang
运算属性
- 运算属性需要重写set和get方法;
- 如果重写set,则必须重写get;
- 如果只重写get,则该属性值只读;
- 运算属性不能用来存储数值,更多的是用来进行逻辑处理,并对其它的存储属性值进行修改。
class Square{
// 存储属性值
var width = 0
// 运算属性值
var round:Int{
get{
return width * 4
}
set{
width = newValue/4
}
}
}
var square = Square()
square.round = 40
print(square.width) // 10
类属性
- 类属性不依赖与具体的实例,是属性类的共有属性
- 命名类属性需要关键字:static
class ClassRoom{
static var maxOfNumber = 10
}
print(ClassRoom.maxOfNumber) // 10
方法
对象方法
类方法
- 类方法声明,需要在方法名前添加关键字 class
- 类方法调用,用类名直接调用
class Tool{
class func share (){
print("开始分享")
}
}
Tool.share()
- 类方法实现单利设计模式
class Tool{
var toolName = ""
struct ToolParmars {
static var tool:Tool? = nil
}
class func sharedTool() -> Tool {
if (ToolParmars.tool == nil) {
ToolParmars.tool = Tool()
}
return ToolParmars.tool!
}
}
var tool = Tool.sharedTool()
tool.toolName = "图片"
var tool2 = Tool.sharedTool()
print(tool2.toolName) // 图片
subscript下标
subscript的作用
- Swift提供的快捷访问对象属性的方法。
- subscript的使用方式与数组及字典对元素的访问方式类似。
subscript的声明
- 声明需要关键字:subscript
class TestClass{
var testDict = Dictionary<Int,String>()
subscript(index:Int)->String{
get{
return testDict[index]!
}
set{
testDict[index] = newValue
}
}
}
var test = TestClass()
test[2] = "hello"
test[12] = "world"
print(test.testDict)
// [2: "hello", 12: "world"]
subscript的使用
- 使用
test[2]
表示访问subscript(2)
,调用里面的set和get方法
subscript的使用方法的例子
// 学生类
class Student{
var name = ""
var stuId = 0
init(name:String,stuId:Int) {
self.name = name
self.stuId = stuId
}
}
// 班级类
class ClassRoom{
var className = ""
// 学生字典
var students = Dictionary<Int,Student>()
init(className:String) {
self.className = className
}
subscript(stuId:Int)->Student{
get{
return students[stuId]!
}
set {
students[stuId] = newValue
}
}
}
// 学校类
class School{
// 班级字典
var classes = Dictionary<Int,ClassRoom>()
// 根据班级、学号查找学生
subscript(classId:Int,stuId:Int) -> Student{
get{
var cr:ClassRoom? = classes[classId]
var stu:Student? = cr![stuId]
return stu!
}
set{
var cr:ClassRoom? = classes[classId]
cr![stuId] = newValue
}
}
// 根据班号查找班级
subscript(classId:Int) -> ClassRoom{
get{
return classes[classId]!
}
set{
classes[classId] = newValue
}
}
}
var phone100 = School()
// 对学校内所有的班级
for classId in 1...20 {
// 利用班级的初始化方法进行初始化
var classRoom = ClassRoom(className: "班级:\(classId)")
// 对每个班的学生进行初始化
for stuId in 1...139 {
// 利用学生的初始化方法进行初始化
var stu = Student(name: "班级:\(classId) - 学生\(stuId)", stuId: stuId)
classRoom[stuId] = stu
}
phone100[classId] = classRoom
}
print(phone100[10,130].name)
var maxNumberOfProvice = 300
var maxNumberOfCity = 40
// 城市类
class City{
var cityName:String!
var cityNumber:Int!
init(cityName:String,cityNumber:Int) {
self.cityName = cityName
self.cityNumber = cityNumber
}
}
// 省份类
class Province{
var citys = Dictionary<Int,City>()
var proviceName:String!
init(proviceName:String) {
self.proviceName = proviceName
}
subscript(cityId:Int)->City{
get{
return citys[cityId]!
}
set{
citys[cityId] = newValue
}
}
}
// 国家类
class Country{
var provinces = Dictionary<Int,Province>()
subscript(provinceId:Int,cityId:Int)->City{
get{
assert(provinceId>maxNumberOfProvice, "省份最多\(maxNumberOfProvice)个")
assert(cityId>maxNumberOfCity, "每个省份城市最多\(maxNumberOfCity)个")
var province = provinces[provinceId]!
var city = province[cityId]
return city
}
set{
var province:Province = provinces[provinceId]!
province[cityId] = newValue
}
}
subscript(provinceId:Int)->Province{
get{
assert(provinceId>maxNumberOfProvice, "省份最多\(maxNumberOfProvice)个")
return provinces[provinceId]!
}
set{
provinces[provinceId] = newValue
}
}
}
var country = Country()
for provinceId in 1...maxNumberOfProvice {
var provice = Province(proviceName: "省份:\(provinceId)")
for cityId in 1...maxNumberOfCity {
var city = City(cityName: "省份:\(provinceId) - 城市:\(cityId)", cityNumber: cityId)
provice[cityId] = city
}
country[provinceId] = provice
}
print(country[3,306].cityName)