本文是對喵神書的學(xué)習(xí)總結(jié)逊脯, 僅僅只是為了加深理解記錄优质,支持正版支持正版
第一版就買了此書,一直沒好好看军洼,真是罪過罪過
一些簡單的代碼Github地址
2016-12-7
創(chuàng)建隨機數(shù)
使用arc4random()
來創(chuàng)建隨機數(shù)是一個不錯的方法巩螃,并且在swift中也可以使用,如果想要某個范圍內(nèi)的數(shù)匕争,可以使用模運算%
來實現(xiàn)避乏。
API
public func arc4random() -> UInt32
public func arc4random_uniform(_ __upper_bound: UInt32) -> UInt32
但是有一個問題:
在iPhone5s及之后的設(shè)備上沒問題,但是在iPhone5及之前的設(shè)備上有時候會崩潰
原因:
Swift的Int
是和CPU的架構(gòu)相關(guān)的:在32位的CPU上甘桑,實際上他是Int32
拍皮,在64位的CPU上,他是Int64
的扇住。而使用arc4random()
返回的值始終是UInt32
的春缕,所以在32位的機器上有一個班的概率會在進行Int轉(zhuǎn)換時候越界,從而崩潰艘蹋。
實踐锄贼,實現(xiàn)創(chuàng)建一個Range的隨機數(shù)的方法
//創(chuàng)建一個Range的隨機數(shù)的方法
func randomInRange(inRange range: Range<Int>) -> Int{
let count = UInt32(range.upperBound - range.lowerBound)
return Int(arc4random_uniform(UInt32(count))) + range.lowerBound
}
for _ in 0...10 {
let range = Range(1...6)
print(randomInRange(inRange: range))
}
自定制print數(shù)據(jù)的格式
實現(xiàn)該功能,在Objective-C中女阀,主要靠重載description
方法來實現(xiàn)
在Swift中通過實現(xiàn)CustomStringConvertible
協(xié)議來實現(xiàn)
class MyPrintClass{
var num: Int
init() {
num = 1
}
}
extension MyPrintClass: CustomStringConvertible{
var description: String{
return "Num: \(self.num)"
}
}
let printClass = MyPrintClass()
print(printClass)
其實主要是學(xué)習(xí)一種思想:實現(xiàn)和定義一個類型的時候宅荤,先定義最簡單的類型結(jié)構(gòu)屑迂,再通過擴展(extension)的方式來實現(xiàn)各種協(xié)議和功能
還有另外一個協(xié)議CustomDebugStringConvertible
,通過實現(xiàn)它可以設(shè)置在調(diào)試中使用debugger來進行打印時候的輸出冯键,類似po printClass
的命令進行打印
未實現(xiàn)時打印結(jié)果類似
(lldb) po printClass
<MyPrintClass: 0x608000029da0>
實現(xiàn)后打印結(jié)果類似
(lldb) po printClass
MyPrintClass Num : 1
打斷點觀察對象時十分有用惹盼,實現(xiàn)方式
class MyPrintClass {
var num: Int
init() {
num = 1
}
}
extension MyPrintClass: CustomDebugStringConvertible{
var debugDescription: String{
return "MyPrintClass Num : \(self.num)"
}
}
2016-12-6
delegate的使用
協(xié)議-委托模式太常見了,不多說
在OC中惫确,使用ARC手报,通常將delegate設(shè)置為weak,這樣可以使得delegate實際的對象被釋放時候改化,被置為nil掩蛤。但在Swift中,不能直接將其設(shè)置為weak陈肛,要解決這個問題揍鸟,首先要知道原因
swift中的protocol
不僅可以被class
所遵循,還可以被struct
或enum
這樣的類型遵循句旱,而對于這些類型阳藻,本身就不會通過引用計數(shù)來進行內(nèi)存管理,所以不能用weak
這樣的概念來修飾
明白了原因就好解決谈撒,就是設(shè)法將protocol
限制在class 內(nèi)
方法一
因為在Objective-C中的protocol
只有類能實現(xiàn)腥泥,所以可以將protocol
聲明為Objective-C的,通過在protocol
前添加@objc
來實現(xiàn)
@objc protocol MyClassDelegate {
func method()
}
class MyClass {
weak var delegate: MyClassDelegate?
}
方法二
在protocol聲明的后面加上class
港华,為編譯器顯示的指明該protocol只能由class
實現(xiàn)
protocol MyClassDelegate: class{
func method()
}
class MyClass {
weak var delegate: MyClassDelegate?
}
關(guān)聯(lián)對象的實現(xiàn)
在Objective-C中道川,我們可以利用Objective-C的運行時和Key-Value Coding的特性,在運行時實現(xiàn)向一個對象添加值存儲立宜。在使用Category擴展現(xiàn)有類的功能時冒萄,直接添加實例變量是不被允許的,但可以使用property配合 Associated Object 的方式橙数,將一個對象關(guān)聯(lián)到一個已有的要擴展的對象上尊流,從外部來看,就像是直接通過屬性訪問對象的實例變量一樣灯帮。
在Swift中實現(xiàn)向extension
中使用 Associated Object的方式將對象進行關(guān)聯(lián)
class MyClass{
}
private var key: Void?
//error: extensions may not contain stored properties
extension MyClass {
var title: String?{
get{
return objc_getAssociatedObject(self, &key) as? String
}
set{
objc_setAssociatedObject(self, &key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
let myClass = MyClass()
if let title = myClass.title {
print("設(shè)置了title: \(title)")
}else{
print("該屬性為空")
}
myClass.title = "Tom"
if let title = myClass.title {
print("設(shè)置了title: \(title)")
}else{
print("該屬性為空")
}
說明:
key
的類型被聲明為 Void?
崖技,并通過&
符號取地址作為UnsafePointer<Void>
類型傳入。
2016-12-5
GCD和延遲調(diào)用
使用GCD來實現(xiàn)多線程編程钟哥,先來個最經(jīng)常使用的例子
//創(chuàng)建目標(biāo)隊列
let workingQueue = DispatchQueue(label: "com.ivan.myqueue")
//派發(fā)到剛創(chuàng)建的隊列中迎献,GCD會負(fù)責(zé)線程調(diào)度
workingQueue.async {
//在workQueue中異步進行
print("Working....")
//模擬兩秒的執(zhí)行時間
Thread.sleep(forTimeInterval: 2)
print("Work finish")
DispatchQueue.main.async {
print("在主線程中更新UI")
}
}
實現(xiàn)延遲調(diào)用,這樣寫主要是為了實現(xiàn)能夠在調(diào)用之前取消該操作
typealias Task = (_ cancel: Bool) -> Void
func delay(_ time: TimeInterval,task: @escaping () -> ()) -> Task? {
//這個時候就體會到@escaping的含義了
func dispatch_later(block: @escaping () -> ()){
let t = DispatchTime.now() + time
DispatchQueue.main.asyncAfter(deadline: t, execute: block)
}
var closure: (() -> Void)? = task
var result: Task?
let delayedClosure: Task = {
cancel in
if let internalClosure = closure {
if cancel == false {
DispatchQueue.main.async(execute: internalClosure)
}
}
closure = nil
result = nil
}
result = delayedClosure
dispatch_later {
if let delayedClosure = result{
delayedClosure(false)
}
}
return result
}
func cancel(_ task: Task?) {
task?(true)
}
2016-12-4
值類型和引用類型
在swift中腻贰,有值類型和引用類型兩種吁恍,值類型在傳遞和賦值時將進行復(fù)制,引用類型只會使用引用對象的一個‘指向’。在swift中有幾點需要注意
- swift中的
struct
和enum
定義的類型都是值類型冀瓦,使用class
定義的對象是引用類型 - swift中所有的內(nèi)建類型都是值類型伴奥,不僅包括
Int
,Bool
翼闽,甚至連String
拾徙、Array
和Dictionary
都是值類型。
這樣做的好處是可以減少堆內(nèi)存上內(nèi)存分配和回收的次數(shù)感局。
驗證一下在swift中數(shù)組是值類型
//驗證: 數(shù)組在swift中是值類型
var array = ["A","B","C"]
var array1 = array
array1.removeLast()
array //["A", "B", "C"]
array1 //["A", "B"]
驗證一下值類型中存儲值類型和引用類型時的操作
//驗證: 值類型在復(fù)制時尼啡,將存儲在其中的值類型進行復(fù)制,但對于其中的引用類型询微,只是復(fù)制一份引用
//引用類型
class MyClass{
var num: Int
init(num: Int) {
self.num = num
}
}
var myObj = MyClass(num: 1)
var classArray = [myObj]
myObj.num = 101
classArray[0].num //101
//值類型
struct MyStruct{
var num: Int
}
var myStruct = MyStruct(num: 2)
var structArray = [myStruct]
myStruct.num = 102
structArray[0].num //2
區(qū)分一下String和NSString
先來個自己的總結(jié)
** String
和NSString
其實并沒有太大的區(qū)別玄叠,他們兩個基本上市可以無縫轉(zhuǎn)換的,之所以有時候需要轉(zhuǎn)換拓提,只是他們有很少一部分沒有相互實現(xiàn)的API,只是為了使用起來方便**
比如遍歷一個字符串
使用String
會更方便隧膘,因為它實現(xiàn)了ColletionType
這樣的協(xié)議
var str = "Hello, playground"
for character in str.characters {
print(character)
}
但如果是要和Range
配合使用代态,使用NSString
會更方便一些
//比如我們要實現(xiàn)一個替換第一個字符到第5個字符之前的字符的功能
//使用String
let startPosition = str.index(str.startIndex, offsetBy: 1)
let endPosition = str.index(str.startIndex, offsetBy: 5)
let range: Range = startPosition..<endPosition
str.replacingCharacters(in: range, with: "TEST") //"HTEST, playground"
//如果使用NSString
let nsRnage = NSMakeRange(1, 4)
(str as NSString).replacingCharacters(in: nsRnage, with: "TEST") //"HTEST, playground"
JUST DO IT.
2016-12-3
在Swift中實現(xiàn)協(xié)議方法的可選實現(xiàn)
在Objective-C中的protocol
可是使用關(guān)鍵字@optional
來實現(xiàn)可選方法。但是在Swift中疹吃,默認(rèn)所有的方法都是必須要實現(xiàn)的蹦疑。但是也并非不能實現(xiàn),有兩種方法
將協(xié)議本身和可選方法都定義為Objective-C
其實也就是在協(xié)議之前和協(xié)議方法之前都加上@objc
萨驶,這樣就可以使用optional
關(guān)鍵字了
有一些限制歉摧,說明一下:
- 使用
@objc
修飾的protocol
只能被class實現(xiàn),對于struct
和enum
類型腔呜,無法令他們實現(xiàn)可選方法和屬性了 - 實現(xiàn)他的class 的方法也必須被標(biāo)注為
@objc
叁温,或者整個類就是繼承自NSObject
@objc protocol MyProtocol1 {
@objc optional func method1() -> String
func method2() -> Int
}
class MyClass1: MyProtocol1 {
//不實現(xiàn)也是可以的
// func method1() -> String {
// return "Test"
// }
func method2() -> Int {
return 2
}
}
//是會報錯的 error: non-class type 'MyStruct1' cannot conform to class protocol 'MyProtocol1'
struct MyStruct1: MyProtocol1{
func method1() -> String {
return "Struct"
}
func method2() -> Int {
return 3
}
}
使用extension
這個方法是在swift2.0之后才支持的,其實就是使用protocol
的extension
給出方法的默認(rèn)實現(xiàn)核畴,這樣膝但,這些方法在實際的類中就是可選實現(xiàn)的了
protocol MyProtocol2 {
func method1() -> String
func method2() -> Int
}
extension MyProtocol2 {
func method1() -> String {
return "MyProtocol2DefaultString"
}
}
class MyClass2: MyProtocol2 {
func method2() -> Int {
return 3
}
}
2016-11-30
條件編譯
swift中沒有宏的概念,所以不能使用#ifdef
的方法來檢測某個符號是否經(jīng)過宏定義
編譯標(biāo)記的使用語法
#if <condition>
#elseif <condition> //可選
#else //可選
#endif
condition
的內(nèi)建組合
方法 | 可選參數(shù) |
---|---|
os() | masOS谤草、iOS跟束、tvOS、watchOS丑孩、Linux |
arch() | i386冀宴、x86_64(對應(yīng)模擬器上的32位和64位CPU),arm、arm54(對應(yīng)真機) |
swift() | >= 某個版本 |
可以配合 typealias
使用温学,更強大
自定義condition
進行條件編譯
使用方法一樣略贮,主要是如何定義編譯符合
在項目的編譯選項中進行設(shè)置,Building Settings - Swift Compiler - Custom Flags,在其中的 Other Swift Flags 加上 -D 定義的編譯符號
就可以了
可用于
- 在同一個Target中完成同一個APP的不同版本,進行區(qū)分
- 在Debug或Release版本中進行不同的操作
編譯標(biāo)記
在OC中刨肃,使用 #param
來標(biāo)記代碼區(qū)間
在Swift中古拴,使用// MARK:
來標(biāo)記代碼區(qū)間
還有 // TODO:
和// FIXME:
來提示工作尚未完成或需要修改的地方
注意:這些都是區(qū)分大小寫的
給方法添加文檔說明,可使用快捷鍵 alt+command+/
真友,這樣在使用時候黄痪,使用alt+單擊
可以查看該方法的文檔說明
2016-11-29
單例的創(chuàng)建
class MyManager{
static let shared = MyManager()
private init(){
}
//在初始化類變量的時候,Apple會將這個初始化包裝在一次 swift_once_block_invoke 中盔然,以保證他的唯一性桅打,類似 dispatch_once 的方式
//加入一個私有的初始化方法,覆蓋默認(rèn)的公開初始化方法愈案,這樣挺尾,其他地方就不能夠通過 init 來生成自己的 MyManager 實例,也保證了單例的唯一性
}
let manager = MyManager.shared
實例方法的動態(tài)調(diào)用
class MyClass{
func method(num: Int) -> Int {
return num + 1
}
}
//如果想要調(diào)用method方法站绪,一般是生成 MyClass 的實例遭铺,使用 .method 來調(diào)用
let obj = MyClass()
obj.method(num: 2)
//或者使用 Swift 的 Type.instanceMethod 的語法來生成一個可以柯里化的方法
let f = MyClass.method //f 的類型是 (MyClass) -> (Int) -> Int
let obj1 = MyClass()
//知道了f的類型,就不難理解為何這樣調(diào)用了
f(obj1)(2)
//注:這種方法只適用于實例方法恢准,對于屬性的getter和setter不可以
//還有一種情況魂挂,實例方法和類型方法名字沖突時
class MyClass1{
func method(num: Int) -> Int {
return num + 1
}
class func method(num: Int) -> Int{
return num + 2
}
}
// 使用 MyClass1.method 默認(rèn)取出來的是類型方法,解決方法是顯式的加上類型聲明
let method1 = MyClass1.method // class func
let method2: (Int) -> Int = MyClass1.method // class func
let method3: (MyClass1) -> (Int) -> Int = MyClass1.method // func method 的柯里化版本