最近在學Swift,本以為多是語法與oc不同,而且都是使用相同的cocoa框架,相同的API,但是或多或少還是有些坑在里,為了避免以后再踩,在這里記下了,以后發現新的坑,也會慢慢在這里加上
[TOC]
OC中main.m中的代碼, 通過@UIApplicationMain標記自動生成
可以注掉AppDelegate里的@UIApplicationMain,自己實現Main,不過一般沒人這樣做
實現下面代碼就是OC中的main文件的函數
UIApplicationMain(Process.argc, Process.unsafeArgv,nil, NSStringFromClass(AppDelegate))
在swift中打印對象時,會發現在類型前面總會有命名空間
.+類名
在swift中用字符串生成類對象就需要拼接成這樣的格式,才能成功生成類
注意,命名空間不要加特殊符號,不然依然無法獲取控制器類
//獲取命名空間,在info.plist文件里就是Executable fileletnameSpace = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"]as!String//拼接成固定格式letcontroller:AnyClass = NSClassFromString(nameSpace +"."+ controllerName)!//創建對象letviewController = (controlleras! UIViewController.Type).init()
3.Swift中的Any,AnyObject,AnyClass分別代表是什么?
AnyObject: 相當于OC中的id, 表示所有class類型的數據, 所有繼承與NSObject的類都隱式實現了protocol AnyObject協議, 所以他可以表示所有的class類型
Any:所有基本數據類型和enum/ struct都可以用Any來表示
注意: 有的時候你會發現將基本數據類型或者enum/ struct通過AnyObject來保存也不會報錯, 這是因為Swift中很多數據類型可以和OC中的數據類型進行自動轉換, 系統內部已經將他們轉換為了OC的對象類型
AnyClass: 用來表示任意類的類類型(元類型)
typealiasAnyClass=AnyObject.Type.Type用于獲取類的元類型, 例如Person.Type就代表著獲取Person的元類型? .self如果通過類名調用, 那么可以獲取該類的類型, 說白了就是獲取自己
在Swift中抓取異常需要do,catch,try這三個關鍵字
這里舉個json序列化的例子
do{letpath =/..路徑../letdata =/..轉data..///編譯器會要求你實行異常檢測,于是在序列化前面添加try字段//外部包裹do,catch,顯而易見出錯自然會走catchletdicArr =tryNSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers)? ? ? ? }catch{// 如果拋出異常就會來到catch}
由于swift沒有宏,我們不能像oc那樣去定義
直接在AppDelegate中寫,反正哪里都可以用
用泛型傳參
如何判斷調試與發布狀態呢?
在Build settings里找到Swift Compiler-custom Flags
在other swift flags 的Debug里添加兩個字段
"-D"
"DEBUG"
代碼中直接判斷就行
funcHJSLog(message: T){? ? #ifDEBUGprint("\(message)")? ? #endif}
在swift中,單例有兩種寫法
一種是按照OC的思維去寫
static var onceToken: dispatch_once_t =0staticvarinstance: NetworkTools?classfunc shareNetworkTools() -> NetworkTools? ? {? ? ? ? dispatch_once(&onceToken) { () ->Voidinprint("我被調用了")instance= NetworkTools()? ? ? ? }returninstance!? ? }
另一種就是swift的純正寫法
在swift中,let本身就只會創建一次,可以運用這個特性
let是線程安全的
staticletinstance:NetworkTools=NetworkTools()classfuncshareNetworkTools() ->NetworkTools{returninstance? ? }
一般我們不公開方法會在前面添加private
但是例如按鈕點擊方法,光是添加private是不夠的
因為swift的方法調用是在編譯時就決定了
而點擊事件方法由于是來自于runloop中
編譯器不會它一起編譯進來,只有在運行時呼叫,這屬于OC的調用方式
所以我們還需要再在方法前面加上@objc
//按鈕點擊handle@objcprivatefunccomposeClick(){? ? }`
在swift中對懶加載有專門的關鍵字
///懶加載一個imageViewprivate lazy var icon:UIImageView= {? ? ? ? let imageV =UIImageView(image:UIImage(named:"visitordiscover_feed_image_smallicon"))returnimageV? ? }()
在swift中定義協議也很簡單
只需要在類前定義就行
@objcprotocolVisitorViewDelegate:NSObjectProtocol{//點擊注冊按鈕optionalfuncvisitorViewDidRegisterBtnClick(visitView: VisitorView)//點擊登錄按鈕optionalfuncvisitorViewDidLoginBtnClick(visitView:VisitorView)}
代理屬性需要設定為weak,防止循環引用
weakvardelegate:VisitorViewDelegate?
在調用代理方法時,代理作為可選屬性,已經幫我們預防代理不存在的可能
我們還需要借助可選屬性來預防方法未實現
當然在確定實現的前提下可以解包
///注冊handle@objcprivatefuncregisterClick(){delegate?.visitorViewDidRegisterBtnClick!(self)? }///登錄handle@objcprivatefuncloginClick(){ delegate?.visitorViewDidLoginBtnClick?(self)? }
我剛從oc轉過來就遇到了如何在swift中寫分類的問題
swift中寫分類很簡單
extension就是swift中的分類
例如給UIBarbuttonItem添加分類
新建一個UIBarButtonItem+Extension.swift文件
importUIKitextensionUIBarButtonItem{? ? convenience init(target:AnyObject?,action:Selector,image:String) {? ? ? ? let btn =UIButton(type:UIButtonType.Custom)? ? ? ? btn.setImage(UIImage(named: image), forState:UIControlState.Normal)? ? ? ? btn.setImage(UIImage(named: image +"_highlighted"), forState:UIControlState.Highlighted)? ? ? ? btn.addTarget(target, action: action, forControlEvents:UIControlEvents.TouchUpInside)self.init(customView:btn)? ? }}
11.為何經常被強制實現init(coder: NSCoder)
因為Objective-C 和 Swift 中都沒有直接的這樣的抽象函數語法支持
然而有些時候我們卻有不想讓別人調用某個方法,但又不得不將其暴露出來的時候。
一般滿足這種需求的就是抽象類型或者抽象函數
在面對這種情況時,為了確保子類實現這些方法,而父類中的方法不被錯誤地調用,我們就可以利用 fatalError 來在父類中強制拋出錯誤,以保證使用這些代碼的開發者留意到他們必須在自己的子類中實現相關方法:
classMyClass{funcmethodMustBeImplementedInSubclass(){? ? ? ? fatalError("這個方法必須在子類中被重寫")? ? }}classYourClass:MyClass{overridefuncmethodMustBeImplementedInSubclass(){print("YourClass 實現了該方法")? ? }}classTheirClass:MyClass{funcsomeOtherMethod(){? ? }}YourClass().methodMustBeImplementedInSubclass()// YourClass 實現了該方法TheirClass().methodMustBeImplementedInSubclass()// 這個方法必須在子類中被重寫
不僅僅是對于類似抽象函數的使用中可以選擇 fatalError,對于其他一切我們不希望別人隨意調用,但是又不得不去實現的方法,我們都應該使用 fatalError 來避免任何可能的誤會。比如父類標明了某個 init 方法是 required 的,但是你的子類永遠不會使用這個方法來初始化時,就可以采用類似的方式, 被廣泛使用 (以及被廣泛討厭的) init(coder: NSCoder) 就是一個例子。在子類中,我們往往會寫
requiredinit(coder:NSCoder) {fatalError("NSCoding not supported")}
12.在swift中使用guard與fatalError配合拋出異常
在嚴謹的開發中會經常用到斷言
前面一條介紹了fatalError來拋出錯誤
這條就來介紹一下guard與fatalError的配合使用達到斷言的效果
guardletsafeValue = criticalValueelse{? fatalError("criticalValue cannot be nil here")}someNecessaryOperation(safeValue)
本來我認為if也可以達到這樣的效果
ifletsafeValue = criticalValue {? someNecessaryOperation(safeValue)}else{? fatalError("criticalValue cannot be nil here")}
或者
ifcriticalValue ==nil{? fatalError("criticalValue cannot be nil here")}someNecessaryOperation(criticalValue!)
但是看到有些博客這么說:
這個flatten code以其他方式進入一個if let 代碼塊,并且在靠近相關的環境中過早地退出了,而不是進入else代碼塊。甚 至當你沒有捕獲一個值(guard let),這個模式在編譯期間也會強制過早退出。在第二個if的例子里,盡管代碼flattend得像guard一樣,但是一個毀滅性的錯誤或者其他返回 一些無法退出的進程(或者基于確切實例的非法態)將會導致crash。一個過早的退出發生時,guard聲明將會及時發現錯誤,并將其從else block中移除。(這博主翻譯得真爛)
所以,還是用guard比較好
在swift中,互斥鎖如何寫
oc中的互斥鎖:
@synchronized(self) {//需要執行的代碼塊}
swift中的互斥鎖
objc_sync_enter(self)//需要執行的代碼塊objc_sync_exit(self)
至于其他多線程的API和以前的一樣,只是少了perform這一類的API,蘋果已經去掉了
在oc中,我們需要在代碼塊用到self時,可以直接把self付給其他變量,然后在塊中使用完畢后制空,或者像下面弱引用self來避免循環引用:
__weaktypeof(self) weakSelf =self;
那么在swift中我們怎么辦到這點呢?
很簡單,看下面代碼
//這里用gcd舉例不好,畢竟系統的塊不會造成循環引用,這里就勉強的學一下怎么改吧dispatch_async(dispatch_get_global_queue(0,0)) {[unownedself] () -> Voidinself.view//添加自己的代碼}
只需要在閉包里加入[unowned self]即可