OC,swift,javascript,Dart語言比較

弋小賤發表於2020-11-12

OC 是 動態型別語言&&強型別語言&&動態語言&&編譯型語言
swift 是 動態型別語言&&強型別語言&&靜態語言&&編譯型語言
javascript是一種動態型別語言和非強型別語言,與php類似
Dart 屬於是強型別語⾔,var 來宣告變數, Dart 會⾃推匯出資料型別這一點與swift很像,但是Dart在編譯期間不進⾏任何的型別檢查,⽽是在運⾏期進⾏型別檢查。

OC和swift混編注意點:
1.新建一個swift檔案時,會提示你是否建立橋接檔案,系統會建立“工程名-Bridging-Header.h”
2.配置工程Build Settings 設定Defines Module 為Yes,設定Product Module Name 為當前工程名
3.swift引用OC檔案,需要在“工程名-Bridging-Header.h”橋接檔案中匯入OC標頭檔案
4.OC引用swift檔案,在OC檔案中匯入#import “工程名-Swift.h”,注意swift檔案的方法名前面要加@objc,不然方法呼叫不了

array

flatMap: 對陣列的每一個元素做一次處理,返回處理後的陣列。
與map的區別是:
1.返回後的陣列中不存在nil, 同時也會把Optional解包。
2.flatMap還能把多維陣列變成一維陣列。

compactMap與flatMap的區別上面也說了,當閉包中的返回結果是可選的時候,使用compactMap代替flatMap

as、as!、as?

as
1.從派生類轉換為基類,向上轉型(upcasts) 子類賦值給父類 let person1 = tom as Person
2.消除二義性,數值型別轉換 消除整型還是浮點
3.switch語法檢測物件的型別 case let person1 as Student:

as!
向下轉型(Downcasting)時使用 父類賦值給子類

as? as? 和 as! 操作符的轉換規則完全一樣。但 as? 如果轉換不成功的時候便會返回一個 nil 物件。

as?判斷一個可選型別是否可以轉換為指定型別,如果可以,系統自動轉換取值,如果不可以,返回nil
if let name = dict1[“name”] as? String

as!將可選型別轉為指定型別,如果不能轉換,會崩潰

語法糖 ?? // 如果可選型別的name有值,則解包並使用,如果沒有值,則使用?? 後面的值 let str1 = name ?? “”

swift self 使用

1.引數名和自身的屬性名同名,消除訪問屬性,呼叫方法時所產生的歧義
class AClass {
var greeting: String
init(greeting: String) {
// 使用self區分屬性和引數
self.greeting = greeting
}
}
2.在便利建構函式中呼叫自身的指定建構函式時
convenience init() {
/*必須使用self,因為按照二段構造的規則,在第一階段初始化完成之前,無法使用self,
而且由於面嚮物件語言的特性,所有的初始化方法名都是init,沒有self,系統不知道呼叫誰的init */
self.init()
}
3.閉包中訪問自身屬性和呼叫自身方法時
UIView.animateWithDuration(0.25) { () -> Void in
/閉包可能被丟擲,其必須知道其中的方法和屬性屬於誰,所以要用self/
self.layoutIfNeeded()
}
4.被mutating修飾的值型別方法中,修改self屬性時
struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}

函式省略規則

func someFunction(_ firstParameterName: Int, secondParameterName: Int) {

}
someFunction(1, secondParameterName: 2)

1.將作為引數的函式變成無名函式

變化規則: 及閉包
a. 省略函式名 保留花括號 {}
b. 把引數列表和返回引數 (即函式型別) 寫在{}第一行
c. 用in 將 函式型別 與 函式體 區分開來

2.省略返回型別, 當編譯器已知該函式返回型別的時候

3.省略引數列表和in, 當沒有引數的時候

4.省略引數型別和引數列表括號,當編譯器已知引數型別的時候

5.省略 函式型別和in ,並使用$0,$1等訪問引數,當編譯器已知引數型別的時候

