//类和结构:它们都有属性和添加方法,为结构体和类扩展
//相同点:
//(1、定义属性都是用于存储值的
//(2、定义方法用于提供功能
//(3、定义下标操作用于访问实例包含的值
//(4、定义构造器用于生成初始化值
//(5、通过宽展增加默认的实现功能
//(6、通过实现协议提供某种标准
//类和结构体的不同点:
//(1、允许一个类继承另一个类(继承)
//(2、在类型转换中允许在运行时检查和解释一个类实例的类型(运行时)
//(3、析构器允许一个类实例释放任何所被分配的资源
//(4、引用计数允许对一个类的多次引用

//1、类和结构体的语法:类class + 类名{定义类},结构体struct + 结构体名{定义定义结构体}
//(1)、类
class PersonClass{//定义类名首字母大写
    //这个里定义类
var age = 0
var gender:String?
var name:String?
var stander = PointStucture()
}

//(2)、结构体
struct PointStucture{//定义结构体名首字母大写
    //这个里定义结构体
var heigth = 0
var fit = 10
}

//2、类和结构体实例
//(1)、类的实例
let newPerson = PersonClass()
//(2)、结构体的实例
let newPoint = PointStucture()

//3、属性访问:实例名后紧跟属性名,使用.链接
//(1)、类
print(newPerson.stander.fit)
//(2)、结构体
print(newPoint.heigth)

//4、结构体类型的成员逐一构造器
let point = PointStucture(heigth:10,fit:34)
//和类的区别,类的实例没有逐一构造器
//5、结构体和枚举是值类型:值类型被被赋予一个常量或变量被传递给一个函数,值会被拷贝。
//所有的基本类型:整型(Integer)、浮点型(floating-point),布尔值(Bool),字符串(String),数组(array),字典(dictionary)这些都是值类型,这些的底层实现都是结构体形式实现的。

//6、结构体和枚举类型是值类型:他们的实例和实例所包含的所有类型的属性,在代码传递的过程中都会被复制
let valuePoint = PointStucture(heigth:10,fit:34)
var newValuePoint = valuePoint//newValuePoint是valuePoint的一个副本
print(valuePoint.heigth)
print(valuePoint.fit)

print(newValuePoint.heigth)
print(newValuePoint.fit)

//虽然副本的值和原来相同,实际上他们是不同的实例
newValuePoint.heigth = 100
print(newValuePoint.heigth)

print(valuePoint.heigth)//之前的实例不受副本值的影响


//枚举值类型同样遵循的标准:
enum
case
}
var currentDuration = duration.East
let newDuration = currentDuration

currentDuration = .North
print(newDuration)

//7、类是引用类型:和值类型不同,引用类型被被赋予一个常量或变量被传递给一个函数,它的值不会被拷贝。
let person = PersonClass()
person.stander = valuePoint
person.name = "huangjingzheng"
person.age = 15
person.gender = "男"

let NewPerson = person
NewPerson.age = 18
//person 和 NewPerson的时机类型是PersonClass的实例
print(person.age)

//8、等价于和不等价于:===   !==
if person === NewPerson {
    print("是同一个实例")
}else{
    print("不是同一个实例")
}

//===:表示两个类型的常量或者变量引用同一个类实例
//==(等于):表示两个实例的值相等或者相同


//9、指针:不需要使用*表明你在创建一个引用

//10、类和结构体的使用场景:
//(1)、使用结构体的情况:
//a、如果想用来封装少量或者简单的数据值
//b、结构体的特性决定:在做值传递的时候,封装的数据会被拷贝,而不是被引用
//c、数据结构中存储的是值类型的属性,应该被copy,并且不被引用
//d、给数据不需要被去继承另一个已经存在的类型的属性或者行为(类的特性)


//11、字符串,数组,字典类型的赋值和复制行为:底层以结构体的方式实现,值类型,在复制给常量或变量的时候,或者传入函数或方法,只是会被拷贝的。
//在OC中NSString,NSArray和NSDictionary,他们以类的方式实现,不是结构体,在复制和值传递的时候不会被拷贝,传递的是实例的引用


