注册

iOS - swift常用的关键词解释和用法

deinit: 当一个类的实例即将被销毁时,会调用这个方法。

class Person  
{
var name:String
var age:Int
var gender:String

deinit
{
//从堆中释放,并释放的资源
}
}

extension:允许给已有的类、结构体、枚举、协议类型,添加新功能。

class Person  
{
var name:String = ""
var age:Int = 0
var gender:String = ""
}

extension Person
{
func printInfo()
{
print("My name is \(name), I'm \(age) years old and I'm a \(gender).")
}
}

inout:将一个值传入函数,并可以被函数修改,然后将值传回到调用处,来替换初始值。适用于引用类型和值类型。 其实就是声明参数为指针

func dangerousOp(_ error:inout NSError?)  
{
error = NSError(domain: "", code: 0, userInfo: ["":""])
}

var potentialError:NSError?
dangerousOp(&potentialError)

//代码运行到这里,potentialError 不再是 nil,而是已经被初始化

internal:访问控制权限,允许同一个模块下的所有源文件访问,如果在不同模块下则不允许访问。


public:可以被任何人访问。但其他 module 中不可以被 override 和继承,而在 module 内可以被 override 和继承。


open:访问控制权限,允许在定义的模块外也可以访问源文件里的所有类,并进行子类化。对于类成员,允许在定义的模块之外访问和重写。

open var foo:String? //这个属性允许在 app 内或 app 外重写和访问。在开发框架的时候,会应用到这个访问修饰符。

private:访问控制权限,只允许实体在定义的类以及相同源文件内的 extension 中访问。

class Person  
{
private var jobTitle:String = ""
}

// 当 extension 和 class 不在同一个源文件时
extension Person
{
// 无法编译通过,只有在同一个源文件下才可以访问
func printJobTitle()
{
print("My job is \(jobTitle)")
}
}

fileprivate:访问控制权限,只允许在定义源文件中访问。// 同文件中访问

class Person  
{
fileprivate var jobTitle:String = ""
}

extension Person
{
//当 extension 和 class 在同一个文件中时,允许访问
func printJobTitle()
{
print("My job is (jobTitle)")
}
}


从高到低排序如下:

open > public > interal > fileprivate > private



static:用于定义类方法,在类型本身进行调用。此外还可以定义静态成员。

class Person  
{
var jobTitle:String?

static func assignRandomName(_ aPerson:Person)
{
aPerson.jobTitle = "Some random job"
}
}

let somePerson = Person()
Person.assignRandomName(somePerson)
//somePerson.jobTitle 的值是 "Some random job"


在方法的 func 关键字之前加上关键字 static 或者 class 都可以用于指定类方法.不同的是用class关键字指定的类方法可以被子类重写, 如下:

override class func work() { print("Teacher: University Teacher")}

但是用 static 关键字指定的类方法是不能被子类重写的, 根据报错信息: Class method overrides a 'final' class method.



我们可以知道被 static 指定的类方法包含 final 关键字的特性--防止被重写.


struct:通用、灵活的结构体,是程序的基础组成部分,并提供了默认初始化方法。与 class 不同,当 struct 在代码中被传递时,是被拷贝的,并不使用引用计数。除此之外,struct 没有下面的这些功能:



  • 使用继承。
  • 运行时的类型转换。
  • 使用析构方法。


数据类型:struct是值类型,class是引用类型。

值类型变量直接包含数据,赋值时也是值拷贝,或者叫深拷贝,所以多个变量的操作不会相互影响。

引用类型变量存储的是对数据的引用地址,后者称为对象,赋值时,是将对象的引用地址复制过去,也叫浅拷贝,因此若多个变量指向同一个对象时,操作会相互影响。

值类型数据没有引用计数,也就不会因为循环引用导致内存泄漏,而引用类型存在引用计数,需要小心循环引用导致的内存泄漏

拷贝时,struct是深拷贝,拷贝的是内容,class则需要选用正确的深浅拷贝类型。