6.省略 函式型別和in ,不訪問函式引數

規則: 使用下劃線 _ 代替整個引數列表

// 定義函式
func test(callback:(_ str : String)->()){
callback(“test”)
}
// 方式1 呼叫函式:尾隨閉包
test(){(js:String) in
print(js)
}
如果閉包是唯一的引數,那麼()也可以省略
// 方式2
test{(js:String) in
print(js)
}

reversedNames = names.sorted(){$0>$1}
轉化
reverseNams = names.sorted{$0>$1}

7.當且僅當無名函式作為caller函式的最後一個引數的時候,
可以省(即把caller函式結束標誌略最後一個引數名稱的 “)” 放在倒數第二個引數的末尾)
將此無名函式緊跟在 “)” 後面,作為最後一個引數

private 表示程式碼只能在當前作用域或者同一檔案中同一型別的作用域中被使用,
fileprivate 表示程式碼可以在當前檔案中被訪問,而不做型別限定。

你可以在遵循協議的類中實現構造器,無論是作為指定構造器,還是作為便利構造器。無論哪種情況,你都必須為構造器實現標上 required

mutating
如果你在協議中定義了一個例項方法,該方法會改變遵循該協議的型別的例項,那麼在定義協議時需要在方法前加 mutating 關鍵字。這使得結構體和列舉能夠遵循此協議並滿足此方法要求

屬性:
1.如果建立了一個結構體的例項並將其賦值給一個常量,則無法修改該例項的任何屬性,即使有屬性被宣告為變數也不行
2.除儲存屬性外,類、結構體和列舉可以定義計算屬性。計算屬性不直接儲存值,而是提供一個 getter 和一個可選的 setter,來間接獲取和設定其他屬性或變數的值。
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
print(“get”)
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
print(“set”)
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
如果計算屬性的 setter 沒有定義表示新值的引數名,則可以使用預設名稱 newValue。

set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}

屬性觀察器監控和響應屬性值的變化,每次屬性被設定值的時候都會呼叫屬性觀察器,即使新值和當前值相同的時候也不例外。

class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print(“About to set totalSteps to (newTotalSteps)”)
}
didSet {
if totalSteps > oldValue {
print(“Added (totalSteps - oldValue) steps”)
}
}
}
}

3.只讀計算屬性的宣告可以去掉 get 關鍵字和花括號:
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
}
}

4.使用關鍵字 static 來定義型別屬性。在為類定義計算型型別屬性時,可以改用關鍵字 class 來支援子類對父類的實現進行重寫

陣列遍歷
// 方式1
for s in arrayM {
print(s)
}
// 方式2
for i in 0…<arrayM.count{
print(arrayM[i])
}
// 方式3: 部分遍歷
for i in arrayM[0…<2]{
print(arrayM[i])
}
// 方式4 : (同時獲取下標和對應的值)
for (index ,item) in arrayM.enumerated{
print(index) // 下標索引
print(item) // 下標的對應值
}

let array4 = array1 + arrayM

字典遍歷

// 遍歷鍵
for key in dict2.keys{
print(key)
}
// 遍歷值
for value in dict2.values{
print(value)
}

// 鍵值遍歷
for (k,v) in dict2{
print(k)
print(v)
}

類和結構體的區別:

類:物件導向的程式設計
結構體:面向協議的程式設計,比物件導向的程式設計能高階點,結構體可以實現類的所有方法

類方法:需要初始化物件呼叫 引用型別(類似於OC中的指標)
結構體:可以使用物件直接呼叫 值型別

結構體只需要給你變數的型別
類需要給出變數的初始值

類需要自己呼叫初始化方法
結構體不需要自己呼叫初始化方法

結構體的主要區別是結構體中有個關鍵字mutating 可以改變結構體的內部方法
結構體改變內容時受let的影響,也就是說結構體中儘量使用var 關鍵字

