Swift小点(3)
随机数生成
由于iPhoen5和之前的手机是32位CPU,arc4random 所返回的值不论在什么平台上都是一个 UInt32 ,于是在 32 位的平 台上就有一半几率在进行 Int 转换时越界 故使用func arc4random_uniform(_: UInt32) -> UInt32
let diceFaceCount: UInt32 = 6
let randomRoll = Int(arc4random_uniform(diceFaceCount)) + 1
print(randomRoll)
func random(in range: Range<Int>) -> Int {
let count = UInt32(range.endIndex - range.startIndex)
return Int(arc4random_uniform(count)) + range.startIndex
}
for _ in 0...100 {
let range = Range<Int>(1...6)
print(random(in: range))
}
print 和 debugPrint
extension Meeting: CustomStringConvertible {
var description: String {
return "于 \(self.date) 在 \(self.place) 与 \(self.attendeeName) 进行会议"
}
}
1.CustomDebugStringConvertible仅发生在调试中使用debugger来进行打印的时候的输出
2.对于实现了CustomDebugStringConvertible协议的类型 可以在打断点并在控制台使用po 对象的命令进行打印
错误和异常处理
do {
try d.write(toFile: "Hello", options: [])
} catch let error as NSError {
print ("Error: \(error.domain)")
}
enum LoginError: Error {
case UserNotFound, UserPasswordNotMatch
}
func login(user: String, password: String) throws {
//users 是 [String: String],存储[用户名:密码]
if !users.keys.contains(user) {
throw LoginError.UserNotFound
}
if users[user] != password {
throw LoginError.UserPasswordNotMatch
}
print("Login successfully.")
}
断言
func convertToKelvin(_ celsius: Double) -> Double {
assert(celsius > -273.15, "输入的摄氏温度不能低于绝对零度")
return celsius - (-273.15)
}
image.png断言是开发时的特性 只有在Debug编译的时候有效,而在运行时是不被编译执行的,因此断言并不会消耗运行时的性能
可以强制禁用断言
fatalError
class MyClass {
func methodMustBeImplementedInSubclass() {
fatalError("这个方法必须在子类中被重写")
}
}
class YourClass: MyClass {
override func methodMustBeImplementedInSubclass() {
print("YourClass 实现了该方法")
}
}
class TheirClass: MyClass {
func someOtherMethod() {
}
}
YourClass().methodMustBeImplementedInSubclass()
// YourClass 实现了该方法
TheirClass().methodMustBeImplementedInSubclass()
JSON 和 Codable
struct Obj: Codable {
let menu: Menu
struct Menu: Codable {
let id: String
let value: String
let popup: Popup
}
struct Popup: Codable {
let menuItem: [MenuItem]
enum CodingKeys: String, CodingKey {
case menuItem = "menuitem"
}
}
struct MenuItem: Codable {
let value: String
let onClick: String
enum CodingKeys: String, CodingKey {
case value
case onClick = "onclick"
}
}
}
1.只有一个类型中所有成员都实现了Codable 那么这个类型就自动满足Codable的要求
2.如果Json中的key和类型中的变量名不一致 要在对应类中声明CodingKeys枚举,并用合适的键值覆盖对应的默认值
let data = jsonString.data(using: .utf8)!
do {
let obj = try JSONDecoder().decode(Obj.self, from: data)
let value = obj.menu.popup.menuItem[0].value
print(value)
} catch {
print("出错啦:\(error)")
}
NSNull
NSNull 会默默的被通过Optional Binding被转换为nil 避免被执行
// 假设 jsonValue 是从一个 JSON 中取出的 NSNull
let jsonValue: AnyObject = NSNull()
if let string = jsonValue as? String {
print(string.hasPrefix("a"))
} else {
print("不能解析")
}
// 输出: // 不能解析
属性访问控制
当希望读取到这个类的属性 但是要保证类型的封装和安全 只能在类型内部对其进行改变或者设置
class MyClass {
private(set) var name: String?
}
泛型扩展
extension Array {
var random: Element? {
return self.count != 0 ? self[Int(arc4random_uniform(UInt32(self.count)))] :nil
}
}
let languages = ["Swift","ObjC","C++","Java"]
languages.random!
// 随机输出是这四个字符串中的某个
let ranks = [1,2,3,4]
ranks.random!
// 随机输出是这四个数字中的某个
尾递归
func sum(_ n: UInt) -> UInt {
if n == 0 {
return 0
}
return n + sum(n - 1)
}
sum(4) // 10
sum(100) // 5050
当输入的数字过大时 就会出现错误 是因为每次对于sum的递归调用都需要在调用栈上保存当前状态,否则无法计算最后的值,当n足够大,调用栈足够深的时候,栈空间被耗尽而导致错误 栈溢出
使用尾递归的方式解决该问题 尾递归就是让函数里的最后一个动作是一个函数调用的形式,这个调用的返回值将直接被当前函数返回 从而避免在栈上保存状态 这样就是更新最后的栈帧 不是新建一个 来避免栈溢出的发生
func tailSum(_ n: UInt) -> UInt {
func sumInternal(_ n: UInt, current: UInt) -> UInt {
if n == 0 {
return current
} else {
return sumInternal(n - 1, current: current + n)
}
}
return sumInternal(n, current: 0)
}
tailSum(1000000)
由于默认Debug模式下Swift编译器不会对尾递归进行优化 可以改为Relese模式 再运行