因为值类型数据是深拷贝,所以是线程安全的,而引用类型数据则不是



  • property的初始化:初始化属性时,class 需要创建一个带形参的constructor;struct可以把属性放在默认的constructor 的参数里。
  • immutable变量:swift用var和let区分可变数据和不可变数据,struct遵循这个特性;对class则不适用。
  • mutating function:struct 的 function 改变 property 时,需加上 mutating,而 class 不用。
  • 速度:struct分配在栈中,class分配在堆中,也就意味着struct更迅速。
  • NSUserDefaults:struct 不能被序列化成 NSData 对象,class可以。
  • 继承: struct不可以继承,class可以继承。
  • swift与oc混合开发时,oc调用swift需要继承NSObject,这就导致了class可以继承,所以可以调用class,但struct不能继承,所以不能调用struct

typealias:给代码中已经存在的类,取别名。

typealias JSONDictionary = [String: AnyObject]

func parseJSON(_ deserializedData:JSONDictionary){}

defer:用于在程序离开当前作用域之前,执行一段代码。 // dafer 是倒叙 先加入后执行




  • 关闭文件

    func foo() {
    let fileDescriptor = open(url.path, O_EVTONLY)
    defer {
    close(fileDescriptor)
    }
    // use fileDescriptor...
    }



  • 加/解锁:下面是 swift 里类似 Objective-C 的 synchronized block 的一种写法,可以使用任何一个 NSObject 作 lock

    func foo() {
    objc_sync_enter(lock)
    defer {
    print("003")
    objc_sync_exit(lock)
    }
    defer {
    print("002")
    }
    print("001")
    // do something...
    }



defer 的执行时机:

defer 的执行时机紧接在离开作用域之后,但是是在其他语句之前。这个特性为 defer 带来了一些很“微妙”的使用方式。比如从 0 开始的自增:

class Foo {
var num = 0
func foo() -> Int {
defer { num += 1 }
return num
}

// 没有 `defer` 的话我们可能要这么写
// func foo() -> Int {
// num += 1
// return num - 1
// }
}

let f = Foo()
f.foo() // 0
f.foo() // 1
f.num // 2

fallthrough:显式地允许从当前 case 跳转到下一个相邻 case 继续执行代码。

let box = 1

switch box
{
case 0:
print("Box equals 0")
fallthrough
case 1:
print("Box equals 0 or 1")
default:
print("Box doesn't equal 0 or 1")
// Box equals 0 和 Box equals 0 or 1 都执行了
}

guard:当有一个以上的条件不满足要求时,将离开当前作用域。同时还提供解包可选类型的功能。

private func printRecordFromLastName(userLastName: String?)
{
guard let name = userLastName, name != "Null" else
{
//userLastName = "Null",需要提前退出
return
}
//继续执行代码
print(dataStore.findByLastName(name))
}


1.guard关键字必须使用在函数中。

2.guard关键字必须和else同时出现。

3.guard关键字只有条件为false的时候才能走else语句 相反执行后边语句。



repeat:在使用循环的判断条件之前,先执行一次循环中的代码。类似于 do while 循环


repeat

{

print("Always executes at least once before the condition is considered")

}

while 1 > 2


where:要求关联类型必须遵守特定协议,或者类型参数和关联类型必须保持一致。也可以用于在 case 中提供额外条件,用于满足控制表达式。



  • 增加判断条件
for i in 0…3 where i % 2 == 0  
{
print(i) //打印 0 和 2
}


  • 协议使用where, 只有基类实现了当前协议才能添加扩展。 换个说法, 多个类实现了同一个协议,该语法根据类名分别为这些类添加扩展, 注意是分别(以类名区分)!!!
protocol SomeProtocol {
func someMethod()
}
class A: SomeProtocol {
let a = 1
func someMethod() {
print("call someMethod")
}
}
class B {
let a = 2
}
//基类A继承了SomeProtocol协议才能添加扩展
extension SomeProtocol where Self: A {
func showParamA() {
print(self.a)
}
}
//反例,不符合where条件
extension SomeProtocol where Self: B {
func showParamA() {
print(self.a)
}
}
let objA = A()
let objB = B() //类B没实现SomeProtocol, 所有没有协议方法
objA.showParamA() //输出1