protocol中可以宣告變數,方便在協議方法中使用
協議方法的具體實現需要在extension中來實現
協議中不允許定類義方法,需改為靜態方法

在 extension 後面加上約束關鍵字【where】,並註明該協議只能被某個類(包括子類)所遵守,而且此時我們還可以拿到遵守該協議的控制器的view

1、private
private訪問級別所修飾的屬性或者方法只能在當前類裡訪問。

2、fileprivate
fileprivate訪問級別所修飾的屬性或者方法在當前的Swift原始檔裡可以訪問。

3、internal(預設訪問級別,internal修飾符可寫可不寫)
internal訪問級別所修飾的屬性或方法在原始碼所在的整個模組都可以訪問。
如果是框架或者庫程式碼,則在整個框架內部都可以訪問,框架由外部程式碼所引用時,則不可以訪問。
如果是App程式碼,也是在整個App程式碼,也是在整個App內部可以訪問。

4、public
可以被任何人訪問。但其他module中不可以被override和繼承,而在module內可以被override和繼承。

5,open
可以被任何人使用,包括override和繼承。

訪問順序:
現在的訪問許可權則依次為:open,public,internal,fileprivate,private。

Open 和 Public 級別可以讓實體被同一模組原始檔中的所有實體訪問,在模組外也可以通過匯入該模組來訪問原始檔裡的所有實體。通常情況下,你會使用 Open 或 Public 級別來指定框架的外部介面。Open 和 Public 的區別在後面會提到。
Internal 級別讓實體被同一模組原始檔中的任何實體訪問,但是不能被模組外的實體訪問。通常情況下,如果某個介面只在應用程式或框架內部使用,就可以將其設定為 Internal 級別。
File-private 限制實體只能在其定義的檔案內部訪問。如果功能的部分細節只需要在檔案內使用時,可以使用 File-private 來將其隱藏。
Private 限制實體只能在其定義的作用域,以及同一檔案內的 extension 訪問。如果功能的部分細節只需要在當前作用域內使用時,可以使用 Private 來將其隱藏。
Open 只能作用於類和類的成員,它和 Public 的區別如下:
Public 或者其它更嚴訪問級別的類,只能在其定義的模組內部被繼承。
Public 或者其它更嚴訪問級別的類成員,只能在其定義的模組內部的子類中重寫。
Open 的類,可以在其定義的模組中被繼承,也可以在引用它的模組中被繼承。
Open 的類成員,可以在其定義的模組中子類中重寫,也可以在引用它的模組中的子類重寫。

因為OC 的侷限性, 使得iOS 開發元件化程式設計變得不可能,得益於面嚮物件語言的特性 (封裝,繼承,多型) 在我們熟悉的設計模式中漸漸形成統一的軟體開發思想.
在抽取某些功能作為基類的不斷運用中,程式碼的可移植性逐漸減弱. 就如同一棵樹,從主幹到各個分支,每個分支再長成細枝末葉.程式碼的耦合性也相應增加.
隨著蘋果 swift 語言的推出, 對於傳統OC 語言取其精華,棄其糟粕. 加上開源社群眾大神的齊力維護.逐漸成為一門非常優秀的高階語言.
其中 swift中的面向協議程式設計思想 就是其中很有代表性的一項優秀組成部分.

3.靜態方法和例項方法的區分

 a).靜態方法常駐記憶體,例項方法不是,所以靜態方法效率高但佔記憶體。事實上,方法都是一樣的,在載入時機和佔用記憶體上,靜態方法和例項方法是一樣的,在型別第一次被使用時載入。呼叫的速度基本上沒有差別。

b).靜態方法在堆上分配記憶體,例項方法在堆疊上。事實上所有的方法都不可能在堆或者堆疊上分配記憶體,方法作為程式碼是被載入到特殊的程式碼記憶體區域,這個區域是不可寫的。

c).例項方法需要先建立例項才可以呼叫,比較麻煩,靜態方法不用,比較簡單。

