2016-07-15 11:27:39 ChinaExcellence 阅读数 1400
接下来我再看一下,Swift高级。语法,可能会被认为是简单的、零散的,但是我们学习的重点是,将这些简单的、零散的语法熟练掌握,应用到功能的实现中去。这才是我们想要的。







//  main.swift
//  Swift-SeniorGrammer
//
//  Created by CE on 16/7/8.
//  Copyright © 2016年 vcchou. All rightsreserved.
//

import Foundation

print("Hello, World!")

//一、函数
//1.定义函数
//无参无返回值的函数
func eat() {
   print("eating")
}
//函数调用
eat()

//2.定义一个有参数无返回值的函数
//func 函数名(参数名称:参数类型。。。)
func drink(a: Int,b: Int) {
    print(a +b)
}
drink(10, b: 20)


//3.定义一个有参数和返回值的函数
//func 函数名(参数名称:参数类型。。。) ->返回值类型
func relax(a: Int,b: Int) ->Int {
    return a -b
}
var result1 = relax(30, b: 20)
print(result1)

//4.可以让元组作为函数的返回值
func run(a: Int,b: Int) ->(a1:Int,b1:Int) {
    return (a *10,b * 10)
}
var r = run(10, b: 20)
print(r)
print(r.a1) //访问到元组里的具体元素
print(r.b1)


//5.多参量函数
//函数可以有多种不同类型的参数
//函数一
func greet(personName: String) ->String {
    return"hello" + personName
}
//函数二
func greetAgain(personName: String) ->String {
    return"hello again" + personName
}
//实现多参量函数
func sayHello(personName: String,alreadyGreet: Bool) ->String{
    ifalreadyGreet {
       return greetAgain(personName)
    } else{
       return greet(personName)
    }
}
print(sayHello("尼古拉斯赵四", alreadyGreet: false))


//6.函数的外部参数
//主要是为了指明每个参数所代表的意义
func login(userName para1: String,password para2: String) {
    print("用户名是\(para1),密码是 \(para2)")
}
login(userName: "小明", password: "123")

//忽略参数
func jump(para1: String,_ para2: String) {
   print("\(para1) \(para2)")
}
jump("123", "456")

//7.函数的默认参数值
func fly(a: Int,b: Int = 10) ->Int {
    return a *b
}
var f = fly(20)
var f1 = fly(20, b: 30)
print(f1)

//8.定义可辨参数的函数(通过...使得函数的参数个数是动态的)
func sum(numbers: Int...) ->Int {
    var result =0
    for i innumbers {
       result += i
    }
   
    returnresult
}
var sumResult = sum(1,3,5,2,7,4)
print(sumResult)

//9.函数的常量参数和变量参数
//默认情况下,函数的参数都是let类型
func swim(var a: Int,b: Int) ->Int {
    a = a +10
    returna
}
var s = swim(10, b: 0)
print(s)

//10.函数的输入输出参数
//使用inout关键字来定义函数的输入输出参数,只有变量才能作为函数的输入输出参数
func change(inout a: Int,inout b: Int) {
    let temp =a
    a = b
    b =temp
}
var c1 = 10
var c2 = 15
change(&c1, b: &c2)

print("c1 is \(c1),c2 is \(c2)")

//11.函数类型的使用
//加法
func add(a:Int,b:Int) ->Int {
    return a +b
}
//减法
func sub(a:Int,b:Int) ->Int {
    return a -b
}
//共同特点是:(Int,Int) ->Int
//函数类型作为类型标注使用
var res1: (Int,Int) ->Int = add
var finalRes1 = res1(2,3)
print(finalRes1)

//函数类型作为参数类型
func hehe(a: (Int,Int) ->Int,b: Int,c: Int) ->Int {
    returna(b,c)
}
var h = hehe(sub, b: 10, c: 30)
print(h)

//函数类型可以作为函数返回类型
func test(a: Int,b: Int) ->(Int,Int) ->Int {
    if a + b> 10 {
       return sub
    } else{
       return add
    }
}
var t = test(5, b: 4)
print(t(100,200))


//二 闭包

//swift中的闭包是自包含的代码块 可以在代码块中传递和使用  类似oc中的block
//闭包可以捕捉和存储所在上下文任意的常量和变量的引用 即包裹这些常量和变量  在swift中会自动管理捕获的过程中涉及到所有的内存操作

//1.闭包表达式语法
//{(参数:参数类型……)->返回值类型 in
//
//       //执行语句
//
//}


//2.sort函数
//是系统提供的一个排序函数  用于对数组中的元素进行排序 并不会修改原来数组

let names = ["Jack","Rose","Xaioming","Zhangsan"];
//比较大小
func prepare(para1:String,para2:String)->Bool{

    return para1< para2

}

//实际上试讲自定义的比较函数整个作为参数传递给了sort函数
var newNames = names.sort()

print(newNames)

//3.使用闭包语法写  在oc中用到block的地方 在swift中就是闭包

newNames = names.sort({(para1:String,para2:String)->Boolin


return para1 > para2

})


print(newNames)


//4.根据上下文来推断类型
//在swift中系统可以自动的推断参数和返回值的类型
newNames = names.sort({para1,para2 in

    return para1> para2

})

print(newNames)


//5.单表达式闭包隐式返回  参数类型 return都隐藏达到隐式返回的目的  只有单行表达式时可以隐藏
newNames = names.sort({para1,para2 in

    para1 >para2

})


print(newNames)



//尾随闭包



//三、枚举
//在c中我们的枚举  将枚举名和一个整型的值相对应 在swift中枚举变得更加灵活  枚举名不再只跟整形值相对应 也不再给每个枚举名提供一个值 如果有值值的类型 可能是字符串 字符  整型 或者 浮点型

//1.定义一个枚举
//使用enum关键字
enum Direction{

   //枚举成员值
    //case关键字表示新的一行 成员值将被定义
    casewest
    caseeast
    casenorth
    casesouth
   
}


//多个成员值可以定义在同一条语句中 用逗号隔开
enum Aniaml {

    casedog,pig,cat,elephant

}



//2.使用枚举值
var direction = Direction.east
//枚举再被初次初始化之后 获取另外的值时 可以直接使用(.)调用
direction = .south

print(direction)


//3.匹配枚举值和swich语句
switch direction {

case.north:
   print("north")
case.south:
   print("south")
   
case.west:
   print("west")
   
case.east:
   print("east")
   
}


//四、类和结构体
//1.定义
//类 使用class关键字
class person {
    //定义属性
    var name =""
    var age =0

}

//结构体 是哦用struct关键字
struct student {

    //定义属性
    var number =20
    var grade =5

}

//2.类的实例化
//person类
var per = person()

//给类的属性赋值
per.name = "张三"
per.age = 3
print("姓名:\(per.name),年龄:\(per.age)")

//student的实例化
var stu = student()
//给结构体属性进行赋值
stu.grade = 7
stu.number = 50
print("年级:\(stu.grade),人数:\(stu.number)")


//3.类和结构体的混合编程


struct Hand{

    var number =0

}


class Child {
   
    var name =""
    var age =0
   //将结构作为类型标注  当类中包含自定的类型时 需要将包含定义的类型初始化
    var hands:Hand = Hand()
   
}