//属性:
//1、存储属性:
struct
var firstValue:Int
let length:Int
}
var newRange = RangeStructure(firstValue:0,length:4)//范围就是:0,1,2,3
print(newRange.firstValue)
//这个结构表示的范围就是0-3
//属性定义成常量和变量的区别:属性定义为常量,之后无法修改它的值

let letNewRange = RangeStructure(firstValue:0,length:5)
//更改结构体中变量的属性:
//letNewRange.length = 10  是会编译报错的
//letNewRange.firstValue = 6  是会编译报错的
//总结:因为letNewRange实例被定义成了let,不管结构体内的属性是var还是let,都无法修改  原因:结构体属于值类型,如果值类型的实例被指定成了常量,那么它的属性也是常量
//如果是类:属于引用类型,把一个引用类型的实例复制给常量,仍然可以改变实例的属性


//2、延迟存储属性:
class
//    获取数据的类
var fileName = "data.json"
}

class
//    管理数据
lazy var touTiaoData = GetData()
var data = [String]()
}

let manager = DataManger()
manager.data.append("somedata")
manager.data.append("moreData")
//touTiaoData这时候没有被创建

print(manager.touTiaoData.fileName)//如果属性没有初始化的时候就被多个线程访问,就没法保证属性只被初始化一次

//3、存储属性和实例变量:
//OC中存储值有两种方式:(1)、属性 (2)、实例变量作为后端存储
//swift属性没有对应的实例变量,属性的后端存储无法直接访问/ 优点:避免不同场景的访问方式,属性的定义简化成了一种语句

//4、计算属性
struct
var x = 0.0,y = 0.0
}

struct
var width = 0.0,height = 0.0
}

struct
var origin = Point()
var size = Size()
var center:Point
get{
let centerX = origin.x + size.width / 2
let centerY = origin.y + size.height / 2
return Point(x:centerX,y:centerY)
        }
set(newCenter){
origin.x = newCenter.x - size.width / 2
origin.y = newCenter.y - size.height / 2
        }
    }
}

//(1)、set的简化
struct
var origin = Point()
var size = Size()
var center:Point
get{
let centerX = origin.x + size.width / 2
let centerY = origin.y + size.height / 2
return Point(x:centerX,y:centerY)
        }
set{
origin.x = newValue.x - size.width / 2
origin.y = newValue.y - size.height / 2
        }
    }
}


var square = Rect(origin: Point(x:0.0,y:0.0),size:Size(width:7.0,height:7.0))

let squareCenter = square.center
print(squareCenter)

square.center = Point(x:4.5,y:4.5)
print(square.origin)
print(square.size)

//(2)、只读计算属性:只有get没有set,只能返回一个值,不能设置新值


//注意:定义计算属性必须使用var关键字,包括只读属性,因为他们的值不固定,let属性初始化后无法修改值

//(3)、只读属性声明可以去掉get关键字:
struct
var width = 0.0,height = 0.0
var mianji:Double{
return width * height
    }
}

let newMianji = MianJi(width:12,height:34)
print(newMianji.mianji)


//(4)、属性观察器:监控和响应属性值的变化
class
var totalSteps:Int = 0
willSet(newTotalSteps){
print(newTotalSteps)
        }
didSet{
if totalSteps
print(totalSteps
            }
        }
    }
}
//注意:父类属性在子类的构造器中被赋值的时候,父类中的willSet和didSet会被调用,之后才会调用我们子类的观察器。在父类初始化方法没有调用之前,自类属性赋值时,观察器不会调用。

var countValue = StepConunter()
countValue.totalSteps = 100
countValue.totalSteps = 200

//(5)、全局变量和局部变量:计算属性和属性观察器所描述的功能也可以用于全局变量和局部变量。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。

//注意:全局的常量或变量都是延迟计算的,跟延迟存储属性相似,不同的地方在于,全局的常量或变量不需要标记 lazy 修饰符