d).靜態方法是靜態繫結到子類,不是被繼承。

e).一般使用頻繁的方法用靜態方法,用的少的方法用動態的。靜態的速度快,佔記憶體。動態的速度相對慢些,但呼叫完後,立即釋放類,可以節省記憶體,可以根據自己的需要選擇是用動態方法還是靜態方法。

f).靜態方法修改的是類的狀態,而物件修改的是各個物件的狀態。

g).類的例項呼叫是在類的生命週期中存在,當類沒有了以後,對應的例項也就沒有了,對應的方法也就沒有了。靜態類不然,只要你引用了那個靜態類的名稱空間,它就會一直存在,直到我們推出系統。

Swiftify 一個比較好的自動化工具

oc中建立swift檔案要建立qiaoi

Swift和OC的區別有很多,這裡簡要總結這幾條:

Swift Objective-C
語言特性 靜態語言,更加安全 動態語言, 不那麼安全
語法 更精簡 冗長
名稱空間 有 無
方法呼叫 直接呼叫,函式表呼叫,訊息轉發 訊息轉發
泛型/元組/高階函式 有 無
語言效率 效能更高,速度更快 略低
檔案特性 .swift 單檔案 .h/.m包含標頭檔案
程式設計特性 可以更好的實現函數語言程式設計/響應式程式設計 物件導向程式設計

4、if let 、 guard let 的用法

if let 處理空的情況

guard let 縮減else 程式碼邏輯更清晰

縮減程式碼量,安全處理資料邏輯

Swift沒有property,也沒有copy,nonatomic等屬性修飾詞,只有表示屬性是否可變的let和var。

1、Swift語句中不需要加分號;。
2、關於Bool型別更加嚴格,Swift不再是OC中的非0就是真,真假只對應true和false。
3、Swift類內一般不需要寫self,但是閉包內是需要寫的。
4、Swift是強型別語言,必須要指定明確的型別。在Swift中Int和Float是不能直接做運算的,必須要將他們轉成同一型別才可以運算。
5、Swift拋棄了傳統的++,–運算,拋棄了傳統的C語言式的for迴圈寫法,而改為for-in。
6、Swift的switch操作,不需要在每個case語句結束的時候都新增break。
7、Swift對enum的使用做了大的很擴充套件,可以支援任意型別,而OC列舉僅支援Int型別,如果要寫相容程式碼,要選擇Int型列舉。
8、Swift程式碼要想被OC呼叫,需要在屬性和方法名前面加上@objc。
9、Swift獨有的特性,如泛型,struct,非Int型的enum等被包含才函式引數中,即使新增@objc也不會被編譯器通過。
10、Swift支援過載,OC不支援。
11、帶預設值的Swift函式再被OC呼叫時會自動展開。

id型別和AnyObject

id 是一種通用的物件型別,它可以指向屬於任何類的物件,在OC中即是可以代表所有繼承於NSObject的物件。
AnyObject可以代表任何class型別的例項。
Any可以代表任何型別,甚至包括func型別。

引用型別和值型別
struct 和 enum 是值型別,類 class 是引用型別。
String,Array和 Dictionary都是結構體,因此賦值直接是拷貝,而NSString, NSArray 和NSDictionary則是類,所以是使用引用的方式。所以並不能動態編譯。
struct 比 class 更“輕量級”,struct 分配在棧中,class 分配在堆中。

protocol(協議/代理)

Swift中對protocol的使用拓寬了許多,不光是class物件,struct和enum也都可以實現協議。需要注意的是struct和enum為指引用型別,不能使用weak修飾。只有指定當前代理只支援類物件,才能使用weak。將上面的程式碼轉成對應的Swift程式碼,就是

通知
通知名由字串變成了NSNotification.Name的結構體
NotificationCenter.default.post(name: NSNotification.Name(rawValue: “NotificationName”), object: self)