//实例化Child
var child = Child()
//属性赋值
child.name = "旺财"
child.age = 12
child.hands.number = 2;
print("小孩\(child.name),今年\(child.age)岁,有\(child.hands.number)只手")


//4.逐一构造器
//结构体具有逐一构造器 但是类没有 因为结构体是值类型 类是引用类型

struct Dog {

    var name =""
    var age =0

}
//逐一构造器 实例化的同时 对属性进行赋值

var dog = Dog(name: "大黄", age: 3)


//5.值类型
//特点 当赋值给变量或常量或者函数的时候 是通过拷贝的形式 拷贝之后是两个并存独立的文件
var dog1 = dog
print(dog1.name)

dog.name = "小黄"
print(dog.name)

//6.引用类型
//类是引用类型  引用不是独立的 当引用类型给变量或常量或函数复制时 只是做了指针的指向内存中并没有拷贝
class Cat {

    var name =""
    var age =0

}

var cat = Cat()
cat.name = "小猫"

var cat1 = cat
print(cat1.name)

cat.name = "大猫"
print(cat.name)
print(cat1.name)


//类和结构体的区别
//相同点 1.定义的方式是一样的  2.都有各自的属性 3.实例化的方式一样  4.属性赋值的方式也是一样的

//不同点 1.关键字不同  类class 结构体struct 2.实例化时 结构体有另外的逐一构造器 但类没有  3.结构体是值类型 类是引用类型

//联系 可以将结构体作为类型标注在类中使用


//7.实例方法和类方法的使用
class Tiger {

    var name =""
    var age =0
   //创建实例方法
    funceat(){
   
       print("practice makes prfect")
       //在实例方法中self表示当前类的对象
       self.name  = "tiger"
    }
   //self还可以用来区分属性和形参
    funcrun(name:String){
   
       self.name = name
    }
    //创建类方法可以通过关键字static或class关键字来标明
   //在实例方法前面加上一个static或class关键字就成了类方法
    static funcbark(){
       
       print("barking")
       
    }

}

//调用类方法和实例方法
//实例化
var tiger = Tiger()
//调用实例化方法:通过对象的调用
tiger.eat()
//调用类方法:通过类名调用
Tiger.bark()

//相同点和不同点


//8.在值类型(结构体)中修改属性
struct student1{

    //定义属性
    var number =100
    var grade =3
   //在值类型中定义实例方法 并在实例方法中修改结构体的属性的话 需要使用关键字mutating
    mutatingfunc study(){
   
    self.number= 60
       print(":\(self.number)")
   
    }
   //在实例方法中可以直接对self进行修改
    mutatingfunc play(){
       //使用逐一构造器
       self = student1(number: 30, grade: 8)
       print("学生人数:\(self.number),年级:\(self.grade)")
   
    }

}

var stu1 = student1()
stu1.play()
stu1.study()

//9.类的继承
class Car{

    var carName= ""
    var price =0.0
   
   //可以在父类的实例化方法中添加final关键字 表示 设置此方法不被子类重写
//    final funcrun(){
     func run(){
   
       print("奔跑吧,皮卡丘")
   
    }
   
}

//通过类的继承 子类可以继承父类所有的方法和属性
//和oc一样 swift中不许多继承
class AutoCar:Car{
   //给子类添加一个新的属性
    var factory= "山东蓝翔"

}

var autoCar = AutoCar()
autoCar.carName = "兰博基尼"
autoCar.price = 1.0;
autoCar.run()

class Bicycle:Car{
   //如果要在子类中重写父类的方法 需要用override关键字进行表明
    overridefunc run() {
       print("奔跑吧,呵呵")
    }
   
}

var bicycle = Bicycle()
bicycle.run()


//五 构造函数 (init函数或者初始化函数)

class Bird {

    var name =""
    var age =0
    letwight:Double
   //创建构造方法
   //构造方法的作用为当类被实例化时 第一个调用的方法
   init(){
   print("bird")
   
   //swift中的构造方法可以给常量赋值
       self.wight = 2.0
   
    }
   //带有内部参数的构造方法
   init(name:String){
    self.name =name
    self.wight =3.0
 
    }

   //带有外部参数的构造方法
    init(namepara1:String,age para2:Int){
       self.name = para1
       self.age = para2
       self.wight = 4.0

    }
   //隐藏外部参数的构造方法
   init(para1:String,_para2:Int){
       self.name = para1
       self.age = _para2
       self.wight = 4.0

    }

}


//创建bird对象
var bird1 = Bird.init()
var bird2 = Bird.init(name:"喜鹊")
var bird3 = Bird.init(name:"",age:3)
//var bird4 = Bird.init("麻雀",3)


class FlyBird:Bird{

   //在子类中重写父类的构造方法
   
    overrideinit(){
   
   super.init()
   
   
    }
   
    //析构函数
    //只适用于类当一个类的实例被释放后 析构器会被立即调用 析构器使用关键字deinit来标明
   deinit{
   
   print("析构器被调用")
   
   
    }
}

//实例化
var flyBird:FlyBird? = FlyBird()
print(flyBird)

//将flyBird对象置为nil 才会被调用
flyBird = nil

//六 扩展
//使用extension关键字定义扩展

extension Int {
    //扩展属性
    var a: Int{
       //扩展之后进行的操作
       return self * self
    }

}
//通过点直接调用属性
print(5.a)

extension Bird {

    //扩展属性
    varbirdColor:String {
       
       return self.name + "是白色的" //self指的还是bird对象
       
    }
    //扩展方法
    funcsingSong(){
   
   print("\(self.name)在唱歌")
   
    }

}

//通过扩展向父类中提添加的属性和方法 都可以传递给子类
var bird11 = FlyBird()

bird11.name = "鹦鹉"
print(bird11.birdColor)
print(bird11.singSong())












2015-07-06 15:28:00 artga870876 阅读数 0

 

Swift基础语法学习总结
Swift高级语法学习总结
Swift语法总结补充(一)

 

1.函数
  1.1 func funcNmae()->(){} 这样就定义了一个函数,它的参数为空,返回值为空,如果有参数和返回值直接写在两个括号里就可以了
  1.2 参数需要指明类型,而如果没有返回值可以不写->(),返回值只需要写返回类型,如果写了返回名称,可以在函数调用后的值用点语法访问
  1.3 在参数名称前可以加上外部参数名,调用的时候就可以带上它了,如果外部参数名和内部参数名相同则可以直接在内部参数名前加#即可
  1.4 如果跟参数设置一个默认值,则swift会自动加上外部参数名,如果不想有在前面用下划线就可以_,如果默认值参数不在最后则不能省略,在传参时传一个下划线进去就可以
  1.5 在最后一个参数后面加...代表是这个参数是可变参数并且类型就是它,参数个数至少是0个,在函数内可以使用for in 这个参数获得 这些参数
  1.6 每个参数前面其实有一个隐藏的关键字let,如果想让参数可变(可赋值)则需要加上var关键字,不过也只是能赋值,因为是值拷贝所以不能修改外部参数的实际值,如果要变成地址传递,需要在参数前加inout 关键字,而实参需要加上& ,
  1.7 swift中函数其实也只是中类型,函数名就是变量名,比如let func1 : () -> () 声明的就是个无参数无返回值的函数类型,所以如果一个函数返回一个函数的话和返回一个普通变量没什么区别