//(6)、类型属性:
//实例属性属于一个特定类型的实例,每创建一个实例,实例都拥有属于自己的一套属性值,实例之间的属性相互独立,也可以为类型本身定义属性,无论创建了多少个该类型的实例,这些属性都只有唯一一份。这种属性就是类型属性。
//注意:(1)、跟实例的存储型属性不同,必须给存储型类型属性指定默认值,因为类型本身没有构造器,也就无法在初始化过 程中使用构造器给类型属性赋值。 
//     (2)、存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化。即使它们被多个线程同时访 问,系统也保证只会对其进行一次初始化,并且不需要对其使用 lazy 修饰符。

//(7)、类型属性语法:
//使用关键字 static 来定义类型属性
struct
static var x = 12
static var y = 20
}

struct
static var x = 12
static var y = 30
}

//在为类定义计算型类型属性时,可以改用关键字 class 来支持子类对父 类的实现进行重写

class
static var x = 12
static var y = 40
class  var NewClass: Int
return 107
    }
}

print(NewSize.x,NewSize.y)
print(SizeClass.y)
print(SizeClass.NewClass)

//(8)、方法:类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。类型方法与 Objective-C 中的类方法(class methods)相似。
//(9)、实例方法:
class
var count = 0
func
count += 1
    }
func increment(by amount: Int) {
count
    }
func
count = 0
    }
}
//实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。

let counter = Counter()
counter.increment()
print(counter.count)

counter.increment(by: 5)
print(counter.count)

counter.reset()
print(counter.count)

//(a)、self 属性:类型的每一个实例都有一个隐含属性叫做 self , self 完全等同于该实例本身。你可以在一个实例的实例方法中 使用这个隐含的 self 属性来引用当前实例。

class
var count = 0
func
self.count += 1
    }
func increment(by amount: Int) {
self.count
    }
func
self.count = 0
    }
}

struct
var x = 0.0, y = 0.0
func isToTheRightOfX(x: Double) -> Bool
return self.x
    }
}

let somePoint = NewPoint(x: 4.0, y: 5.0)

if somePoint.isToTheRightOfX(x: 1.0) {
    print("This point is to the right of the line where x == 1.0")
}
//注意:如果不使用 self 前缀,Swift 就认为两次使用的 x 都指的是名称为 x 的函数参数


//(b)、在实例方法中修改值类型
//结构体和枚举是值类型。默认情况下,值类型的属性不能在它的实例方法中被修改.但是,如果你确实需要在某个特定的方法中修改结构体或者枚举的属性,你可以为这个方法选择可变(mutating) 行为,然后就可以从其方法内部改变它的属性;并且这个方法做的任何改变都会在方法执行结束时写回到原始 结构中
struct
var x = 0.0, y = 0.0
mutating func moveByX(x deltaX: Double, y deltaY: Double) {
self.x
self.y
    }
}
//实例化特定的实例
var NewMovePoint = MovePoint(x: 1.0, y: 1.0)
NewMovePoint.moveByX(x: 2.0, y: 3.0)
print("The point is now at (\(NewMovePoint.x), \(NewMovePoint.y))")
//注意:不能在结构体类型的常量上调用可变方法,因为其属性不能被改 变,即使属性是变量属性
let fixedPoint = MovePoint(x: 3.0, y: 3.0)
//fixedPoint.moveByX(2.0, y: 3.0) 错误写法

//(c)、在可变方法中给 self 赋值:
//(1)、结构体:
struct
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
self = NextPoint(x: x + deltaX, y: y
    }
}
//(2)、枚举:
enum
case
mutating func
switch self
case .Off:
self = .Low
case .Low:
self = .High
case .High:
self = .Off
        }
    }
}

var ovenLight = TriStateSwitch.High
ovenLight.next()


//(10)、类型方法:可以定义在类型本身上调用的方法,这种方法就叫做类型方法。在方法的 func 关键字之前加上关键字 static ,来指定类型方法。类还可以用关键字 class 来允许子类重写父类的方法实现。
class
class func
        // 在这里实现类型方法
    }
class func
        // 在这里实现类型方法
    }
}
//类型方法的掉用使用的是类本身
HumbinClass.eatMethod()