as:类型转换运算符,用于尝试将值转成其它类型。




  • as : 数值类型转换

    let age = 28 as Int
    let money = 20 as CGFloat
    let cost = (50 / 2) as Double
    switch person1 { 
    case let person1 as Student:
    print("是Student类型,打印学生成绩单...")
    case let person1 as Teacher:
    print("是Teacher类型,打印老师工资单...")
    default: break
    }



  • as!:向下转型(Downcasting)时使用。由于是强制类型转换,如果转换失败会报 runtime 运行错误。

    let a = 13 as! String
    print(a)
    //会crash
    let a = 13 as? String
    print(a)
    //输出为nil



is:类型检查运算符,用于确定实例是否为某个子类类型。

class Person {}  
class Programmer : Person {}
class Nurse : Person {}

let people = [Programmer(), Nurse()]

for aPerson in people
{
if aPerson is Programmer
{
print("This person is a dev")
}
else if aPerson is Nurse
{
print("This person is a nurse")
}
}

nil:在 Swift 中表示任意类型的无状态值。



Swift的nil和OC中的nil不一样.在OC中,nil是一个指向不存在对象的指针.而在Swift中,nil不是指针,它是一个不确定的值.用来表示值缺失.任何类型的optional都可以被设置为nil. 而在OC中,基本数据类型和结构体是不能被设置为nil的. 给optional的常量或者变量赋值为nil.来表示他们的值缺失情况.一个optional常量或者变量如果在初始化的时候没有被赋值,他们自动会设置成nil.

class Person{}  
struct Place{}

//任何 Swift 类型或实例可以为 nil
var statelessPerson:Person? = nil
var statelessPlace:Place? = nil
var statelessInt:Int? = nil
var statelessString:String? = nil

super:在子类中,暴露父类的方法、属性、下标。

class Person  
{
func printName()
{
print("Printing a name. ")
}
}

class Programmer : Person
{
override func printName()
{
super.printName()
print("Hello World!")
}
}

let aDev = Programmer()
aDev.printName() //打印 Printing a name. Hello World!

self:任何类型的实例都拥有的隐式属性,等同于实例本身。此外还可以用于区分函数参数和成员属性名称相同的情况。

class Person  
{
func printSelf()
{
print("This is me: \(self)")
}
}

let aPerson = Person()
aPerson.printSelf() //打印 "This is me: Person"

Self:在协议中,表示遵守当前协议的实体类型。

protocol Printable  
{
func printTypeTwice(otherMe:Self)
}

struct Foo : Printable
{
func printTypeTwice(otherMe: Foo)
{
print("I am me plus \(otherMe)")
}
}

let aFoo = Foo()
let anotherFoo = Foo()

aFoo.printTypeTwice(otherMe: anotherFoo) //打印 I am me plus Foo()

_:用于匹配或省略任意值的通配符。

for _ in 0..<3  
{
print("Just loop 3 times, index has no meaning")
}

另外一种用法:

let _ = Singleton() //忽略不使用的变量

convenience:


在 Swift 中,为保证安全性,init 方法只能调用一次,且在 init 完成后,保证所有非 Optional 的属性都已经被初始化。


每个类都有指定的初始化方法:designated initializer,这些初始化方法是子类必须调用的,为的就是保证父类的属性都初始化完成了。


而如果不想实现父类的 designated initializer,可以添加 convenience 关键字,自己实现初始化逻辑。

convenience 初始化不能调用父类的初始化方法,只能调用同一个类中的 designated initializer。

由于 convenience 初始化不安全,所以 Swift 不允许 convenience initializer 被子类重写,限制其作用范围。

class People {
var name: String
init(name: String) {
self.name = name
}
}

通过extension给原有的People类增加init方法:

// 使用convenience增加init方法
extension People {
convenience init(smallName: String) {
self.init(name: smallName)
}
}