2.闭包
  2.1 闭包代表了一段程序代码,{ (传入参数) -> 返回值的类型 in ... 表达式 ... },函数只是闭包的一个特例
  2.2 闭包可以推断返回类型,所以可以省略->返回值类型,参数类型也可以推到,所以参数类型也不要,括号也可以去掉,如果闭包只有一个表达式直接可以省略掉return关键字,因为我们可以用$0/$1简写参数,所以参数也可以省略掉.
  2.3如果闭包是函数最后一个参数,则可以去掉小括号,直接用大括号里的内容,不过需要大括号紧接着写,叫尾随闭包
  2.4 内层返回函数会捕获外层的变量的值,当内层函数返回后,外层函数的内部变量并不会释放内存,它的变量的值会跟着内部函数的执行而变化
3.枚举
  3.1 用enum CompassPoint{ case North, South , East, West}定义枚举,可以不用逗号,和声明语句一样分开用多个case写就可以了
  3.2 可以用元组来设定枚举对应每项中的值,并可以用switch case 中的值绑定来判断,
  3.3 枚举类型如果初始化为Int类型,它的下一项也会有一个加1的原始值,不过枚举直接复制成string都可以的
4.结构体
  4.1 结构题的属性必须初始化,必须有默认值或者通过构造器init
  4.2 结构体本身是值传递,如果一个结构体赋值给另外一个结构体了也是两份拷贝,互相修改不会有影响
  4.3 如果一个结构体用let声明了,那么它内部的值就不能再做修改了,var声明的结构体才能修改
  4.4 但是class不同,对象的赋值会是同一份引用,修改会影响到另外一个对象,但是let 声明的变量也是不能赋值的,只是能修改它内部的值而已
5.属性(成员变量)
  5.1 结构体/类在初始化的时候成员变量一定要有值,如果你没有给出初始化方法,则默认有一个包含所有必须初始化的的init方法,如果你提供了,默认了就没有了
  5.2 (延迟属性)用let声明的成员变量,就不能再修改了,如果是一个耗时的属性比如值是一个自定义对象,可以加上lazy属性,它只有在用到的时候才会对这个属性做初始化,避免不要的消耗(延迟属性)
  5.3 (计算属性)有的属性是根据其它的属性计算出来的,并不是一个必须的属性,只是让使用的时候方便了些,在属性定义后面加上set/get方法,get方法需要返回一个值,set方法有一个参数,用来设置其它属性,如果不要参数就不要写参数外面的括号,它就有一个默认的参数newValue
  5.4 如果只有get方法就是只读属性,只读属性swift提供了一种简写方式,直接把返回语句写在最外面的大括号里就可以了
  5.5 swift提供了属性监听方法:willSet和didSet,两个都是一样有一个参数,分别将要设置的值,和属性过去的值,同样你如果不提供参数,会用两个默认的newValue和oldValue.这两个方法和set/get方法并列,在didSet方法中可以直接修改(调整)属性的值,但是这两个方法不能和set/get方法共存
  5.6 swift中有类别属性,enum/struct 用关键字static,class就用class关键字,在class中let 声明的需要直接赋初始值,var声明的必须用get方法return,因为swift不允许class储存类属性,而enum/struct可以
6.函数(成员方法)
  6.1 class中的函数都不需要加上外部参数,因为除了第一个参数都默认加上了#符号,不过这只是swift帮你做了点事情而已,它并没有在语法上强制你做什么,你想为第一个参数加上外部参数也行,都用_代替默认的外部参数都可以
  6.2 在swift里self.x不代表会调用setX/getX方法,所以直接可以在set/get方法里这么用.
  6.3 在struct和enum中,成员方法不允许修改成员变量,如果要修改需要加上mutating关键字,但是如果声明的结构体变量是一个let常量的话,这个方法也是不允许调用的.
  6.4 在struct和enum中你可以在mutating方法中直接跟self赋值成另外一个变量
  6.5 在struct和enmu中用static标识一个方法是类方法,而class中用class关键字
7.角标(subscript)
  7.1 重写subscript,类似于subscript(index: Int) -> Int{},里面写set/get方法,和声明变量,根据传参和返回值来确定下标的类型和返回值,重写了这个方法这个对应的类型就可以用角标了.
  7.2 subscript方法参数的个数对应角标的个数,例如两个参数:mar[2,3]
8.继承
  8.1 swift中没有基础类,所有不继承其它类的的类都是基础类,重写父类中init方法,要先调用super的init方法,然后再在后面修改属性的值,访问属性直接用属性名字就可以了,不用用self等.
  8.2 要重写属性和重写方法类似,直接加个override就好了,在重写的set/get方法也可以调用super对应的属性值,或设置值都可以.
  8.3 覆盖了didSet属性监视器就不能再覆盖set/get方法了,跟方法或属性加上final关键字可以防止被子类覆盖
9.初始化(init)
  9.1 init方法和普通方法一样,你需要在init方法中把每个必须要赋值的属性都赋值,否则会出编译错误,init方法会给每个参数加上#,不要它的话可以用_,在方法内部用self访问,也可以不用
  9.2 如果你自定义了init方法,那么swift会不再提供默认的init方法,你可以自己写一个init方法,init方法带不带参数什么都是可以的,自己决定就行
  9.3 如果你想一个init方法中调用另一个init方法,需要加上一个convenience关键字,在这个init方法里就可以调用另一个init方法了
  9.4 在子类继承父类的时候,首先需要初始化子类的成员变量,然后才能调用super的init方法初始化父类的属性,最后可以修改子类和父类的属性,如果这个属性是父类的,在子类中也是用self访问,因为这个属性已经是它自己的了
  9.5 如果子类一个init方法都没有提供,那么子类继承父类所有的构造器,可以用父类的init方法初始化
  9.6 在初始化属性的时候,可以用闭包实现,只要在复制的=后面加上{},你们写return和其它语句,最后在{}后面加一个()表示闭包立刻执行,闭包和属性的set方法是类似的,只是在最开始提供的
10.销毁方法(deinit)
  10.1 deinit方法会在对象销毁的时候调用,可以打印判断它时候销毁
11.内存管理(ARC)
  11.1 可选类型或者普通类型属性只要对一个对象有一个引用,它的这个对象的引用计数就要加1,如果两个对象相互引用就会产生引用循环,所以需要跟其中的一个属性用关键字weak声明为弱引用,就是可以设置为nil
  11.2 一般用weak声明的用可选类型,因为它引用的可能为nil,如果你在调用的时候能确定它是有值的,你可以声明为unowned的普通类型,它的效果是如果你能保证调用这个属性时不为nil时,swift建议用unowned,其它都和weak一样的
12.可选链(Optional Chaining)
  12.1 对于一个可选类型的属性可以用?.和!.来访问,如果确定有值可以用a!.b!.c!.d,如果不确定有值可以用a?.b?.c?.d
