Swift快速上手笔记
可选项
Optional可以让我们摆脱很多不必要的判断和取值,声明变量在类型后面加上?
的语法
class Toy {
let name: String
init(name: String) {
self.name = name
}
}
class Pet {
var toy: Toy?
}
class Child {
var pet: Pet?
}
if let toyName = xiaoming.pet?.toy?.name {
// 通过多级守护安全取到值
}
还有很多高级用法,后面慢慢研究。
if let & guard let
用于守护操作
// MARK: - if let / gucard let 用法
func ifletDemo() {
let i:Int? = 10
if let a = i {
// 此处a 解包了i,如果a解包是nil则不成立
print(a)
} else {
print("i is nil")
}
// 此处使用guard 减少逻辑分支层级,a在后续使用已经被解包
guard let a = i else {
print("i is nil")
return
}
print(a)
}
switch
swift 的switch比OC要灵活很多,支持多种数据类型,Int、String、浮点,结构体, 并且支持使用元组值绑定,结构体值绑定。
let n = 10
switch n {
case 1:
print("\(n)")
case 10:
print("\(n)")
default:
print("default")
}
// 多种类型, 字符串、Bool、结构体、浮点
var str = "my"
switch str {
case "my":
print("case my")
fallthrough
case "d":
print("1") //被输出
default:
print("default")
}
// 运算区间
switch n {
case 0 ..< 5 :
print("0~5")
case 5 ... 10 :
print("5~10")
default:
print("default")
}
// Value Binding 值绑定,适合和enum合用
let request = (6,"success")
switch request {
case (let errorCode, let state):
print("\(state),\(errorCode)")
case (let errorCode, _):
"error code is \(errorCode)"
}
// enum 结构值绑定
enum Result {
case Success(json: String, isSuccess: Bool)
case Fail(error: String)
}
let r = Result.Success(json: "{}", isSuccess: true)
switch r {
case Result.Success(let json, let isSuccess):
print("\(json)")
default:
print("default")
}
for
swift 3.0 已经去掉了 C 语言风格的for循环。
let array = [1, 12, 9, 4]
// 遍历数组
for i in array {
print(i)
}
// 条件区间
for i in 0...10 {
print(i)
}
字符串
swift 中的字符串操作,如果遇到不方便操作的时候记得转为NSString
// 遍历字符串
for c in str.characters {
print(c)
}
// 反序
for c in str.characters.reversed() {
print(c)
}
// 转换成NSString
let ocStr = str as NSString
// 字符串长度
print(ocStr.length)
// 字节长度
print(str.lengthOfBytes(using: .utf8))
// 截取字符串
let substr = ocStr.substring(with: NSMakeRange(0, 5))
print(substr)
数组
// MARK: -遍历数组
func arrayDemo() {
let myArray = [1, 2, 3]
// 遍历
// 元组1
print("-----遍历数组1....")
for e in myArray.enumerated() {
print("\(e.offset), \(e.element)")
}
// 元组2
print("-----遍历数组2....")
for (of, el) in myArray.enumerated() {
print("\(of),\(el)")
}
// 直接遍历
print("-----遍历数组3....")
for item in myArray {
print(item)
}
// 反转数组
print("-----反转数组....")
for item in myArray.reversed() {
print(item)
}
print("-----")
for (of, el) in myArray.enumerated().reversed() {
print("\(of),\(el)")
}
}
// MARK: - 数组拼接
func arrayAppendDemo() {
print("------数组拼接")
var array = ["1", "test"]
print(array)
// 数组添加元素
array.append("x")
let array1 = ["a", "b", "c"]
array += array1
print(array)
print("------")
// 数组的拼接需要在同类型下进行
var array2: [Any] = [2, "tt"] as [Any]
let array3: [Any] = ["a", "b", "c"]
array2 += array3
print(array2)
}
字典
// 字典遍历
func demo() {
// Swift3.1 必须要转为一个确定的类型 as [String:Any]
let dic = ["name":"alienjunx", "age":20] as [String:Any]
print(dic)
// 直接遍历
for (k,v) in dic {
print("\(k),\(v)")
}
print("-----")
for item in dic {
print("\(item.key),\(item.value)")
}
}
// 合并
func demo2() {
print("-----合并")
var dic1 = ["name":"alienjunx", "age":20] as [String:Any]
print(dic1)
let dic2 = ["height":90, "name":"老万"] as [String:Any]
print(dic2)
for (k,v) in dic2 {
dic1[k] = dic2[k]
}
print(dic1)
}
// 删除
func removeDemo() {
print("-----删除")
var dic1 = ["name":"alienjunx", "age":20] as [String:Any]
print(dic1)
dic1.removeValue(forKey: "name")
print(dic1)
}
函数
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
// 函数返回一个String 类型
func myFunc() -> String {
let i = "myFunc"
return i
}
// 函数返回一个闭包
func loadMyName(name: String) -> ((String)->()) {
print("\(name)")
let result: ((String)->()) = {(rname: String)->() in
print("\(rname),\(name)")
}
return result
}
loadMyName(name: "test")("my")
// 异步处理回调函数
// 参数的闭包需要进行声明 @escaping 逃逸闭包
func funcCallBack(completion: @escaping (_ json: String)->()) {
DispatchQueue.global().async {
//// 耗时处理
sleep(2)
// 主线程回调
DispatchQueue.main.async(execute: {
completion("ok")
})
}
}
funcCallBack { (resultStr) in
print(resultStr)
}
闭包
与OC中的block一样,都是一段提前准备好的代码,通常用于参数传递进行处理回调结果
// closure 是 '() -> ()' 一个闭包的类型
// '=' 后面是实现这闭包 '闭包声明 in 实现'
let closure: () -> () = {() -> () in
print("closure")
}
/*
1.如果闭包是无参数无返回,则实现时可以省略 in和前面的声明
2.按照swift的类型推导,closure1的所属类型也可以不显示声明
*/
let closure1 = {
print("closure1")
}
// 调用闭包
closure()
// 有参数的闭包
let closure2 = { (name: String) -> () in
print(name)
}
closure2("alienjunx")
//---------------------------
// 函数中传递闭包作为回调
closureFunc(callBack: { (name) -> () in
print(name)
})
// 如果函数的最后一个参数是闭包,可以使用尾随闭包的方式简写
closureFunc { (name) in
print(name)
}
// callBack 是一个 (String) -> () 类型的闭包
func closureFunc(callBack:(String) -> ()) {
let myName = "testName"
callBack(myName)
}
??、?、!
let i:Int? = 10
// ?? 操作符,用于解包,如果Option中是nil,则返回 后面的值
let num = i ?? 0
问号?
-- 声明时添加?,告诉编译器这个是Optional的,如果声明时没有手动初始化,就自动初始化为nil
-- 在对变量值操作前添加?,判断如果变量时nil,则不响应后面的方法。
叹号!
-- 声明时添加!,告诉编译器这个是Optional的,并且之后对该变量操作的时候,都隐式的在操作前添加!
-- 在对变量操作前添加!,表示默认为非nil,直接解包进行处理
构造函数
构造函数在swift中都是以init命名
override init() {
name = ""
// 会隐式调用super.init()
}
init(name: String) {
self.name = name
// 会隐式调用super.init()
}
重写
只要父类提供了可重写的函数,那么在子类中可以重新实现此函数,在swift中
-- 子类先初始化后父类才初始化,这与oc完全是不一样的
class Person: NSObject {
var name: String
init(name: String) {
self.name = name
// 可不写super.init() 会隐式调用
}
}
// 继承于Person
class Student: Person {
var no: Int = 0
// 重写父类构造函数
override init(name: String) {
super.init(name: name)
self.no = 10
}
}
let s = Student(name: "小明")
print("\(s.name) \(s.no)")
重载
函数名相同,参数类型和个数不同,重载不同的参数来满足不同的初始化操作,在OC中构造函数重载用的非常多。
class Person: NSObject {
var name: String
var title: String?
// 构造方法1
init(name: String) {
self.name = name
}
// 构造方法2
init(name: String, title: String?) {
self.name = name
self.title = title
}
}
便捷构造函数
作为构造函数的一种补充方式。 本身不负责对象的创建和初始化工作,只是负责一些值的设置,并且可以通过条件判断,进行是否需要初始化,防止不必要的内存开销,只要不满足条件可以直接返回nil。
-- 必须调用的是自己的init,而不是父类的init
-- 不能被重写
-- 可以用于extension 来扩展控件的初始化操作
// 便捷构造函数 (如果有初始化失败的情况,记得init? 可选的情况)
convenience init?(name: String, age: Int) {
// 年龄小于0不满足初始化条件
// 直接返回nil,表示初始化失败
if age < 0 {
return nil
}
// 对象真正初始化
self.init()
// 赋新的值
self.name = name
}
析构函数
类似OC中的dealloc函数,可以在对象销毁前做一些事情。
-- 不可直接调用
-- 不可重载
-- 对象销毁前自动调用
deinit {
// 跟踪对象的销毁
// 必须释放的资源
/*
-- 通知释放,不释放会造成内存泄露
-- kvo释放,崩溃
-- NSTimer
-- CADisplay
*/
}
KVC
-- 不能对private修饰的属性进行赋值。
-- oc中是全部裸露,与swift不同
KVO
kvo 是基于kvc的,都是OC运行时的概念,在swift中还需要做额外的工作,那就是将想要观测的对象标记为 dynamic。
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
class MyClass: NSObject {
dynamic var date = Date()
}
class MyClass1: NSObject {
var date = Date()
}
class MyChildClass: MyClass1 {
dynamic override var date: Date {
get { return super.date }
set { super.date = newValue }
}
}
private var myContext = 0
class Class: NSObject {
var myObject: MyClass!
override init() {
super.init()
myObject = MyClass()
print("初始化 MyClass,当前日期: \(myObject.date)")
myObject.addObserver(self,
forKeyPath: "date",
options: .new,
context: &myContext)
delay(3) {
self.myObject.date = Date()
}
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?)
{
if let change = change, context == &myContext {
let newDate = change[.newKey]
print("日期发生变化 \(newDate)")
}
}
}
let obj = Class()
private & fileprivate & @objc
-- private 在swift3开始,private 就是真正的私有,只能在本类中访问(在extension或其他类中都不能访问)。
-- fileprivate 在当前文件中私有,别的文件不能访问此关键字修饰的属性和函数。
-- @objc 允许这个函数在运行时通过OC的消息机制被调用
-- open > public > interal > fileprivate > private
extension
-- 不能有属性
-- 不能重写父类方法
extension String {
func myFrist() -> String? {
let str = (self as NSString)
if str.length == 0 {
return nil
}
let f = str.substring(with: NSMakeRange(0, 1))
return f
}
}
错误处理
try?(有异常会返回nil) / try! (如果有异常就崩溃 )
let json = try? JSONSerialization.jsonObject(with: data! as Data, options: [])
捕获错误
do {
let json = try JSONSerialization.jsonObject(with: data! as Data, options: [])
} catch {
print(error)
}
应用实战
一、runtime 获取属性
class func propertyList() -> [String] {
var count: UInt32 = 0
var ptyList: [String] = []
// 获取属性列表
let list = class_copyPropertyList(self, &count)
// 循环取出属性
for i in 0..<Int(count) {
// 用guard let 来优化 (?!)带来的解包问题
// UnsafePointer<Int8>? -> Byte(8个字节)- Char ,C语言的字符串
// 转换成String
guard let pty = list?[i],
let cName = property_getName(pty),
let name = String(utf8String: cName) else {
continue
}
ptyList.append(name)
}
// 释放 C 语言对象
free(list)
print("属性数量\(count)")
return ptyList
}
二、循环引用
// 模拟加载数据的函数
func loadData(completion: @escaping () -> ()) {
completion()
}
// weakSelf -> ViewController?
// OC的解决循环引用方法
weak var weakSelf = self
loadData {
print(weakSelf?.view)
}
// Swift 推荐方法
// [weak self] 表示 {} 中所有self都是弱引用,需要注意解包
loadData { [weak self] in
print(self?.view)
}
三、懒加载
// 懒加载
// 懒加载的代码只会在第一次调用的时候执行闭包
// 如果label被置为nil,懒加载也不会再次执行
/*
1.延迟创建
2.避免解包 ? 的烦恼
*/
lazy var label: MyLabel = MyLabel()
// 懒加载其实也是一个闭包
// 上面的懒加载方式展开如下
lazy var label = { () -> (MyLabel) in
let l = MyLabel()
return l
}()
统计代码行数
在项目根目录下 以下命令
find . -name "*.swift" | xargs wc -l