OC可以通過是否將方法宣告在.h檔案表明該方法是否為私有方法。Swift中沒有了.h檔案,對於方法的許可權控制是通過許可權關鍵詞進行的,各關鍵詞許可權大小為: private < fileprivate < internal < public < open

初始化方法
對於初始化方法OC先呼叫父類的初始化方法,然後初始自己的成員變數。Swift先初始化自己的成員變數,然後在呼叫父類的初始化方法。

Swift中全域性變數是懶載入,在AppDelegate中被初始化,之後所有的呼叫都會使用該例項。這保證了全域性變數的構造器(initializer)只會被呼叫一次

型別特型

1型別安全,編譯程式碼時進⾏型別檢查
2型別推斷,C或者Objective-C ⽐起來 Swift 很少需要宣告型別

型別轉換

as 從派生類轉換為基類,向上轉型(upcasts)

as! 向下轉型(Downcasting)時使用。由於是強制型別轉換,如果轉換失敗會報 runtime 執行錯誤

as? 和 as! 操作符的轉換規則完全一樣。但 as? 如果轉換不成功的時候便會返回一個 nil 物件。

型別別名

布林值:嚴格的布林型別

oc沒有的 (元組和可選型別)

元組:Swift 增加OC中沒有的⾼階資料型別⽐如元組(Tuple)(404, “Not Found”)

1.賦值:位數順序對應,只需要⼀部分元組值,分解的時候可以把要忽略的部分⽤下劃線( _ )標記

2.元素訪問:通過下標來訪問元組中的單個元素,元素命名,通過名字來獲取這些元素的值

可選型別 來處理值可能缺失(如型別轉換),swift中的nil不是指標(oc:指向不存在物件的指標),它是⼀個確定的值,⽤來表示值缺失,不只修飾是物件型別

可選繫結: if 語句和 nil ⽐較來判斷⼀個可選值是否包含值

! 來獲取⼀個不存在的可選值會導致運⾏時錯誤。使⽤ ! 來強制解析值之前,⼀定要確定可選包含⼀個⾮ nil 的值。

錯誤處理

throws ⼀個函式可以通過在宣告中新增 throws 關鍵詞來丟擲錯誤訊息。當你的函式能丟擲錯誤訊息時,你應該在表示式中前置 try 關鍵詞。

func canThrowAnError() throws { // 這個函式有可能丟擲錯誤

}

do{
try canThrowAnError() // 沒有錯誤訊息丟擲
}catch {
// 有⼀個錯誤訊息丟擲
}

assert(age >= 0, “A person’s age cannot be less than zero”)

運算子

區間運算子:a…<b
半開:for i in 0…<count,單側:for name in names[2…]

空合運算子 ??

操作符 + 可實現陣列和字串的拼接和合並

let s = " / 2 3 4 ? / "
// 替換 replacingOccurrences(of: " ", with: “-”))
// 分割 s.components(separatedBy: “/”)
// 拼接let a = [“1”, “2”, “3”] a.joined(separator: “-”)

陣列的遍歷
var someInts = Int

for item in shoppingList

for (index, value) in shoppingList.enumerated() .enumerated()區分了陣列和字典

集合:當集合元素順序不重要時或者希望確保每個元素只出現⼀次時可以使⽤集合⽽不是陣列。
遍歷
for (airportCode, airportName) in airports

for airportCode in airports.keys

for airportName in airports.values

某個字典的鍵集合或者值集合來作為某個接受 Array
let airportCodes = String
let airportNames = String

控制流

Repeat-While,和 while 的區別是在判斷迴圈條件之前,先執⾏⼀次迴圈的程式碼塊。然後重複迴圈直到條件為 false