13.类型转化
  13.1 可以用 a is b 来判断对象a是否是b类型,返回值是一个boolean类型的值
  13.2 as 可以把普通类型转化,比如double,int,cgfloat之类都可以用它转化
  13.3 可以用 as? 把之类转化成父类,它的结果可以是nil或者转型成功,所以结果是一个可选类型,转化成功后用?.来访问方法或属性,也可以做可选绑定.
  13.4 如果一个对象一定可以转化成功,则可以用as!转化,如果转化不成功会报运行时错误,比如在一个数组中全是animal,但是声明时是anyobject就可以这样用.
14.扩展/协议(categories,extension,protocol)
  14.1 swift的extension没有名称,它是扩展到所有的实体类的,它不可以增加成员属性,但是可以增加计算属性
  14.2 可以在protocol中声明属性和方法,可以定义属性时候是只读的或者是可读可写的
  14.3 可以让一个extension继承一个protocol,在里面实现对应的方法
  14.4 协议的类型是protocol<protcl1,protcl2>
15.泛型
  15.1 可以在func或struct后面跟上<T1,T2>,在参数中就可以声明参数类型为这个类型,完全可以把它看做是一个类型
16.运算符重载
  16.1 在swift中运算符可以重载,方法名就是运算符号,参数的个数和类型是根据它是几目运算符和运算符两边对应的参数类型决定的.

转载于:https://www.cnblogs.com/hkJser/p/4624488.html

2014-06-12 23:31:10 sqc3375177 阅读数 2776

除了基本操作符中所讲的运算符,Swift还有许多复杂的高级运算符,包括了C语和Objective-C中的位运算符和移位运算。

 

不同于C语言中的数值计算,Swift的数值计算默认是不可溢出的。溢出行为会被捕获并报告为错误。你是故意的?好吧,你可以使用Swift为你准备的另一套默认允许溢出的数值运算符,如可溢出加&+。所有允许溢出的运算符都是以&开始的。

 

自定义的结构,类和枚举,是否可以使用标准的运算符来定义操作?当然可以!在Swift中,你可以为你创建的所有类型定制运算符的操作。

 

可定制的运算符并不限于那些预设的运算符,自定义有个性的中置,前置,后置及赋值运算符,当然还有优先级和结合性。这些运算符的实现可以运用预设的运算符,也可以运用之前定制的运算符。

 

位运算符

位操作符通常在诸如图像处理和创建设备驱动等底层开发中使用,使用它可以单独操作数据结构中原始数据的比特位。在使用一个自定义的协议进行通信的时候,运用位运算符来对原始数据进行编码和解码也是非常有效的。

 

Swift支持如下所有C语言的位运算符:

 

按位取反运算符

按位取反运算符~对一个操作数的每一位都取反。

这个运算符是前置的,所以请不加任何空格地写着操作数之前。

  1. let initialBits: UInt8 = 0b00001111 
  2. let invertedBits = ~initialBits  // 等于 0b11110000 

UInt8是8位无符整型,可以存储0~255之间的任意数。这个例子初始化一个整型为二进制值00001111(前4位为0,后4位为1),它的十进制值为15。

 

使用按位取反运算~对initialBits操作,然后赋值给invertedBits这个新常量。这个新常量的值等于所有位都取反的initialBits,即1变成0,0变成1,变成了11110000,十进制值为240。

 

按位与运算符

按位与运算符对两个数进行操作,然后返回一个新的数,这个数的每个位都需要两个输入数的同一位都为1时才为1。

以下代码,firstSixBits和lastSixBits中间4个位都为1。对它俩进行按位与运算后,就得到了00111100,即十进制的60。

  1. let firstSixBits: UInt8 = 0b11111100 
  2. let lastSixBits: UInt8  = 0b00111111 
  3. let middleFourBits = firstSixBits & lastSixBits  // 等于 00111100 

按位或运算

按位或运算符|比较两个数,然后返回一个新的数,这个数的每一位设置1的条件是两个输入数的同一位都不为0(即任意一个为1,或都为1)。

如下代码,someBits和moreBits在不同位上有1。按位或运行的结果是11111110,即十进制的254。

  1. let someBits: UInt8 = 0b10110010 
  2. let moreBits: UInt8 = 0b01011110 
  3. let combinedbits = someBits | moreBits  // 等于 11111110 

按位异或运算符

按位异或运算符^比较两个数,然后返回一个数,这个数的每个位设为1的条件是两个输入数的同一位不同,如果相同就设为0。

以下代码,firstBits和otherBits都有一个1跟另一个数不同的。所以按位异或的结果是把它这些位置为1,其他都置为0。

  1. let firstBits: UInt8 = 0b00010100 
  2. let otherBits: UInt8 = 0b00000101 
  3. let outputBits = firstBits ^ otherBits  // 等于 00010001 

按位左移/右移运算符

左移运算符<<和右移运算符>>会把一个数的所有比特位按以下定义的规则向左或向右移动指定位数。

 

按位左移和按位右移的效果相当把一个整数乘于或除于一个因子为2的整数。向左移动一个整型的比特位相当于把这个数乘于2,向右移一位就是除于2。

 

无符整型的移位操作

对无符整型的移位的效果如下:

 

已经存在的比特位向左或向右移动指定的位数。被移出整型存储边界的的位数直接抛弃,移动留下的空白位用零0来填充。这种方法称为逻辑移位。

 

以下这张把展示了 11111111 << 1(11111111向左移1位),和 11111111 >> 1(11111111向右移1位)。蓝色的是被移位的,灰色是被抛弃的,橙色的0是被填充进来的。

  1. let shiftBits: UInt8 = 4   // 即二进制的00000100 
  2. shiftBits << 1             // 00001000 
  3. shiftBits << 2             // 00010000 
  4. shiftBits << 5             // 10000000 
  5. shiftBits << 6             // 00000000 
  6. shiftBits >> 2             // 00000001 

你可以使用移位操作进行其他数据类型的编码和解码。

  1. let pink: UInt32 = 0xCC6699 
  2. let redComponent = (pink & 0xFF0000) >> 16    // redComponent 是 0xCC, 即 204 
  3. let greenComponent = (pink & 0x00FF00) >> 8   // greenComponent 是 0x66, 即 102 
  4. let blueComponent = pink & 0x0000FF           // blueComponent 是 0x99, 即 153 

这个例子使用了一个UInt32的命名为pink的常量来存储层叠样式表CSS中粉色的颜色值,CSS颜色#CC6699在Swift用十六进制0xCC6699来表示。然后使用按位与(&)和按位右移就可以从这个颜色值中解析出红(CC),绿(66),蓝(99)三个部分。

 

对0xCC6699和0xFF0000进行按位与&操作就可以得到红色部分。0xFF0000中的0了遮盖了OxCC6699的第二和第三个字节,这样6699被忽略了,只留下0xCC0000。

 

然后,按向右移动16位,即 >> 16。十六进制中每两个字符是8比特位,所以移动16位的结果是把0xCC0000变成0x0000CC。这和0xCC是相等的,都是十进制的204。

 

同样的,绿色部分来自于0xCC6699和0x00FF00的按位操作得到0x006600。然后向右移动8們,得到0x66,即十进制的102。

 

最后,蓝色部分对0xCC6699和0x0000FF进行按位与运算,得到0x000099,无需向右移位了,所以结果就是0x99,即十进制的153。

 

有符整型的移位操作

