public func print(_ items: Any..., //任意个数,任何类型的参数它们将输出到控制台
separator: String = default,//输出多个参数之间的分隔符
terminator: String = default)//输出字符串之后的结束符号
参数separator
、terminator
都可以省略,separator
只有字输出数据大于1时才有意义。
print("a","b","c","d","e", separator: "|", terminator: "-")
打印结果为a|b|c|d|e-
,用|
将打印的字母分开,用-
来结束打印。
//自定义log p28
func hjLog(mes:Any){
print("file:\(#file) column:\(#column) line:\(#line) \(mes)")
}
由于Swift中不能定义宏,只能定义一个打印日志的方法,上面方法打印文件路径,行数,及要打印的内容,可以方便的定位到打印的位置。
%
不能应用于浮点数运算,需要使用public func truncatingRemainder(dividingBy other: Double) -> Double
方法来计算浮点型取余。var c = 19.22
//浮点型取余
let d = c.truncatingRemainder(dividingBy: 6.1)
值类型
和引用类型
。值类型
就是创建一个副本,把副本赋值或传递过去,这样在函数的调用过程中不会影响原始数据;引用类型
就是把数据本身的引用(即指针)赋值或传递过去在函数的调用过程中会影响原始数据。String
是值类型、NSString
是引用类型。==
比较基本类型的值是否相等===
比较对象类型,是不是同一个对象is
判断某个实例是否为某种类型as
强制类型转换//打印1到9平方的值
for i in 1..<10{
print("\(i)x\(i)=\(i*i)")
}
//打印集合元素
let nums = [1,2,3,4,5,6,7,8,9]
for item in nums {
print(item)
}
enumerated()
方法let nums = [1,2,3,4,5,6,7,8,9]
for(index,item) in nums.enumerated(){
print("\(index):\(item)")
}
...
和左闭右开区间..<
。for i in 1...10{
print("\(i)x\(i)=\(i*i)")
if i == 5{
break;
}
}
label1: for x in 0..<5 {
label2: for y in (1...5).reversed(){
if x == y {
break label1
}
print("(x,y)=(\(x),\(y))")
}
}
默认情况下break只会跳出最近的内循环,而示例中条件成立时直接跳出循环,给外循环添加了一个标签label1,然后break后面指定这个标签,当条件成立就跳出该标签的外循环。reversed()
是反向变量区间。
数组、字典、Set集合
。数组
是一组有序的由相同类型元素构成的集合;字典
由两部分组成,一个键集合,一个值集合,键集合不能有重复元素,而值集合可以重复,键值成对出现;Set集合
是由一串无序的,不能重复的相同类型元素构成的集合。Any
。//正规声明
var arr : Array<Any> = [1,2,"3",1.22]
print(arr[1])
//简写声明
let dic:[AnyHashable : Any] = [1:2,2:3,"3":4,4:"5"]
//字典的key和value都为任何类型,有可能是可选类型,获取元素值时要解包
print(dic[1]!)
let set:Set<String> = ["1","12","123"]
print(set.first!)
let set:Set<String> = ["1","123","12","12","1234"]
print("第一个元素\(set.first!)")
print("元素个数\(set.count)")
var set2:Set<String> = ["1234","1","123","12"]
if set == set2{
print("set等于set2")
}
//插入一个元素
set2.insert("插入")
//删除某个元素
let item = "1"
set2.remove(item)
print(set2)
//删除一个元素,这里并不是第一个元素,而是随机的
set2.removeFirst()
print(set2)
//判断是否包含某个元素
if set.contains("1234"){
print("set有该元素")
}
结果可以看出Set的first方法获取的并不一定是第一个元素,而是随机的。多个重复的元素在Set中只算一个,从count可以看出。
for item in set{
print(item)
}
for (index,item) in set.enumerated(){
print("\(index+1):\(item)")
}
注意:Set的enumerated()
方法可以取出Set的索引和元素, (index,item)是元组类型。这里的index是循环遍量,可以表示循环次数,而不是元素的序号脚标。
let A:Set<String> = ["a","b","c","d"]
let B:Set<String> = ["c","d","e","f"]
print("A与B的交集 = \(A.intersection(B))")
print("A与B的并集 = \(A.union(B))")
print("A与B异或集合 = \(A.symmetricDifference(B))")
let C = A.subtracting(B)
print("A与B差集 = \(C)")
if C.isSubset(of: A){
print("C是A的子集")
}
func rectangleArea(W width:Double,H height:Double) ->Double{
return width*height
}
print(rectangleArea(W: 10.0, H: 10.0))
W,H就是参数标签,外部调用时会提示使用。
func rectangleArea2(width:Double,height:Double) ->Double{
return width*height
}
print(rectangleArea2(width: 10.0, height: 10.0))
_
关键字声明的标签。func rectangleArea3(_ width:Double,_ height:Double) ->Double{
return width*height
}
print(rectangleArea3(10,10))
func test(a:Int = 10,b:Int) -> Int{
return a+b
}
print(test(b: 20)) //30
print(test(a: 1, b: 2)) //3
func sum(nums:Int...)->Int{
var total = 0
for num in nums{
total += num
}
return total
}
print(sum(nums: 1,2,3))
print(sum(nums: 1,2,3,4,5,6,8))
sum函数是用来求多个整型的函数,参数nums:Int…是Int类型的可变参数,在函数体重nums被认为是一个Double类型的数组
func increment(value: inout Double,auto:Double = 1.0){
value += auto
}
var value:Double = 10.0
print(value)
increment(value: &value)
print(value) //11.0
参数value是需要增长的数值,它被设计为inout
类型,inout
修饰的参数称为输入输出参数,value必须是变量不能是let修饰的常量。
func rectangleArea(width: Double,height:Double) -> Double {
return width * height
}
let rectangleArea1: (Double,Double)->Double = rectangleArea(width:height:)
print(rectangleArea1(10,10)) //100.0
var rectangleArea2: (Double,Double)->Double = rectangleArea(width:height:)
rectangleArea2(20,20)
rectangleArea2 = rectangleArea(width:height:)
print(rectangleArea2(30,30)) //900.0
//计算矩形面积
func rectangleArea(width: Double,height:Double) -> Double {
return width * height
}
//计算三角形面积
func triangleArea(width: Double,height:Double) -> Double {
return width * height/2.0
}
//作为函数返回类型使用
func getArea(type:String) -> (Double,Double)->Double {
var returnFunc: (Double,Double)->Double
switch type {
case "矩形":
returnFunc = rectangleArea
default://三角形
returnFunc = triangleArea
}
return returnFunc
}
let rectangleFunc = getArea(type: "矩形")
print("矩形面积:\(rectangleFunc(20,20))") //矩形面积:400.0
let triangleFunc = getArea(type: "三角形")
print("三角形面积:\(triangleFunc(20,20))") //三角形面积:200.0
getArea
返回值类型为函数类型,常量rectangleFunc
和triangleFunc
只是接收了getArea
函数的返回值,是将计算矩形面积和三角形面积的函数真正声明了,rectangleFunc(20,20)和triangleFunc(20,20)才是对计算面积的函数的真正调用。总之就是getArea
函数调用是为了声明rectangleArea
和triangleArea
,返回的函数常量或变量的调用才是真正用于计算面积的,
//计算矩形面积
func rectangleArea(width: Double,height:Double) -> Double {
return width * height
}
//计算三角形面积
func triangleArea(width: Double,height:Double) -> Double {
return width * height/2.0
}
//作为参数类型使用
func getAreabByFunc(funcName:(Double,Double)->Double,a:Double,b:Double)->Double {
let area = funcName(a,b) //对传进来函数参数的真正调用
return area;
}
print("矩形面积:\(getAreabByFunc(funcName: rectangleArea, a: 10, b: 10))") //矩形面积:100.0
print("三角形面积:\(getAreabByFunc(funcName: triangleFunc, a: 10, b: 10))") //三角形面积:50.0
getAreabByFunc
调用值只需要将函数名传进来就可以,传进来的函数参数直接在里面调用。
7.4. Swift嵌套函数
嵌套函数
。//嵌套函数
func calculate(opr:String) -> (Int,Int) -> Int {
//定义加函数
func add(a:Int,b:Int) -> Int{
return a + b
}
//定义减函数
func sub(a:Int,b:Int) -> Int{
return a - b
}
var result: (Int,Int) -> Int
switch opr {
case "+":
result = add
case "-":
result = sub
default:
result = add
}
return result
}
let addfunc = calculate(opr: "+") // 声明加函数
print("5+5 = \(addfunc(5,5))") //5+5 = 10
let subfunc = calculate(opr: "-")// 声明减函数
print("5-5 = \(subfunc(5,5))")//5-5 = 0
嵌套函数的作用域在外函数体内,但我们可以定义外函数的返回值类型为嵌套函数类型,从而将嵌套函数出啊递给外函数,被其调用者调用。
===
和!===
来判断是不是同一个对象;对于值类型通常用==
和!=
来判断两个值是否相等。struct Student {
var name = ""
var no = 0
var age = 0
}
var stu1 = Student()
stu1.name = "张三"
stu1.no = 1
stu1.age = 18
var stu2 = Student()
stu2.name = "张三"
stu2.no = 1
stu2.age = 18
if stu1 == stu2{
print("是同一个学生")
}else{
print("不是同一个学生")
}
可以发现在运行时报错了,报错说明为==
不能用于两个Student
结构体实例操作。说了stu1和stu2不能用于比较。我们需要在这些类型中重载
==和
!=运算符号。即定义相等规则。
struct Student {
var name = ""
var no = 0
var age = 0
}
//定义重载==号运算符符
func == (lsh:Student,rhs:Student) -> Bool {
return lsh.name == rhs.name && lsh.no == rhs.no && lsh.age == rhs.age
}
//定义重载!=号运算符符
func != (lsh:Student,rhs:Student) -> Bool {
return (lsh.name != rhs.name || lsh.no != rhs.no || lsh.age != rhs.age)
}
var stu1 = Student()
stu1.name = "张三"
stu1.no = 1
stu1.age = 18
var stu2 = Student()
stu2.name = "张三"
stu2.no = 1
stu2.age = 18
if stu1 == stu2{
print("是同一个学生")
}else{
print("不是同一个学生")
}
class Employee {
var name = ""
var no = 0
var job = ""
var day = WeekDays.Friday
var dept = Department()
struct Department {
var no = 10
var name = "Sales"
}
enum WeekDays{
case Monday,Tuesday,Wednesday,Thursday,Friday
struct Day {
static var mes = "Today is ..."
}
}
}
let emplo = Employee()
print(emplo.day)
print(emplo.dept.name)
print(Employee.WeekDays.Day.mes)
class Employee{ //员工类
var no = ""
var name = ""
var dept: Department?
}
struct Department{//部门结构体
var no = ""
var name = ""
}
上面示例可以看出,由于员工的编号都是独一无二,每个员工是独立的个体,所以员工可以声明成类Employee;如果具有相同部门标号和部门名称,我们就认为是它们是相同的部门,所以就可以把部门设计为机构体Department。
存储属性
和计算属性
。存储属性就是oc中的数据成员,计算属性不存储数据,但可以通过计算其他属性返回数据。class Employee{
let no = 0
var firstName = "Tony"
var lastName = "Guan"
lazy var dept: Department = Department() //延迟存储属性
var fullName: String{ //计算属性
get{
return firstName + "." + lastName
}
set(newFullName){
let names = newFullName.components(separatedBy: ".")
firstName = names.first!
lastName = names.last!
}
// set{
// let names = newValue.components(separatedBy: ".")
// firstName = names.first!
// lastName = names.last!
// }
}
}
struct Department{
var no = ""
var name = ""
}
let emp = Employee()
//emp.no = 10;编译会报错 ,常量属性不允许被修改。
print(emp.fullName)
emp.fullName = "Jack.Ma"
print(emp.fullName)
let dept = Department()
上面类Employee中的no、firstName、lastName、dept都是存储属性,fullName是计算属性,其中dept是延迟存储属性。
可选的(可以不实现set方法)
Settter(设置访问器)来间接设置其他属性或变量的值,语法如下。面向对象的类型(class、struct、enum) 类型名{
存储属性
var 计算属性名: 属性数据类型{
get{
return 计算后的语句值
}
set(新属性值){
语句组
}
}
}
其中新属性值
是要赋值给属性值的,当然也可以不写,Swift提供了一个默认的变量newValue
去接收新传入的值。上面set(newFullName)
可以省略如下:
set{
let names = newValue.components(separatedBy: ".")
firstName = names.first!
lastName = names.last!
}
class Employee{
let no = 0
var firstName = "Tony"
var lastName = "Guan"
lazy var dept: Department = Department()
var fullName: String{
get{
return firstName + "." + lastName
}
}
}
let emp = Employee()
//emp.no = 10;
print(emp.fullName)
// emp.fullName = "Jack.Ma" //不能赋值
fullName就是只读计算属性,不能给其赋值,结构体,枚举的计算属性也是类似,这里不再赘述。只读属性可以简化去掉get关键字和括号,上面只读属性可以简化为:
var fullName: String {
return firstName + "." + lastName
}
面向对象类型(class/struct) 类型名{
...
var 存储属性值: 属性数据类型 = 初始值{
willSet(新值){
...
}
didSet(旧值){
...
}
}
}
示例
class Employee1{
let no = 0
var name = "Tony"{
willSet(newName){
print("员工新名字:\(newName)")
}
didSet(oldName){
print("员工旧名字:\(oldName)")
}
}
}
struct Department1{
var no = 1{
willSet{
print("部门新编号:\(newValue)")
}
didSet{
print("部门旧编号:\(oldValue)")
}
}
var name = "移动开发部"
}
let emp1 = Employee1()
emp1.name = "Jack"
//结构体是值类型,必须用声明为变量才能修改其属性
var dept1 = Department1()
dept1.no = 10
Employee1类中给name存储属性添加了观察者, willSet(newName)中的newName是要传进来的新值,didSet(oldName)中的oldName是新值传进来之前的旧值;参数的声明可以省略。Department1结构体中的no就省略了观察者参数,Swift提供了对应默认参数,新值默认是newValue
,旧值默认是oldValue
。这里需要说明下枚举只有计算属性没有存储属性,所以枚举不支持属性观察者
。
枚举中没有实例存储属性但是可以有静态存储属性
。struct Account {
var amount = 0.0 //账户金额
var ower = "" //账户名
static let monthRate = 0.00688 //月利率
static var yearRate : Double{ //计算存储属性不能用let修饰,及不能是常量
return monthRate * 12
}
var owerInterest:Double{ //年利息
return Account.yearRate * amount
}
}
//访问静态属性
print(Account.yearRate)
var account = Account()
//访问实例属性
account.amount = 100000.0
print(account.owerInterest)
class Account {
var amount = 0.0 //账户金额
var ower = "" //账户名
static let monthRate = 0.00688 //月利率
class var yearRate : Double{ //class换成static子类就不能重写该属性
return monthRate * 12
}
var owerInterest:Double{ //年利息
return Account.yearRate * amount
}
static var test:Int = 10{ //静态属性也支持添加属性监听者
willSet{
print(newValue)
}
didSet{
print(oldValue)
}
}
}
//访问静态属性
print(Account.yearRate)
var account = Account()
//访问实例属性
account.amount = 100000.0
print(account.owerInterest)
class Account2:Account{
override class var yearRate : Double{ //class换成static子类就不能重写该属性
return monthRate * 12 * 1.1
}
}
这里枚举与结构体类似不再举例,类中static修饰的属性为类的静态属性,class修饰的属性称为类的类属性,区别是类的类属性可以被子类重写,但是class不能修饰存储属性,static却可以
。
- 实例属性和静态属性总结
1. 类、结构体,枚举都支持静态存储属性、静态计算属性,实例计算属性;但是只有类和结构体支持实例存储属性,枚举不支持实例存储属性。
2. 基于第一条可知只有类和结构体支持添加属性观察者(因为只有存储属性才能添加属性观察者)
3. 延迟属性只能是延迟存储属性
4. 计算属性必须是var声明的
5. let修饰的存储属性,前可以加static称为静态存储属性,不能加class
6. class修饰的属性可以被重写,但static修饰的属性不允许被重写。
7. class只能修饰计算属性。
面向对象类型(class/struct/enum) 类型名{
...
subscript (参数:参数数据类型)->返回值类型{
get{
return 返回值
}
set(新属性值){
...
}
}
}
Swift中没有提供二维数组,但是我们可以通过下标自定义一个二维数组。
struct DoubleDimensionalArray {
let rows:Int,colums:Int
var grid: [Int]
init(rows:Int,colums:Int) {
self.rows = rows //行数
self.colums = colums //列数
grid = Array(repeating: 0, count: rows * colums) //初始化数组都为0
}
subscript(row:Int,col:Int) -> Int{
get{
return grid[row * colums + col] //返回对应的脚标取出二维数组的值
}
set{
grid[row * colums + col] = newValue //通过脚标给二维数组赋值
}
}
}
//初始化一个10行10列的二维数组
var arr = DoubleDimensionalArray(rows: 10, colums: 10)
for i in 0..<10 {
for j in 0..<10{
arr[i,j] = i*j //通过脚标给二维数组赋值为脚标之和
}
}
for i in 0..<10 {
for j in 0..<10{
print("\t \(arr[i,j])",terminator: " ") //通过脚标获取二维数组中的值
}
print("\n")
}
- Swift中,方法是在类,结构体,枚举中定义的函数,分为实例方法和静态方法.
- 方法和函数的区别:方法是在在类,结构体,枚举内部定义的.方法调用前面要有主体,而函数就不需要.
- 我们在枚举和结构体方法掐面添加关键字mutatting,将方法声明为可以变方法,可变方法能够修改
值类型变量属性
,但不能修改值类型常量属性
.也就说不可变方法值类型属性是都不能访问的,但引用类型的属性是可以访问的。- static修饰的方法为静态方法,当然类中class修饰的方法类方法.与计算属性类似,实例方法中既可以访问实例属性和方法又可以访问静态属性和方法,但是静态方法不能访问实例属性和实例方法,只能访问静态属性和方法.
- class修饰的方法能被重写,static修饰的方法不能被重写.
override
)这些特征。class Person {
var name: String
var age: Int
init(name:String,age:Int) {
self.name = name
self.age = age
}
}
class Student: Person {
var school: String
override var age:Int {
get{
return super.age
}
set{
super.age = newValue<8?8:newValue
}
}
}
从属性重写来看,子类本身并不存储数据,数据存储在父类的存储属性中,子类将其变成了计算属性并重写。
注意:一个属性重写了Getter和Setter访问器后就不能重写观察者。另外常量属性和只读计算属性也都不能重写属性观察者。
class Account{
class var staticProp:Double{
return 0.0668 * 1000000
}
}
class TermAccount: Account {
override static var staticProp:Double{
return 0.0700*1000000
}
}
Account的静态属性staticProp只能用class修饰,因为要在子类TermAccount重写该静态属性,所以该属性能被继承才行,而TermAccount可以用class也可以用static修饰,因为没有子类继承TermAccount的staticProp属性。
class Person {
var name: String
var age: Int
init(name:String,age:Int) {
self.name = name
self.age = age
}
func description() -> String {
return "\(name)的年龄为:\(age)"
}
}
class Student: Person {
var school: String
override var age:Int {
get{
return super.age
}
set{
super.age = newValue
}
}
override init(name:String,age:Int) {
self.school = "清华大学"
super.init(name: name, age: age)
}
override func description() -> String {
return "\(name)的年龄为:\(age),所在学校为\(school)"
}
}
静态方法重写与实例方法重写类似,在方法名钱加上override关键字即可,但是只有class修饰的静态方法才能被被继承被重写,static修饰的静态方法不能被重写。
is
操作符可以判断一个实例是否是某个类的类型。(只要是该类型以及该类型的父类以及父类的父类类型,返回都为true,类似于oc的isKindOfClass
方法)//Student继承于Person
let stu = Student(name: "马云", age: 18)
if stu is Student{
print("马云18岁是一个学生")
}
if stu is Person{
print("马云18岁是一个人")
}
//Student,Worker类都继承于People
let stu1 = Student(name: "李彦宏", age: 35, school: "北京大学")
let stu2 = Student(name: "马化腾", age: 45, school: "深圳大学")
let stu3 = Student(name: "马云", age: 55, school: "杭州师范大学")
let wk1 = Worker(name: "李开复", age: 56, factory: "微软")
let wk2 = Worker(name: "张小龙", age: 46, factory: "腾讯")
let people = [stu1,stu2,stu3,wk1,wk2]
for p in people{
if let stu = p as? Student{ //这里在是先转换为可选类型,并且在转换成功后进行了可选绑定(因为都有值就解包了)
print("Student \(stu.name),年龄:\(stu.age),毕业于:\(stu.school)")
}else if let wk = p as? Worker{
print("Worker \(wk.name),年龄:\(wk.age),工作于:\(wk.factory)")
}
}
let stu4 = people[0]as? Student //这里是直接赋值为可选类型
print("--------")
print(stu4 as Any)
print(stu4?.name as Any) //可选类型安全访问其属性的写法
print(stu4!.name)
AnyObject
和Any
类型:Swift提供了两种类型来表示不确定类型(任意类型),AnyObject
表示任何类(class)的类型
;Any
则表示任何类型
,包括Int、Double、Array、struct等基本数据类型,也包括AnyObject
类型。也就是说AnyObject
是Any
的子集。