1.关于命名空间
Objective-C 没有命名空间的,在应用开发时,所有的代码和引用的静态库最终都会被编译到同一个域和二进制中。这样的后果是一旦我们有重复的类名的话,就会导致编译时的冲突和失败。为了避免这种事情的发生,Objective-C 的类型一般都会加上两到三个字母的前缀,比如 Apple 保留的 NS和 UI 前缀,各个系统框架的前缀 SK (StoreKit), CG (CoreGraphic) 等。Objective-C 社区的大部分开发者也遵守了这个约定,一般都会将自己名字缩写作为前缀,把类库命名为 AFNetworking 或者 MBProgressHUD 这样。这种做法可以解决部分问题,至少我们在直接引用不同人的库时冲突的概率大大降低了,但是前缀并不意味着不会冲突,有时候我们确实还是会遇到即使使用前缀也仍然相同的情况。另外一种情况是可能你想使用的两个不同的库,分别在它们里面引用了另一个相同的很流行的第三方库,而又没有更改名字。在你分别使用这两个库中的一个时是没有问题的,但是一旦你将这两个库同时加到你的项目中的话,这个大家共用的第三方库就会和自己发生冲突了。
在 Swift中,由于可以使用命名空间了,即使是名字相同的类型,只要是来自不同的命名空间的话,都是可以和平共处的。swift中的命名空间的使用不是一个项目,而是需要跨项目,在一个项目中,都是一个命名空间,在同一个命名空间下,所有全局变量或者函数共享,不需要import,从swift开始,官方更多的建议大家使用pod来管理第三方框架(不然拿进来一个框架,整个项目哪哪都能使)
2.构造函数 以及 override 重写
/**
1.给自己的属性分配空间并且设置初始值
2.调用父类的构造函数,给父类的属性分配空间设置初始值
ONObject 没有属性,只有一个成员变量 �"isa"
3.调用父类的构造函数,给父类的属性分配空间设置初始值
上面两步的操作与OC中是相反的,OC中是先调用父类的,然后在为属性赋值
4.如果重载了构造函数,并且没有实现父类的init方法,系统不再提供init()方法,
*/
class Person: NSObject {
// 'Person' cannot be constructed because it has no accessible initializers
// person类 没有初始化构造器,构造函数,可以有多个,默认是 init
var name: String
// 构造函数不需要写 fun
// override 重写
// 1. 父类存在相同的方法
// 2. 子类重新编写父类方法中的实现
override init() {
// 非 option 属性,都必须在构造函数中设置初始值,从而保证对象在被实例化的时候,属性都被正确初始化,
// 在调用父类的构造函数之前,必须保证本类的属性都已经初始化完成
name = "呵呵呵";
print("Person - init")
super.init()
}
// 重载 函数名相同,但是参数和个数不同
// 重载可以给自己的属性外部设置初始值
// OC是没有重载的, initWithXXX
init(name: String) {
// 使用参数的name 设置给属性
self.name = name
super.init()
}
}
3.遍历构造器
指定构造器是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。
便利构造器是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入值的实例。”
类的构造器代理规则
规则 1
指定构造器必须调用其直接父类的的指定构造器。
指定构造器规则 2
便利构造器必须调用同类中定义的其它构造器。规则 3
便利构造器必须最终导致一个指定构造器被调用。规则4
convenience 的初始化方法是不能被子类重写
一个更方便记忆的方法是:
指定构造器必须总是向上代理
便利构造器必须总是横向代理
4.可失败的构造器
如果一个类、结构体或枚举类型的对象,在构造过程中有可能失败,则为其定义一个可失败构造器。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。
为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在init关关键字后面添加问号(init?)。
注意
可失败构造器的参数名和参数类型,不能与其它非可失败构造器的参数名,及其参数类型相同。
可失败构造器会创建一个类型为自身类型的可选类型的对象。你通过return nil语句来表明可失败构造器在何种情况下应该“失败”。
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
注意
严格来说,构造器都不支持返回值。因为构造器本身的作用,只是为了确保对象能被正确构造。因此你只是用return nil表明可失败构造器构造失败,而不要用关键字return来表明构造成功。
let someCreature = Animal(species: "Giraffe")
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
let someCreature2 = Animal(species: "")
// someCreature 的类型是 Animal? 而不是 Animal
print(someCreature ?? ()) // 输出结果()
guard let giraffe = someCreature else {
print("类型不匹配") // 输出结果 类型不匹配
return
}
- 枚举类型的可失败构造器
enum TemperatureUnit {
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
5.必要构造器
在类的构造器前添加required修饰符表明所有该类的子类都必须实现该构造器:
class SomeClass {
required init() {
// 构造器的实现代码
}
}
在子类重写父类的必要构造器时,必须在子类的构造器前也添加required修饰符,表明该构造器要求也应用于继承链后面的子类。在重写父类中必要的指定构造器时,不需要
添加override
修饰符:
class SomeSubclass: SomeClass {
required init() {
// 构造器的实现代码
}
}
注意
如果子类继承的构造器能满足必要构造器的要求,则无须在子类中显式提供必要构造器的实现。
6.通过闭包或者函数设置属性的默认值
如果某个存储型属性的默认值需要一些定制或设置,你可以使用闭包或全局函数为其提供定制的默认值。每当某个属性所在类型的新实例被创建时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性。
这种类型的闭包或函数通常会创建一个跟属性类型相同的临时变量,然后修改它的值以满足预期的初始状态,最后返回这个临时变量,作为属性的默认值。
class SomeClass {
let someProperty: SomeType = {
// 在这个闭包中给 someProperty 创建一个默认值
// someValue 必须和 SomeType 类型相同
return someValue
}()
}
注意闭包结尾的大括号后面接了一对空的小括号。这用来告诉 Swift 立即执行此闭包。如果你忽略了这对括号,相当于将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。”
注意
如果你使用闭包来初始化属性,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能在闭包里访问其它属性,即使这些属性有默认值。同样,你也不能使用隐式的self属性,或者调用任何实例方法。
7.使用KVC属性赋值,以及后期使用字典转模型框架,我们的模型应该如何定义?
/**
1.定义模型属性的时候,如果是对象,通常定义成可选的
- 在需要的时候创建,避免写构造函数 ,可以简化代码
2.如果是基本数据类型,不能设置成可选的,必须要设置初始值,否者KVC会崩溃
3.使用KVC设置数值的时候,数值不能是private的
4.使用KVC之前,应该调用super.init 保证对象实例化完成
*/
class Person: NSObject {
var name: String?
// 这种写法会报错
// 使用KVC提示无法找到 age这个KEY
// 原因:Int是一个基本数据类型的结构体,OC中没有,OC中只有基本数据类型
// var age: Int?
var age: Int = 0
// this class is not key value coding-compliant for the key title.'
// 因为这个属性是私有的,使用KVC设置值的时候,同样无法设置,会崩溃
// private var title: String?
var title: String?
init(dict: [String: Any]) {
super.init()
// Use of 'self' in method call 'setValuesForKeys' before super.init initializes self
// 使用self的方法 setValuesForKeys 之前,应该调用 super.init方法
// KVC的方法是OC的方法。在运行时给对象发送消息
// 要求对象已经实例化完成
setValuesForKeys(dict)
}
override func setValue(_ value: Any?, forUndefinedKey key: String) {
// 注意这里不能调用父类的方法,不能将父类的代码完全覆盖,这样才不会崩溃
// super.setValue(value, forUndefinedKey: key)
}
}
// 我们没有定义XXX这个key的属性,所以会崩溃,如果解决这个问题,在对象中,重写
// override func setValue(_ value: Any?, forUndefinedKey key: String) 这个方法
let stu2 = Student(dict: ["name": "洋葱", "age": 11, "title": "Boss", "no": "110", "XXX": "yyy"])
print("\(stu2.name ?? "") \(stu2.age) \(stu2.no ?? "")")
4.使用runtime获取对象的属性,这个和OC中的使用姿势基本一致
class Student: Person {
var no: String?
var bithday: String?
var address: String?
var height: Int = 180
class func getpropertiesList() ->[String] {
var count: UInt32 = 0
var arrM: [String] = []
// 获取类的属性列表,返回属性列表的数组,可选项
let list = class_copyPropertyList(self, &count)
print("属性个数:\(count)")
// 遍历数组
for i in 0..
//转换过程:Int8 -> Byte -> Char -> C语言字符串
let cName = property_getName(pty!)
//转换成String的字符串
let name = String(utf8String: cName!)
arrM.append(name!)
print("-----\(name!)")
}
free(list) //释放list
return arrM
}
// 使用guard let来实现
class func propertyList() -> [String] {
var count: UInt32 = 0
var arrM: [String] = []
// 获取类的属性列表,返回属性列表的数组,可选项
let list = class_copyPropertyList(self, &count)
print("属性个数:\(count)")
for i in 0..