有符整型的移位操作相对复杂得多,因为正负号也是用二进制位表示的。(这里举的例子虽然都是8位的,但它的原理是通用的。)

 

有符整型通过第1个比特位(称为符号位)来表达这个整数是正数还是负数。0代表正数,1代表负数。

 

其余的比特位(称为数值位)存储其实值。有符正整数和无符正整数在计算机里的存储结果是一样的,下来我们来看+4内部的二进制结构。

符号位为0,代表正数,另外7比特位二进制表示的实际值就刚好是4。

 

负数呢,跟正数不同。负数存储的是2的n次方减去它的绝对值,n为数值位的位数。一个8比特的数有7个数值位,所以是2的7次方,即128。

 

我们来看-4存储的二进制结构。

现在符号位为1,代表负数,7个数值位要表达的二进制值是124,即128 - 4。

负数的编码方式称为二进制补码表示。这种表示方式看起来很奇怪,但它有几个优点。

 

首先,只需要对全部8个比特位(包括符号)做标准的二进制加法就可以完成 -1 + -4 的操作,忽略加法过程产生的超过8个比特位表达的任何信息。

第二,由于使用二进制补码表示,我们可以和正数一样对负数进行按位左移右移的,同样也是左移1位时乘于2,右移1位时除于2。要达到此目的,对有符整型的右移有一个特别的要求:

 

对有符整型按位右移时,使用符号位(正数为0,负数为1)填充空白位。

这就确保了在右移的过程中,有符整型的符号不会发生变化。这称为算术移位。

 

正因为正数和负数特殊的存储方式,向右移位使它接近于0。移位过程中保持符号会不变,负数在接近0的过程中一直是负数。

 

溢出运算符

默认情况下,当你往一个整型常量或变量赋于一个它不能承载的大数时,Swift不会让你这么干的,它会报错。这样,在操作过大或过小的数的时候就很安全了。

 

例如,Int16整型能承载的整数范围是-32768到32767,如果给它赋上超过这个范围的数,就会报错:

  1. var potentialOverflow = Int16.max 
  2. // potentialOverflow 等于 32767, 这是 Int16 能承载的最大整数 
  3. potentialOverflow += 1 
  4. // 噢, 出错了 

对过大或过小的数值进行错误处理让你的数值边界条件更灵活。

 

当然,你有意在溢出时对有效位进行截断,你可采用溢出运算,而非错误处理。Swfit为整型计算提供了5个&符号开头的溢出运算符。

 

溢出加法 &+

溢出减法 &-

溢出乘法 &*

溢出除法 &/

溢出求余 &%

 

值的上溢出

下面例子使用了溢出加法&+来解剖的无符整数的上溢出

  1. var willOverflow = UInt8.max 
  2. // willOverflow 等于UInt8的最大整数 255 
  3. willOverflow = willOverflow &+ 1 
  4. // 这时候 willOverflow 等于 0 

willOverflow用Int8所能承载的最大值255(二进制11111111),然后用&+加1。然后UInt8就无法表达这个新值的二进制了,也就导致了这个新值上溢出了,大家可以看下图。溢出后,新值在UInt8的承载范围内的那部分是00000000,也就是0。

 

值的下溢出

数值也有可能因为太小而越界。举个例子:

 

UInt8的最小值是0(二进制为00000000)。使用&-进行溢出减1,就会得到二进制的11111111即十进制的255。

 

Swift代码是这样的:

  1. var willUnderflow = UInt8.min 
  2. // willUnderflow 等于UInt8的最小值0 
  3. willUnderflow = willUnderflow &- 1 
  4. // 此时 willUnderflow 等于 255 

有符整型也有类似的下溢出,有符整型所有的减法也都是对包括在符号位在内的二进制数进行二进制减法的,这在 "按位左移/右移运算符" 一节提到过。最小的有符整数是-128,即二进制的10000000。用溢出减法减去去1后,变成了01111111,即UInt8所能承载的最大整数127。

来看看Swift代码:

  1. var signedUnderflow = Int8.min 
  2. // signedUnderflow 等于最小的有符整数 -128 
  3. signedUnderflow = signedUnderflow &- 1 
  4. // 如今 signedUnderflow 等于 127 

除零溢出

一个数除于0 i / 0,或者对0求余数 i % 0,就会产生一个错误。

  1. let x = 1 
  2. let y = x / 0 

使用它们对应的可溢出的版本的运算符&/和&%进行除0操作时就会得到0值。

  1. let x = 1 
  2. let y = x &/ 0 
  3. // y 等于 0 

优先级和结合性

运算符的优先级使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。

 

结合性定义相同优先级的运算符在一起时是怎么组合或关联的,是和左边的一组呢,还是和右边的一组。意思就是,到底是和左边的表达式结合呢,还是和右边的表达式结合?

 

在混合表达式中,运算符的优先级和结合性是非常重要的。举个例子,为什么下列表达式的结果为4?

  1. 2 + 3 * 4 % 5 
  2. // 结果是 4 

如果严格地从左计算到右,计算过程会是这样:

 

2 plus 3 equals 5;

2 + 3 = 5

5 times 4 equals 20;

5 * 4 = 20

20 remainder 5 equals 0

20 / 5 = 4 余 0

 

但是正确答案是4而不是0。优先级高的运算符要先计算,在Swift和C语言中,都是先乘除后加减的。所以,执行完乘法和求余运算才能执行加减运算。

 

乘法和求余拥有相同的优先级,在运算过程中,我们还需要结合性,乘法和求余运算都是左结合的。这相当于在表达式中有隐藏的括号让运算从左开始。

  1. 2 + ((3 * 4) % 5) 

(3 * 4) is 12, so this is equivalent to: 3 * 4 = 12,所以这相当于:

  1. 2 + (12 % 5) 

(12 % 5) is 2, so this is equivalent to: 12 % 5 = 2,所这又相当于

  1. 2 + 2 

计算结果为 4。

 

查阅Swift运算符的优先级和结合性的完整列表,请看表达式。

 

注意:Swift的运算符较C语言和Objective-C来得更简单和保守,这意味着跟基于C的语言可能不一样。所以,在移植已有代码到Swift时,注意去确保代码按你想的那样去执行。

 

运算符函数

让已有的运算符也可以对自定义的类和结构进行运算,这称为运算符重载。

 

这个例子展示了如何用+让一个自定义的结构做加法。算术运算符+是一个两目运算符,因为它有两个操作数,而且它必须出现在两个操作数之间。

 