switch 條件的不存在隱式貫穿
1.Swift 中,當匹配的 case 分⽀中的程式碼執⾏完畢後,程式會終⽌ switch 語句,⽽不會繼續執⾏下⼀個 case 分⽀
、不需要在case 分⽀中顯式地使⽤ break 語句。需要貫穿使⽤ fallthrough
2.每⼀個 case 分⽀都必須包含⾄少⼀條語句,為了讓單個 case 同時匹配 a 和 A ,可以將這個兩個值組合成⼀個複合匹配,並且⽤逗號分
開,case 分⽀的模式也可以是⼀個值的區間,case 分⽀的模式可以使⽤ where 語句來判斷額外的條件。

case “a”: // ⽆效,這個分⽀下⾯沒有語句
case “A”:
print(“The letter A”)

case “a”, “A”:
print(“The letter A”)

case 1…<5:
naturalCount = “a few”

guard 語句來要求條件必須為真時

guard let name = person[“name”] else {
return
}

引用型別和值型別
swift 結構體,列舉和元組都是值型別,陣列,字典,集合,整,浮點字串也都是結構體都是值型別在記憶體上是儲存在棧上

閉包的本質還是函式,函式和閉包都是引用型別

Swift的閉包捕獲變數是在執行閉包時捕獲,在閉包內部對外部變數值的改變也會影響到外部,OC的block是在block體內捕獲,在函式內部與外部修改這個變數值不會產生相互影響,Swift捕獲列表 使用[] 加in 實現跟oc一樣的效果

結構體和類的抉擇是那些條件下用結構體:

結構體的主要目的是用來封裝少量相關簡單資料值。
有理由預計一個結構體例項在賦值或傳遞時,封裝的資料將會被拷貝而不是被引用。
任何在結構體中儲存的值型別屬性,也將會被拷貝,而不是被引用。
結構體不需要去繼承另一個已存在型別的屬性或者行為。

與objective-c不同的是,structure和enumeration也可以擁有方法(method),其中方法可以為例項方法(instance method),也可以為類方法(type method),例項方法是和型別的一個例項繫結的。例項方法中是不可以(不建議)修改值型別的屬性,如果要修改用 mutating修飾

private (set) 修飾變數的賦值範圍

擴充套件和 Objective-C 的分類很相似。(與Objective-C 分類不同的是,Swift 擴充套件是沒有名字的。)

BehaviorRelay 通過vaule訪問到 訊號轉換成值 (只讀屬性)
通過accept 接受訊號 Observable 的值 可以將 Observable轉換成 BehaviorRelay
asObservable 將 BehaviorRelay轉化成 Observable

將 Observable 轉換為 Driver 使用 asDriver() 方法,Driver 轉換為 Observable 使用 asObservable() 方法
通過drive方法將其訊號繫結到UI控制元件上

rxswift tablevie或者collectionView 牽涉到重用就要解決銷燬重複新增問題,解決是
1.基類封裝 在重用方法中重置重用袋

在oc 中的列舉型別的成員變數是整型,第一個成員變數的值預設為0,第二個為1,順次遞增。但是在swift裡,列舉型別的成員變數型別可以為字串、整型、浮點數

在Swift中,一個例項可以是Class,也可以是Struct或Enum,所以,為了表示一個例項是否具有比較是否相等的能力,就要依靠 protocol來實現了

Equalable協議定義了比較兩個例項的方式
在Objective-C中,基本上所有的例項都是NSObject或其子類的例項。由於NSObject有一個方法isEqual,因此所有的例項都具有了這個比較是否相等的能力
但是在Swift中,一個例項可以是Class,也可以是Struct或Enum,所以,為了表示一個例項是否具有比較是否相等的能力,就要依靠 protocol來實現了。
能夠判斷是否相等的型別都要實現這個協議:對於實現這個協議的型別,想判斷二者是否相等的時候,就可以使用==運算子來判斷了。

Provider

MultiProvider:包裹根節點

Providers:預先定義共享的Model provider 常量或者方法 ChangeNotifierProvider 資料改變而被通知更新 ChangeNotifierProxyProvider 一個ModelA依賴另一個ModelB,ModelB更新

Provider.of(context) / Widget Consumer

相關文章