接下来,Student类继承父类People

class Student: People {
var grade: Int

init(name: String, grade: Int) {
self.grade = grade
super.init(name: name)
// 无法调用
// super.init(smallName: name)
}

// 可以被重写
override init(name: String) {
grade = 1
super.init(name: name)
}

// 无法重写,编译不通过
override init(smallName: String) {
grade = 1
super.init(smallName: smallName)
}
}

子类对象调用父类的convenience的init方法:只要在子类中实现重写了父类convenience方法所需要的init方法的话,我们在子类中就可以使用父类的convenience初始化方法了

class People {

var name: String

init(name: String) {
self.name = name
}
}
// 使用convenience增加init方法
extension People {
convenience init(smallName: String) {
self.init(name: smallName)
}
}


// 子类
class Teacher: People {

var course: String

init(name: String, course: String) {
self.course = course
super.init(name: name)
}

override init(name: String) {
self.course = "math"
super.init(name: name)
}
}

// 调用convenience的init方法
let xiaoming = Teacher(smallName: "xiaoming")


  • 总结:子类的designated初始化方法必须调用父类的designated方法,以保证父类也完成初始化。

required


对于某些我们希望子类中一定实现的designated初始化方法,我们可以通过添加required关键字进行限制,强制子类对这个方法重写。

required修饰符的使用规则:



  • required修饰符只能用于修饰类初始化方法。
  • 当子类含有异于父类的初始化方法时(初始化方法参数类型和数量异于父类),子类必须要实现父类的required初始化方法,并且也要使用required修饰符而不是override。
  • 当子类没有初始化方法时,可以不用实现父类的required初始化方法。
    class MyClass {
var str:String
required init(str:String) {
self.str = str
}
}
class MySubClass:MyClass
{
init(i:Int) {
super.init(str:String(i))
}

}
// 编译错误
MySubClass(i: 123)

会报错,因为你没有实现父类中必须实现的方法。正确的写法:


    class MyClass {
var str:String
required init(str:String) {
self.str = str
}
}
class MySubClass:MyClass
{
init(i:Int) {
super.init(str:String(i))
}
required init(str: String) {
fatalError("init(str:) has not been implemented")
}
}
// 编译错误
MySubClass(i: 123)

从上面的代码中,不难看出子类需要添加异于父类的初始化方法,必须要重写有required的修饰符的初始化方法,并且也要使用required修饰符而不是override,请千万注意!


如果子类中并没有不同于父类的初始化方法,Swift会默认使用父类的初始化方法:

class MyClass{
var str: String?
required init(str: String?) {
self.str = str
}
}
class MySubClass: MyClass{
}
var MySubClass(str: "hello swift")

在这种情况下,编译器不会报错,因为如果子类没有任何初始化方法时,Swift会默认使用父类的初始化方法。


以#开头的关键字


#available:基于平台参数,通过 if,while,guard 语句的条件,在运行时检查 API 的可用性。

if #available(iOS 10, *)  
{
print("iOS 10 APIs are available")
}

#colorLiteral:在 playground 中使用的字面表达式,用于创建颜色选取器,选取后赋值给变量。

let aColor = #colorLiteral //创建颜色选取器

#column:一种特殊的字面量表达式,用于获取字面量表示式的起始列数。

class Person  
{
func printInfo()
{
print("Some person info - on column \(#column)")
}
}

let aPerson = Person()
aPerson.printInfo() //Some person info - on column 47

#function:特殊字面量表达式,返回函数名称。在方法中,返回方法名。在属性的 getter 或者 setter 中,返回属性名。在特殊的成员中,比如 init 或 subscript 中,返回关键字名称。在文件的最顶层时,返回当前所在模块名称。

class Person
{
func printInfo()
{
print("Some person info - inside function \(#function)")
}
}

let aPerson = Person()
aPerson.printInfo() //Some person info - inside function printInfo()

#line:特殊字面量表达式,用于获取当前代码的行数。

class Person  
{
func printInfo()
{
print("Some person info - on line number \(#line)")
}
}