例子中定义了一个名为Vector2D的二维坐标向量 (x,y) 的结构,然后定义了让两个Vector2D的对象相加的运算符函数。

  1. struct Vector2D { 
  2.     var x = 0.0, y = 0.0 
  3. @infix func + (left: Vector2D, right: Vector2D) -> Vector2D { 
  4.     return Vector2D(x: left.x + right.x, y: left.y + right.y) 

该运算符函数定义了一个全局的+函数,这个函数需要两个Vector2D类型的参数,返回值也是Vector2D类型。需要定义和实现一个中置运算的时候,在关键字func之前写上属性 @infix 就可以了。

 

在这个代码实现中,参数被命名为了left和right,代表+左边和右边的两个Vector2D对象。函数返回了一个新的Vector2D的对象,这个对象的x和y分别等于两个参数对象的x和y的和。

 

这个函数是全局的,而不是Vector2D结构的成员方法,所以任意两个Vector2D对象都可以使用这个中置运算符。

  1. let vector = Vector2D(x: 3.0, y: 1.0) 
  2. let anotherVector = Vector2D(x: 2.0, y: 4.0) 
  3. let combinedVector = vector + anotherVector 
  4. // combinedVector 是一个新的Vector2D, 值为 (5.0, 5.0) 

这个例子实现两个向量 (3.0,1.0) 和 (2.0,4.0) 相加,得到向量 (5.0,5.0) 的过程。如下图示:

 

前置和后置运算符

上个例子演示了一个双目中置运算符的自定义实现,同样我们也可以玩标准单目运算符的实现。单目运算符只有一个操作数,在操作数之前就是前置的,如-a; 在操作数之后就是后置的,如i++。

 

实现一个前置或后置运算符时,在定义该运算符的时候于关键字func之前标注 @prefix 或 @postfix 属性。

  1. @prefix func - (vector: Vector2D) -> Vector2D { 
  2.     return Vector2D(x: -vector.x, y: -vector.y) 

这段代码为Vector2D类型提供了单目减运算-a,@prefix属性表明这是个前置运算符。

 

对于数值,单目减运算符可以把正数变负数,把负数变正数。对于Vector2D,单目减运算将其x和y都进进行单目减运算。

  1. let positive = Vector2D(x: 3.0, y: 4.0) 
  2. let negative = -positive 
  3. // negative 为 (-3.0, -4.0) 
  4. let alsoPositive = -negative 
  5. // alsoPositive 为 (3.0, 4.0) 

组合赋值运算符

组合赋值是其他运算符和赋值运算符一起执行的运算。如+=把加运算和赋值运算组合成一个操作。实现一个组合赋值符号需要使用@assignment属性,还需要把运算符的左参数设置成inout,因为这个参数会在运算符函数内直接修改它的值。

  1. @assignment func += (inout left: Vector2D, right: Vector2D) { 
  2.     left = left + right 

因为加法运算在之前定义过了,这里无需重新定义。所以,加赋运算符函数使用已经存在的高级加法运算符函数来执行左值加右值的运算。

  1. var original = Vector2D(x: 1.0, y: 2.0) 
  2. let vectorToAdd = Vector2D(x: 3.0, y: 4.0) 
  3. original += vectorToAdd 
  4. // original 现在为 (4.0, 6.0) 

你可以将 @assignment 属性和 @prefix 或 @postfix 属性起来组合,实现一个Vector2D的前置运算符。

  1. @prefix @assignment func ++ (inout vector: Vector2D) -> Vector2D { 
  2.     vector += Vector2D(x: 1.0, y: 1.0) 
  3.     return vector 

这个前置使用了已经定义好的高级加赋运算,将自己加上一个值为 (1.0,1.0) 的对象然后赋给自己,然后再将自己返回。

  1. var toIncrement = Vector2D(x: 3.0, y: 4.0) 
  2. let afterIncrement = ++toIncrement 
  3. // toIncrement 现在是 (4.0, 5.0) 
  4. // afterIncrement 现在也是 (4.0, 5.0) 

注意:默认的赋值符是不可重载的。只有组合赋值符可以重载。三目条件运算符 a?b:c 也是不可重载。

 

比较运算符

Swift无所知道自定义类型是否相等或不等,因为等于或者不等于由你的代码说了算了。所以自定义的类和结构要使用比较符==或!=就需要重载。

 

定义相等运算符函数跟定义其他中置运算符雷同:

  1. @infix func == (left: Vector2D, right: Vector2D) -> Bool { 
  2.     return (left.x == right.x) && (left.y == right.y) 
  3.  
  4. @infix func != (left: Vector2D, right: Vector2D) -> Bool { 
  5.     return !(left == right) 

上述代码实现了相等运算符==来判断两个Vector2D对象是否有相等的值,相等的概念就是他们有相同的x值和相同的y值,我们就用这个逻辑来实现。接着使用==的结果实现了不相等运算符!=。

 

现在我们可以使用这两个运算符来判断两个Vector2D对象是否相等。

  1. let twoThree = Vector2D(x: 2.0, y: 3.0) 
  2. let anotherTwoThree = Vector2D(x: 2.0, y: 3.0) 
  3. if twoThree == anotherTwoThree { 
  4.     println("这两个向量是相等的."
  5. // prints "这两个向量是相等的." 

自定义运算符

标准的运算符不够玩,那你可以声明一些个性的运算符,但个性的运算符只能使用这些字符 / = - + * % < >!& | ^。~。

 

新的运算符声明需在全局域使用operator关键字声明,可以声明为前置,中置或后置的。

  1. operator prefix +++ {} 

这段代码定义了一个新的前置运算符叫+++,此前Swift并不存在这个运算符。此处为了演示,我们让+++对Vector2D对象的操作定义为 双自增 这样一个独有的操作,这个操作使用了之前定义的加赋运算实现了自已加上自己然后返回的运算。

  1. @prefix @assignment func +++ (inout vector: Vector2D) -> Vector2D { 
  2.     vector += vector 
  3.     return vector 

Vector2D 的 +++ 的实现和 ++ 的实现很接近, 唯一不同的前者是加自己, 后者是加值为 (1.0, 1.0) 的向量.

  1. var toBeDoubled = Vector2D(x: 1.0, y: 4.0) 
  2. let afterDoubling = +++toBeDoubled 
  3. // toBeDoubled 现在是 (2.0, 8.0) 
  4. // afterDoubling 现在也是 (2.0, 8.0) 

自定义中置运算符的优先级和结合性

可以为自定义的中置运算符指定优先级和结合性。可以回头看看优先级和结合性解释这两个因素是如何影响多种中置运算符混合的表达式的计算的。

 

结合性(associativity)的值可取的值有left,right和none。左结合运算符跟其他优先级相同的左结合运算符写在一起时,会跟左边的操作数结合。同理,右结合运算符会跟右边的操作数结合。而非结合运算符不能跟其他相同优先级的运算符写在一起。

 

结合性(associativity)的值默认为none,优先级(precedence)默认为100。

 

以下例子定义了一个新的中置符+-,是左结合的left,优先级为140。

  1. operator infix +- { associativity left precedence 140 } 
  2. func +- (left: Vector2D, right: Vector2D) -> Vector2D { 
  3.     return Vector2D(x: left.x + right.x, y: left.y - right.y) 
  4. let firstVector = Vector2D(x: 1.0, y: 2.0) 
  5. let secondVector = Vector2D(x: 3.0, y: 4.0) 
  6. let plusMinusVector = firstVector +- secondVector 
  7. // plusMinusVector 此时的值为 (4.0, -2.0) 

这个运算符把两个向量的x相加,把向量的y相减。因为他实际是属于加减运算,所以让它保持了和加法一样的结合性和优先级(left和140)。查阅完整的Swift默认结合性和优先级的设置,请移步表达式;

2016-03-07 21:47:35 qq_32780047 阅读数 367
// 高级用法 (面试)
// 可选类型 值绑定(可选类型结合 if-else 使用), 都要定义局部变量
var bindingOptional:Int!
// local生命周期 - if 结束
// * 局部变量后面必须是可选类型, 其实判断的是可选类型是否为空, 当为空时,直接走 else, 都有值时,走 if,顺便完成局部变量的赋值,方便我们在 if 花括号中对局部变量的使用
if var local = bindingOptional {
    print("local = \(local)")
} else {
    print("值为空")
}

// switch - case
// 1.switch 中, 功能更强大
// 2.默认没有贯穿, fallthrough关键词为贯穿下面的内容, 就是在得到要的值后面还会输出下面的内容

var switchNumber = 5
// case 一定包含所有的情况, 如果情况不能都写完, 就用 default 代替
switch switchNumber {
case 0,1,2,3,4:
    print("小于5")
    fallthrough
case 6...100:
    print("大于5小于100")
    fallthrough
case 5:
    print("5")
    fallthrough
default:
    print("others")
}

// 高级用法(面试)
// switch - case 值绑定
// 把一个作为判断条件, 一个作为局部变量接受值
// 利用系统的推断
var (x,y) = (10, 0)
switch (x,y) {
case (var xValue, 0):
    print("xValue = \(xValue)")
case (0,var yValue):
    print("yValue = \(yValue)")
default:
    print("位于坐标系内")
}

// 循环
for var i = 0; i < 10; i++ {
    print("i = \(i)")
}

// for - in循环
for i in 1...10 {
    print("i = \(i)")
}

// while 循环
var whileNumber = 0
while whileNumber < 10 {
    print("whileNumber = \(whileNumber)")
    whileNumber++
}

// repeat - while 循环
// 先执行, 后判断
repeat {
    print("我能行")
} while false

// 标签语句
// 应用场景: 循环嵌套
// 准确的取消哪一个循环, 想取消哪一个循环, 就该哪一个循环取个标签
joner: while true {
    for var i = 0; i < 50; i++ {
        print("i == \(i)")
        if i == 5 {
           break joner
        }
    }
}

// 枚举
// 还是值类型
enum Person {
    case Shaoqiong
    case Xiaoze
    case Bowen
}

print(Person.Bowen.hashValue)

// ***开发中
// 原始值
// 指定枚举值的类型 Int
enum Fruit:Int {
    case Apple
    case Banana
    case Orange
}
// 原始值, rawValue 就是原始值
print(Fruit.Orange.rawValue)

// 函数, 是一个引用类型
// 函数其实就是OC当中的方法

// 定义函数使用 func
// 1.没返回值, 没参数
// 注意:()里卖写的是参数列表, ->代表返回值
// 函数的类型包括参数类型和返回值类型
// eg.()->Void 整体是一个类型
// 书写时:->Void 可以省
func printStr()->Void {
    print("hello world")
}
// 函数调用
printStr()

// 2.没返回值, 有参数
// (String)->Void为函数类型
func printPersonName(name:String) {
    print("name = \(name)")
}
// 调用
printPersonName("gangyun")
// *常量
var jonerFunc :(String)->Void = printPersonName
// 调用 jonerFunc
jonerFunc("qiong")
// *多个参数
func printPersonInfo(name:String,age:Int) {
    print("name = \(name), age = \(age)")
}
printPersonInfo("joner", age: 23)

// 3.有返回值, 无参数

// 4.有返回值, 有参数
// 函数类型 (String)->String
func returnPersonName(name:String) ->String {
    return name
}
var returnName =  returnPersonName("joner")
print("returnName = \(returnName)")

// 多个参数多个返回值, 借助元祖
// (String,Int,Float)->(String,Int,Float)为函数类型
func returnPersonInfo(name:String,age:Int, height:Float) ->(String,Int,Float) {
    return (name,age, height)
}
// 接受:
var (name,shaoqiongAge,height) =  returnPersonInfo("shaoqiong", age: 19, height: 160.0)
print("name = \(name),shaoqiongAge = \(shaoqiongAge),height = \(height)")

// *使用函数的注意事项
// 1.参数在默认情况下是 let 类型, 是不能改变的,想改变的话,参数必须使用 var 修饰
func changeBottleHeight(var height:Float) {
    // 高度的改变
    height += 49
}
// 2.
// 封装交换两个值得函数, 想改变-用地址(加&, 关键字- inout), 因为其为值传递
//当想要函数的交换, 影响外部值的时候, 变量的关键字-形参的关键字用 inout(输入输出关键字)
func swapTwoNumber( inout a:Int, inout b:Int) {
    // let tmp:Int = a;
    var tmp:Int = 0
    tmp = a
    a = b
    b = tmp
}
var aa = 49,bb = 94
swapTwoNumber(&aa, b: &bb)
print("aa = \(aa),bb = \(bb)")
2015-07-06 18:01:00 dengxu4495 阅读数 0

Swift高级语法学习总结

1.函数
  1.1 func funcNmae()->(){} 这样就定义了一个函数,它的参数为空,返回值为空,如果有参数和返回值直接写在两个括号里就可以了
  1.2 参数需要指明类型,而如果没有返回值可以不写->(),返回值只需要写返回类型,如果写了返回名称,可以在函数调用后的值用点语法访问
  1.3 在参数名称前可以加上外部参数名,调用的时候就可以带上它了,如果外部参数名和内部参数名相同则可以直接在内部参数名前加#即可
  1.4 如果跟参数设置一个默认值,则swift会自动加上外部参数名,如果不想有在前面用下划线就可以_,如果默认值参数不在最后则不能省略,在传参时传一个下划线进去就可以
  1.5 在最后一个参数后面加...代表是这个参数是可变参数并且类型就是它,参数个数至少是0个,在函数内可以使用for in 这个参数获得 这些参数
  1.6 每个参数前面其实有一个隐藏的关键字let,如果想让参数可变(可赋值)则需要加上var关键字,不过也只是能赋值,因为是值拷贝所以不能修改外部参数的实际值,如果要变成地址传递,需要在参数前加inout 关键字,而实参需要加上& ,
  1.7 swift中函数其实也只是中类型,函数名就是变量名,比如let func1 : () -> () 声明的就是个无参数无返回值的函数类型,所以如果一个函数返回一个函数的话和返回一个普通变量没什么区别
2.闭包
  2.1 闭包代表了一段程序代码,{ (传入参数) -> 返回值的类型 in ... 表达式 ... },函数只是闭包的一个特例
  2.2 闭包可以推断返回类型,所以可以省略->返回值类型,参数类型也可以推到,所以参数类型也不要,括号也可以去掉,如果闭包只有一个表达式直接可以省略掉return关键字,因为我们可以用$0/$1简写参数,所以参数也可以省略掉.
  2.3如果闭包是函数最后一个参数,则可以去掉小括号,直接用大括号里的内容,不过需要大括号紧接着写,叫尾随闭包
  2.4 内层返回函数会捕获外层的变量的值,当内层函数返回后,外层函数的内部变量并不会释放内存,它的变量的值会跟着内部函数的执行而变化
3.枚举
  3.1 用enum CompassPoint{ case North, South , East, West}定义枚举,可以不用逗号,和声明语句一样分开用多个case写就可以了
  3.2 可以用元组来设定枚举对应每项中的值,并可以用switch case 中的值绑定来判断,
  3.3 枚举类型如果初始化为Int类型,它的下一项也会有一个加1的原始值,不过枚举直接复制成string都可以的
4.结构体
  4.1 结构题的属性必须初始化,必须有默认值或者通过构造器init
  4.2 结构体本身是值传递,如果一个结构体赋值给另外一个结构体了也是两份拷贝,互相修改不会有影响
  4.3 如果一个结构体用let声明了,那么它内部的值就不能再做修改了,var声明的结构体才能修改
  4.4 但是class不同,对象的赋值会是同一份引用,修改会影响到另外一个对象,但是let 声明的变量也是不能赋值的,只是能修改它内部的值而已
5.属性(成员变量)
  5.1 结构体/类在初始化的时候成员变量一定要有值,如果你没有给出初始化方法,则默认有一个包含所有必须初始化的的init方法,如果你提供了,默认了就没有了
  5.2 (延迟属性)用let声明的成员变量,就不能再修改了,如果是一个耗时的属性比如值是一个自定义对象,可以加上lazy属性,它只有在用到的时候才会对这个属性做初始化,避免不要的消耗(延迟属性)
  5.3 (计算属性)有的属性是根据其它的属性计算出来的,并不是一个必须的属性,只是让使用的时候方便了些,在属性定义后面加上set/get方法,get方法 需要返回一个值,set方法有一个参数,用来设置其它属性,如果不要参数就不要写参数外面的括号,它就有一个默认的参数newValue
  5.4 如果只有get方法就是只读属性,只读属性swift提供了一种简写方式,直接把返回语句写在最外面的大括号里就可以了
  5.5 swift提供了属性监听方法:willSet和didSet,两个都是一样有一个参数,分别将要设置的值,和属性过去的值,同样你如果不提供参数,会用 两个默认的newValue和oldValue.这两个方法和set/get方法并列,在didSet方法中可以直接修改(调整)属性的值,但是这两个方 法不能和set/get方法共存
  5.6 swift中有类别属性,enum/struct 用关键字static,class就用class关键字,在class中let 声明的需要直接赋初始值,var声明的必须用get方法return,因为swift不允许class储存类属性,而enum/struct可以
6.函数(成员方法)
  6.1 class中的函数都不需要加上外部参数,因为除了第一个参数都默认加上了#符号,不过这只是swift帮你做了点事情而已,它并没有在语法上强制你做什么,你想为第一个参数加上外部参数也行,都用_代替默认的外部参数都可以
  6.2 在swift里self.x不代表会调用setX/getX方法,所以直接可以在set/get方法里这么用.
  6.3 在struct和enum中,成员方法不允许修改成员变量,如果要修改需要加上mutating关键字,但是如果声明的结构体变量是一个let常量的话,这个方法也是不允许调用的.
  6.4 在struct和enum中你可以在mutating方法中直接跟self赋值成另外一个变量
  6.5 在struct和enmu中用static标识一个方法是类方法,而class中用class关键字
7.角标(subscript)
  7.1 重写subscript,类似于subscript(index: Int) -> Int{},里面写set/get方法,和声明变量,根据传参和返回值来确定下标的类型和返回值,重写了这个方法这个对应的类型就可以用角标了.
  7.2 subscript方法参数的个数对应角标的个数,例如两个参数:mar[2,3]
8.继承
  8.1 swift中没有基础类,所有不继承其它类的的类都是基础类,重写父类中init方法,要先调用super的init方法,然后再在后面修改属性的值,访问属性直接用属性名字就可以了,不用用self等.
  8.2 要重写属性和重写方法类似,直接加个override就好了,在重写的set/get方法也可以调用super对应的属性值,或设置值都可以.
  8.3 覆盖了didSet属性监视器就不能再覆盖set/get方法了,跟方法或属性加上final关键字可以防止被子类覆盖
9.初始化(init)
  9.1 init方法和普通方法一样,你需要在init方法中把每个必须要赋值的属性都赋值,否则会出编译错误,init方法会给每个参数加上#,不要它的话可以用_,在方法内部用self访问,也可以不用
  9.2 如果你自定义了init方法,那么swift会不再提供默认的init方法,你可以自己写一个init方法,init方法带不带参数什么都是可以的,自己决定就行
  9.3 如果你想一个init方法中调用另一个init方法,需要加上一个convenience关键字,在这个init方法里就可以调用另一个init方法了
  9.4 在子类继承父类的时候,首先需要初始化子类的成员变量,然后才能调用super的init方法初始化父类的属性,最后可以修改子类和父类的属性,如果这个属性是父类的,在子类中也是用self访问,因为这个属性已经是它自己的了
  9.5 如果子类一个init方法都没有提供,那么子类继承父类所有的构造器,可以用父类的init方法初始化
  9.6 在初始化属性的时候,可以用闭包实现,只要在复制的=后面加上{},你们写return和其它语句,最后在{}后面加一个()表示闭包立刻执行,闭包和属性的set方法是类似的,只是在最开始提供的
10.销毁方法(deinit)
  10.1 deinit方法会在对象销毁的时候调用,可以打印判断它时候销毁
11.内存管理(ARC)
  11.1 可选类型或者普通类型属性只要对一个对象有一个引用,它的这个对象的引用计数就要加1,如果两个对象相互引用就会产生引用循环,所以需要跟其中的一个属性用关键字weak声明为弱引用,就是可以设置为nil
  11.2 一般用weak声明的用可选类型,因为它引用的可能为nil,如果你在调用的时候能确定它是有值的,你可以声明为unowned的普通类型,它的效果是如果你能保证调用这个属性时不为nil时,swift建议用unowned,其它都和weak一样的
12.可选链(Optional Chaining)
  12.1 对于一个可选类型的属性可以用?.和!.来访问,如果确定有值可以用a!.b!.c!.d,如果不确定有值可以用a?.b?.c?.d
13.类型转化
  13.1 可以用 a is b 来判断对象a是否是b类型,返回值是一个boolean类型的值
  13.2 as 可以把普通类型转化,比如double,int,cgfloat之类都可以用它转化
  13.3 可以用 as? 把之类转化成父类,它的结果可以是nil或者转型成功,所以结果是一个可选类型,转化成功后用?.来访问方法或属性,也可以做可选绑定.
  13.4 如果一个对象一定可以转化成功,则可以用as!转化,如果转化不成功会报运行时错误,比如在一个数组中全是animal,但是声明时是anyobject就可以这样用.
14.扩展/协议(categories,extension,protocol)
  14.1 swift的extension没有名称,它是扩展到所有的实体类的,它不可以增加成员属性,但是可以增加计算属性
  14.2 可以在protocol中声明属性和方法,可以定义属性时候是只读的或者是可读可写的
  14.3 可以让一个extension继承一个protocol,在里面实现对应的方法
  14.4 协议的类型是protocol<protcl1,protcl2>
15.泛型
  15.1 可以在func或struct后面跟上<T1,T2>,在参数中就可以声明参数类型为这个类型,完全可以把它看做是一个类型
16.运算符重载
  16.1 在swift中运算符可以重载,方法名就是运算符号,参数的个数和类型是根据它是几目运算符和运算符两边对应的参数类型决定的.

转载于:https://www.cnblogs.com/yaoliang11/p/4625003.html

没有更多推荐了,返回首页