從Object-C -> Swift3.0
前言
當我們開始接觸一門新語言時,我們難免避免不了型別和基本語法規則。我們會急切的想知道如何用新的語法規則寫我們原先所熟知的語句。本文旨在對於Object-C和Swift做一些基本的對比。通過閱讀本文,您能快速的瞭解Swift3.0和Object-C的一些差別。
- 基本型別
同所有的面嚮物件語言一樣,Swift裡也只有兩種型別:
1.值型別
2.引用型別
值型別包括struct,enum;
引用型別主要是class,嗯還有閉包也是引用型別哦。
- 程式碼不再需要分號結尾
Swift程式碼單行結束,不需要強制加分號,但也可以選擇加
- 型別推斷
Swift可以自動根據上下文為一個變數推斷出其型別;
- 強型別語言
儘管Swift可以支援型別推斷,可以用var來宣告變數。但Swift是強型別語言。這樣意味著,只要涉及不同型別之間的運算,都需要進行強制型別轉換
- 變數、常量定義
let constant name : type = expression
var variable name : type = expression
在開始所有介紹前,我們先看簡單的幾行程式碼.大家採一些預期結果是什麼?
var num=3.0
var integer=6
print(num+integer) //輸出結果是?
結果:編譯不過。因為Swift是一個強型別語言
從Object-C向Swift過渡
接下來將會從Protocol,block,GCD等大家在Object-C裡熟知的內容開始,一步步過渡到Swift3.0。
1.protocol
2.block
3.GCD
4.property
5.函式
6.extension,category
7.可選型別?與??
8.型別判斷與轉換
9.id型別與Any,AnyObject,nil
10.typedef與typealias
11.巨集定義
12.混合程式設計
13.API設計與函式命名對比
1.protocol
Swift也和OC一樣有protocol,但不同的是Swift的protocol可以被class,struct,enum實現,甚至還能進行extension擴充套件。
參考:Protocols
- Protocol以及optional protocol定義
//Object-C
@protocol RefreshHeaderDelegate <NSObject>
@optional
- (void)willBeginRefresh:(PullRefreshHeader*)header;
- (void)didRefresh:(PullRefreshHeader*)header;
@required
- (void)didFinishRefresh:(PullRefreshHeader*)header;
@end
//Swift
@objc protocol RefreshHeaderDelegate {
@objc optional func willBeginRefresh(header:PullRefreshHeader) -> Void
@objc optional func didRefresh(header:PullRefreshHeader) -> Void
}
//加了class的protocol才能被使用在類的delegate模式中,因為protocol不加class標記則還可以被enum,struct實現
protocol RefreshHeaderDelegate :class{
func willBeginRefresh(header:PullRefreshHeader) -> Void
func didRefresh(header:PullRefreshHeader) -> Void
}
- 定義了optional方法後,如何判斷delegate是否實現了該方法?
//Object-C
if([self.delegate respondsToSelector:@selector(willBeginRefresh:)]){
//執行delegate的方法
[self.delegate willBeginRefresh:self];
}
//Swift中我們可以更優雅的實現,optional是很方便的
self.delegate?.willBeginRefresh?(header: self)
2.block
Swift中任何兩個{}之間的程式碼都算是閉包,因此函式也是一種特殊的閉包。
參考:closure-expressions-in-swift
- 閉包定義
//Swift閉包定義
{ (obj:AnyObject) ->Void in
//閉包內容開始
print(obj)
pring($0)
//可以用$<index>來指向閉包的形參,下標從0開始
}
- 如何避免閉包中的迴圈引用?
//Object-C
__weak __typeof(self) __weak_self = self
[button onClicked:^(MttTopBannerButton * button) {
MttBottomBanner *banner=[[MttBottomBanner alloc] init];
[__weak_self.view addSubview:banner];
[banner show];
button.hidden=YES;
}];
//Swift更優雅
button.onClicked({
[weak self]
(button:MttTopBannerButton) ->Void in
var banner = MttBottomBanner()
self?.view.addSubview(banner)
banner.show()
button.hidden=true
})
- weakself,strongself
//Swift
DispatchQueue.main.sync(execute: {
[weak self]
(_:AnyObject?) ->Void in
self?.draw()
self?.color=UIColor.white()
DispatchQueue.main.sync(execute: {
if let strongSelf=self {
strongSelf.lock.lock()
strongSelf.layer.contents=strongSelf.display?.cgImage
strongSelf.lock.unlock()
}
})
})
//請思考為何此處我是NSLock來加鎖?
//因為Swift裡沒有@synchonized關鍵字的替代品了啊,需要的話,得自行呼叫GCD介面實現了.
//Object-C
__weak __typeof(self) __weak_self = self
dispatch_async(dispatch_get_main_queue(),^{
[self draw];
self.color=[UIColor whiteColor];
__typeof(__weak_self) strongSelf = __weak_self
dispatch_async(dispatch_get_main_queue(),^{
[strongSelf.lock lock];
strongSelf.layer.contents=strongSelf.display.CGImage;
[strongSelf.lock unlock];
})
});
3.GCD
Grand Dispatch Queue在Object-C中是一組C語言介面,雖然在swif1,swift2中仍保留了這種用法習慣,但它畢竟不太符合一門強型別與物件導向的語言的要求(就像這些人取消了++,--運算子一樣,無時無刻不再想著摒棄老式的C語言程式設計思維),於是Swift3中GCD終於也物件導向了.
- dispatch_async
//Swift
DispatchQueue.main.async(execute:{blk(nil)})
//Object-C
dispatch_async(dispatch_get_main_queue(),^(id obj){
});
- dispatch_after
//Object-C
- (void)performBlockOnMainThread:(void (^)())block afterDelay:(CGFloat)delay
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
block();
});
}
//block引數可為空,因為是optional
func performOnMainThread(_ block:AsyncLoaderTask? ,delay:Double) -> Void {
if let blk=block {
let time=DispatchTime.now()+delay
//DispatchWorkItem相當於之前的dispatch_queue_t所以直接用
DispatchQueue.main.after(when:time,execute:{blk(nil)})
}
}
4.Property屬性
在Swift中類裡定義了的變數或常量即是屬性
屬性分為2種:
- 儲存型屬性
跟Object-C的property基本差不多。但 - 計算型屬性
比較特別,並不是用於真正的儲存一個值或變數,而是提供了getter和setter方法來間接的獲取其他屬性或變數的值。其本身的存在價值是依賴於其它屬性或變數的存在
1.屬性定義
2.屬性觀察器
3.getter,setter
4.delegate模式
5.普通屬性
6.lazy標記
7.optional屬性
8.completion block模式
9.初始化
typealias AsyncLoaderTask = (_:AnyObject?) ->Void
class PullHeaderTable: UITableView {
// MARK: Public Properties
//////////////
//1.標準屬性定義方式
//常量屬性,以及屬性定義的兩種方式,可以隱身讓Swift自行推斷型別,也可以明確指定型別
let source=PullHeaderTableSource()
let src : PullHeaderTableSource = PullHeaderTableSource()
var s : PullHeaderTableSource = PullHeaderTableSource()
//////////////
//2.property觀察器
var data : [AnyObject]?{
didSet{
source.data=data
print(oldValue)
}
willSet{
print(newValue)
}
}
//////////////
//3.getter,setter,通過getter返回的,叫做計算型屬性
var src : PullHeaderTableSource {
get{
return PullHeaderTableSource()
}
set{
print(newValue)
}
}
//////////////
//4.delegate模式
weak var tableDelegate : PulllHeaderTableDelegate?
//////////////
//5.普通屬性,必須在init方法裡最先被初始化
var header : UIView
//////////////
//6.lazy標記,該屬性只會在被呼叫時自動呼叫初始化賦值,而不是在init之前就初始化了,thread unsafe
lazy var footer = UIView()
//7.optional屬性
var listener : AnyObject?
//8.optional block屬性(和OC的completion block模式)
var completion : AsyncLoaderTask?
//////////////
//9.對於非optional的屬性,在init時必須要初始化,否則會出錯
//init初始化與OC的不一樣,是先把當前例項的非optional屬性初始化完畢,然後再呼叫父類的init
//原因在於Swift定義了的任何型別property編譯器都不會給預設值,因此非optional property必須在init中有優先初始化
init(frame:CGRect) {
header=UIView()
super.init(frame:frame, style:UITableViewStyle.plain)
self.onCreate()
}
required init?(coder aDecoder: NSCoder) {
header=UIView()
super.init(coder: aDecoder)
self.onCreate()
}
override init(frame:CGRect,style:UITableViewStyle){
header=UIView()
super.init(frame: frame, style: style)
self.onCreate()
}
deinit {
self.dataSource=nil
self.delegate=nil
}
// MARK: Private Functions
private func onCreate() -> Void {
self.scrollsToTop=false
self.separatorColor=UIColor.clear()
self.separatorStyle=UITableViewCellSeparatorStyle.none
self.contentOffset=CGPoint.zero
self.delegate=source
self.dataSource=source
}
}
5.函式
使用 func 來宣告一個函式,使用名字和引數來呼叫函式。使用 -> 來指定函式返回值的型別。
參考:Functions
1.如何讓函式返回多個返回值?
2.如何傳入1個或N個引數
3.重構->函式內巢狀函式
4.函式型別與函式返回值,函式引數
Swift的函式相比較於OC發生了很多很好的改進,我們可以更自由的使用定義函式了.
同時還加入了函式型別。
參考:函式型別
class App: NSObject {
//func test() -> Void {}
func test() {
print(calculate(scores: [0,1,2,3,4,5]))
print(sumOf(numbers: 0,1,2,3,4,5))
runAnotherFunction(longlongFunction)
}
// MARK:Function Usage
//1.使用元組返回多個返回值
//下述函式從陣列中返回最小,最大,求和.返回值是一個元組.
//其中呼叫了Swift裡的min,max函式,全域性泛型函式,用於比較大小返回結果.
//min,max函式類似於OC巨集定義的MIN(),MAX()
func calculate(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
var vmin=scores[0]
var vmax=vmin
var sum : Int = 0
for val in scores {
vmin=min(vmin, val)
vmax=max(vmax, val)
sum+=val
}
return (vmin,vmax,sum)
}
//2.傳入可變個數的引數
func sumOf(numbers: Int...) -> Int {
var sum=0
for val in numbers {
sum+=val
}
return sum
}
//3.巢狀函式,用於部分程式碼的重構,其實也可以裡面定義一個block一樣的。不過比OC多了個選擇
func longlongFunction() {
var x=10
//猜猜看 _ 這裡下劃線的作用?看看如何呼叫的你就會明白了
func increament(_ val:Int) -> Int {
return val+1
}
print(increament(x))
func add() {
//x++
//Swift 3.0不支援++,--了哦
x=x+1
}
add()
}
//4.函式作為引數,當然函式也可以作為返回值的.函式型別
func runAnotherFunction(_ fun: () -> ()) {
fun()
}
}
6.extension,category
Swift中沒有category的概念,因為Swift中的extension基本涵蓋了所有原先Object-C中需要的一切
1.新增計算型屬性和計算型靜態屬性
2.定義例項方法和型別方法
3.提供新的構造器
4.定義下標[]
5.定義和使用新的巢狀型別
6.支援協議
參考:下標運算子
extension String {
func toHex() -> String {
return ""
}
}
protocol AppProtocol: class {
func app() -> Void
}
class App: NSObject {
var data : Array = Array<Int>()
static var instance = App()
private override init() {
for i in 0...8 {
self.data.append(i)
}
}
}
extension App: AppProtocol {
//////////////
//1.新增計算性屬性,儲存型的不可以啊
var hashCode : Int {
return Int(Date.timeIntervalSinceReferenceDate)+8
}
//非法,儲存性屬性不能再extension裡存在
//var newProperty : Int = 0
//////////////
//2.新增新的建構函式
convenience init(_ id:Int, time:Int) {
self.init()
}
//////////////
//3.函式或實現協議
func app() -> Void {
print("app")
}
//////////////
//4.為物件實現下標運算子
subscript(index: Int) -> Int {
get{
return self.data[index]
}
set{
self.data[index]=newValue
}
}
//////////////
//5.巢狀型別,class,enum,也就是相當於定義內部類
class UI : NSObject {
}
}
func testApp() -> Void {
let app = App.instance
app[0]=1
print(app[1])
}
7.可選型別?與??
可空鏈式呼叫(Optional Chaining)
這個名詞太專業了,我們就簡單的來談談吧
定義屬性或變數時,如果要允許一個屬性為空即nil,像Object-C一樣的話。那麼我們需要制定其位optional型別
只需定義結束末尾在加一個?符號即可,如下
var obj : AnyObject?
//定義了一個相當於Object-C的 id obj=nil;東西
對於optional型別的變數或方法(如optional protocol method)我們在呼叫時需要把optional型別的值/物件取出來,這一步叫做unwrap;反之定義時就叫wrap;
通過?來unwrap或者直接利用!來強制unwrap(強制的前提是你能確保變數是有有效值的),如下
var str : String?
str="value"
//前面已經為str賦值過了,所以我們是已知強制unwrap是不會有異常的
if str!.characters.count>0 {
}
//如果簽名沒有為str賦值,或者不確定有值與否,需要嘗試unwrap
if str?.characters.count>0 {
}
var count : Int
count=str?.characters.count ?? 0
print(count)
大家是否在好奇上述的??符號到底是什麼意思呢?很簡單,如果你理解選擇表示式,那麼可以把它看做對於optional型別的選擇表示式吧
等價於如下程式碼
if str != nil {
count=str!.characters.count
}
else
{
count=0
}
8.型別判斷與轉換
1.在Object-C中我們經常會對一個id型別或者不確定具體型別的例項物件做型別判斷,如isKindOfClass;在Swift中我們可以更簡單來實現
參考:Type Casting
is型別檢查運算子
//Object-C判斷型別
UIView *v=XXX;
if([v isKindOfClass:[UIWindow class])
{
}
//Swift
var v=XXX
if v is UIWindow {
}
2.在Object-C中我們要做型別轉換可以直接使用C語言的方式來強制型別賦值,但在Swift中需要用as運算子實現
as 用於向上轉型(upcasts)
as? 和as! 用於向下轉型(Downcasting)
備註:
1.向上轉型:由派生類轉為父類物件
2.向下轉型:有父類物件轉為具體子類物件
當不確定型別轉換是否成功時,用型別轉換的條件形式( as? )
as?如果失敗會返回nil
當確定一定是時,用強制形式式( as! )
//Object-C
for(UIViewController *ct in controllers)
{
if([ct isKindOfClass:[MttBaseViewController class]])
{
NSLog(@"MttBaseViewController");
}
else if([ct isKindOfClass:[MttRootViewController class]])
{
NSLog(@"MttRootViewController");
}
}
//Swift
for ct in controllers {
if let mtt = ct as? MttBaseViewController {
print("MttBaseViewController")
} else if let nav = ct as? MttRootViewController {
print("MttRootViewController")
}
}
9.id型別與Any,AnyObject,nil
Any 和 AnyObject 是 Swift 中兩個妥協的產物;
AnyObject 可以代表任何 class 型別的例項
Any 可以表示任意型別,甚至包括方法 (func) 型別
AnyObject用於在Swift中替換Object-C的id型別;
Swift中編譯器不會對 AnyObject 例項的方法呼叫做出檢查,甚至對於 AnyObject 的所有方法呼叫都會返回 Optional 的結果。因此使用AnyObject例項之前我們務必需要檢測物件是否存在和型別有效性。
參考:ANY 和 ANYOBJECT
nil:
Swift中的nil與Object-C的nil是不一樣的。Object-C中,nil是一個指標指向一個不存在的object。在Swift中,nil不是指標,而是對於特定型別的預設值。任何optional型別都可以設定為nil,而不僅僅是物件型別;
optional型別變數的值預設就是nil;
10.typedef與typealias
typealias相當於Object-C中的typedef,用來為已經存在的型別重新定義名字。通過命名,可以使程式碼變得更加清晰。
參考:swift-typealias,typelias
//重新命名閉包,和Object-C一樣
typealias AsyncLoaderTask = (_:AnyObject?) ->Void
func submit(_ block:AsyncLoaderTask?,completion:AsyncLoaderTask?) -> Void {
if let blk=block {
self.queue.async(execute:{
blk(nil)
if let cmp=completion {
cmp(nil)
}
})
}
}
//protocol組合
protocol changeName{
func changeNameTo(name:String)
}
protocol changeSex{
func changeSexTo(sex:String)
}
typealias changeProtocol = protocol<changeName,changeSex>
struct Person:changeProtocol{
func changeNameTo(name:String){
}
func changeSexTo(sex:SEX){
}
}
//swift中很多型別別名即是typealias定義的
public typealias AnyClass = AnyObject.Type
public typealias NSInteger = Int
11.巨集定義
很抱歉,Swift中終於不支援巨集定義了。
Object-C中的巨集暴露到Swift中的話,只有簡單展開巨集會被直接變為同名常量。
參考:converting-complex-objective-c-macros-swift-functions,Objective的巨集到swift
//Object-C
#defien MTT_DEBUG 1
//Swift
let MTT_DEBUG = 1
//以上二者等價
其餘複雜的巨集定義以及巨集巢狀都無法被翻譯到Swift裡,也無法在Swift中呼叫或使用。
所以要使用巨集,只能在Object-C中使用.
12.混合程式設計
處於某些目的,我們還是有必要在Swift程式碼中使用Object-C,或者在Object-C程式碼中裝一下樣子,搞點Swift。那這時就涉及到混合程式設計了。
參考:混和程式設計
1.OC中使用Swift
在工程的 Build Settings 中把 defines module 設為 YES.即可.(如需必要,再把把 product module name 設定為專案工程的名字。理論上不需要了,XCode 8.0已經預設是這樣了)
最後一步,在你的OC檔案中匯入 ProjectName-Swift.h.即(productModuleName-Swift.h)
2.Swift中使用OC
Swift程式碼引用OC,需依靠 Objective-C bridging header 將相關檔案暴露給Swift。
建立 Objective-C bridging header 有兩種方法
a.當你在Swift專案中嘗試建立OC檔案時,系統會自動幫你建立 Objective-C bridging header
b.自己建立 Objective-C bridging header
File > New > File > (iOS or OS X) > Source > Header File
切記,名字 一定要 是 專案工程名-Bridging-Header.
然後還有一步,在專案的 Build Settings 選項裡,要確保Swift Compiler 選項裡有這個 Bridging Header 檔案的設定,路徑必須指向檔案本身,而不是目錄!
13.API設計與函式命名對比
篇幅原因,這裡就不copy&&paste了,大家可以自行看原文或者譯文。這是一篇很好的API設計指導,終於可以拋棄Object-C的冗長命名了。
參考:Swift API設計 , 譯文
參考資料
Swift學習資料
http://www.swiftguide.cn/
https://github.com/ipader/SwiftGuide參考文獻
https://swift.org/about/
https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html
http://www.devtalking.com/articles/closure-expressions-in-swift/
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-XID_270
http://www.jianshu.com/p/989999a5104b
http://blog.csdn.net/xbenlang/article/details/31787973
http://swifter.tips/any-anyobject/
http://www.jianshu.com/p/082202b9dc17
http://swifter.tips/typealias/
https://www.andrewcbancroft.com/2015/01/29/converting-complex-objective-c-macros-swift-functions/
http://www.cnblogs.com/xilifeng/p/5243986.html
http://kittenyang.com/swiftandoc/
http://www.jianshu.com/p/fce426e4f1c4
結語
如果你在一門語言上,可以看到很多讓你曾痴狂,壓抑,熱愛,也失望的特性時。那就一定是Swift了。
本文比較了Swift3.0和Object-C在開發中主要的一些差別,限於篇幅(已經很長了)與作者本人認知能力,並未對Swift中更多特性做介紹或深究。如有疏漏或錯誤還望指正。
Swift就像一個四不像,因為var而像javascript,因為@objc而又保留了Object-C特性,因為內部類而多了點Java的影子,因為deinit而帶了些c++的氣味。但無論如何,Swift必定是將來!
參考:About Swift
相關文章
- object-c基礎Object
- Object-C複習Object
- 搶先看Swift3.0Swift
- Swift3.0 CurryingSwift
- object-c中疑問Object
- Object-C,物件和方法Object物件
- Object-C,陣列NSArrayObject陣列
- Object-C初體驗Object
- Swift3.0 - 遇到的坑Swift
- 五、Object-C 類NSObjectObject
- Object-C之(Null)與(Bool)ObjectNull
- Object-C,NSSet,不可變集合Object
- Object-C陣列和字典Object陣列
- Object-C,遍歷目錄Object
- Swift3.0整合極光推送Swift
- Swift3.0 -- 建構函式Swift函式
- swift3.0後的for in寫法Swift
- object-c 協議和委託Object協議
- object-c [self class] 和 [self _cmd]Object
- Object-C,檔案路徑APIObjectAPI
- iOS Swift3.0 UIView動畫詳解iOSSwiftUIView動畫
- Swift3.0 已出坑-適配 iOS10,專案遷移 Swift3.0 問題總結SwiftiOS
- Object-c KVC的使用和舉例Object
- Object-c Block的使用及說明ObjectBloC
- Object-C使用NSLog列印日誌Object
- Object-C,NumberDemo和StringDemoObject
- Swift3.0語言教程使用URL字串Swift字串
- Swift3.0 Delegate和Block的實現SwiftBloC
- Swift3.0語言教程組合字串Swift字串
- Object-C語言Block的實現方式ObjectC語言BloC
- Object-C,NSArraySortTest,陣列排序3種方式Object陣列排序
- Object-C,四則運算計算器Object
- Swift3.0 呼叫C函式-_silen_nameSwift函式
- Swift3.0語言教程使用路徑字串Swift字串
- Swift3.0語言教程替換子字串Swift字串
- 在swift3.0中列印物件的地址資訊Swift物件
- Swift3.0語言教程獲取C字串Swift字串
- Object-C中emoji與json的問題ObjectJSON