let aPerson = Person()
aPerson.printInfo() //Some person info - on line number 5

#selector:用于创建 Objective-C selector 的表达式,可以静态检查方法是否存在,并暴露给 Objective-C。

//静态检查,确保 doAnObjCMethod 方法存在  
control.sendAction(#selector(doAnObjCMethod), to: target, forEvent: event)

dynamic && @objc


@objc


OC 是基于运行时,遵循了 KVC 和动态派发,而 Swift 为了追求性能,在编译时就已经确定,而不需要在运行时的,在 Swift 类型文件中,为了解决这个问题,需要暴露给 OC 使用的任何地方(类,属性,方法等)的生命前面加上 @objc 修饰符

如果用 Swift 写的 class 是继承 NSObject 的话, Swift 会默认自动为所有非 private 的类和成员加上@objc


在Swift中,我们在给button添加点击事件时,对应的点击事件的触发方法就需要用@objc来修饰


dynamic


Swift 中的函数可以是静态调用,静态调用会更快。当函数是静态调用的时候,就不能从字符串查找到对应的方法地址了。这样 Swift 跟 Objective-C 交互时,Objective-C 动态查找方法地址,就有可能找不到 Swift 中定义的方法。


这样就需要在 Swift 中添加一个提示关键字,告诉编译器这个方法是可能被动态调用的,需要将其添加到查找表中。这个就是关键字 dynamic 的作用。


didSet


属性观察者,当值存储到属性后马上调用。

var data = [1,2,3]  
{
didSet
{
tableView.reloadData()
}
}

final


防止方法、属性、下标被重写。

final class Person {}  
class Programmer : Person {} //编译错误


get


返回成员的值。还可以用在计算型属性上,间接获取其它属性的值。

class Person  
{
var name:String
{
get { return self.name }
set { self.name = newValue}
}

var indirectSetName:String
{
get
{
if let aFullTitle = self.fullTitle
{
return aFullTitle
}
return ""
}

set (newTitle)
{
//如果没有定义 newTitle,可以使用 newValue
self.fullTitle = "(self.name) :(newTitle)"
}
}
}


infix


指明一个用于两个值之间的运算符。如果一个全新的全局运算符被定义为 infix,还需要指定优先级。

let twoIntsAdded = 2 + 3


indirect


指明在枚举类型中,存在成员使用相同枚举类型的实例作为关联值的情况。

indirect enum Entertainment  
{
case eventType(String)
case oneEvent(Entertainment)
case twoEvents(Entertainment, Entertainment)
}

let dinner = Entertainment.eventType("Dinner")
let movie = Entertainment.eventType("Movie")

let dateNight = Entertainment.twoEvents(dinner, movie)


lazy


指明属性的初始值,直到第一次被使用时,才进行初始化。

class Person  
{
lazy var personalityTraits = {
//昂贵的数据库开销
return ["Nice", "Funny"]
}()
}
let aPerson = Person()
aPerson.personalityTraits //当 personalityTraits 首次被访问时,数据库才开始工作

left


指明运算符的结合性是从左到右。在没有使用大括号时,可以用于正确判断同一优先级运算符的执行顺序。

//"-" 运算符的结合性是从左到右
10-2-4 //根据结合性,可以看做 (10-2) - 4


mutating


允许在方法中修改结构体或者枚举实例的属性值。

struct Person  
{
var job = ""

mutating func assignJob(newJob:String)
{
self = Person(job: newJob)
}
}

var aPerson = Person()
aPerson.job //""

aPerson.assignJob(newJob: "iOS Engineer at Buffer")
aPerson.job //iOS Engineer at Buffer


none


是一个没有结合性的运算符。不允许这样的运算符相邻出现。

//"<" 是非结合性的运算符
1 < 2 < 3 //编译失败


nonmutating


指明成员的 setter 方法不会修改实例的值,但可能会有其它后果。

enum Paygrade  
{
case Junior, Middle, Senior, Master

var experiencePay:String?
{
get
{
database.payForGrade(String(describing:self))
}

nonmutating set
{
if let newPay = newValue
{
database.editPayForGrade(String(describing:self), newSalary:newPay)
}
}
}
}

let currentPay = Paygrade.Middle

//将 Middle pay 更新为 45k, 但不会修改 experiencePay 值
currentPay.experiencePay = "$45,000"

optional


用于指明协议中的可选方法。遵守该协议的实体类可以不实现这个方法。

@objc protocol Foo  
{
func requiredFunction()
@objc optional func optionalFunction()
}

class Person : Foo
{
func requiredFunction()
{
print("Conformance is now valid")
}
}

override


指明子类会提供自定义实现,覆盖父类的实例方法、类型方法、实例属性、类型属性、下标。如果没有实现,则会直接继承自父类。

class Person  
{
func printInfo()
{
print("I'm just a person!")
}
}

class Programmer : Person
{
override func printInfo()
{
print("I'm a person who is a dev!")
}
}

let aPerson = Person()
let aDev = Programmer()

aPerson.printInfo() //打印 I'm just a person!
aDev.printInfo() //打印 I'm a person who is a dev!

postfix


位于值后面的运算符。

var optionalStr:String? = "Optional"  
print(optionalStr!)

precedence


指明某个运算符的优先级高于别的运算符,从而被优先使用。

infix operator ~ { associativity right precedence 140 }  
4 ~ 8

prefix


位于值前面的运算符。


var anInt = 2  
anInt = -anInt //anInt 等于 -2


required


确保编译器会检查该类的所有子类,全部实现了指定的构造器方法。

class Person  
{
var name:String?

required init(_ name:String)
{
self.name = name
}
}

class Programmer : Person
{
//如果不实现这个方法,编译不会通过
required init(_ name: String)
{
super.init(name)
}
}

right


指明运算符的结合性是从右到左的。在没有使用大括号时,可以用于正确判断同一优先级运算符的顺序。

//"??" 运算符结合性是从右到左
var box:Int?
var sol:Int? = 2

let foo:Int = box ?? sol ?? 0 //Foo 等于 2


set


通过获取的新值来设置成员的值。同样可以用于计算型属性来间接设置其它属性。如果计算型属性的 setter 没有定义新值的名称,可以使用默认的 newValue。

class Person  
{
var name:String
{
get { return self.name }
set { self.name = newValue}
}

var indirectSetName:String
{
get
{
if let aFullTitle = self.fullTitle
{
return aFullTitle
}
return ""
}

set (newTitle)
{
//如果没有定义 newTitle,可以使用 newValue
self.fullTitle = "(self.name) :(newTitle)"
}
}
}


Type


表示任意类型的类型,包括类类型、结构类型、枚举类型、协议类型。

class Person {}  
class Programmer : Person {}

let aDev:Programmer.Type = Programmer.self

unowned


让循环引用中的实例 A 不要强引用实例 B。前提条件是实例 B 的生命周期要长于 A 实例。

class Person  
{
var occupation:Job?
}

//当 Person 实例不存在时,job 也不会存在。job 的生命周期取决于持有它的 Person。
class Job
{
unowned let employee:Person

init(with employee:Person)
{
self.employee = employee
}
}

weak


允许循环引用中的实例 A 弱引用实例 B ,而不是强引用。实例 B 的生命周期更短,并会被先释放。

class Person  
{
var residence:House?
}

class House
{
weak var occupant:Person?
}

var me:Person? = Person()
var myHome:House? = House()

me!.residence = myHome
myHome!.occupant = me

me = nil
myHome!.occupant // myHome 等于 nil


willSet


属性观察者,在值存储到属性之前调用。

class Person  
{
var name:String?
{
willSet(newValue) {print("I've got a new name, it's (newValue)!")}
}
}

let aPerson = Person()
aPerson.name = "Jordan" //在赋值之前,打印 "I've got a new name, it's Jordan!"



链接:https://www.jianshu.com/p/46cf5c77dee7

0 个评论

要回复文章请先登录注册