新的訪問控制fileprivate和open
在swift 3中新增加了兩種訪問控制權限 fileprivate和 open。下面將對這兩種新增訪問控制做詳細介紹。
在原有的swift中的 private其實並不是真正的私有,如果一個變量定義為private,在同一個文件中的其他類依然是可以訪問到的。這個場景在使用extension的時候很明顯。
class User {
private var name = "private"
}
extension User{
var accessPrivate: String {
return name
}
}
這樣帶來了兩個問題:
當我們標記為private時,意為真的私有還是文件內可共享呢?當我們如果意圖為真正的私有時,必須保證這個類或者結構體在一個單獨的文件裡。否則可能同文件裡其他的代碼訪問到。由此,在swift 3中,新增加了一個 fileprivate來顯式的表明,這個元素的訪問權限為文件內私有。過去的private對應現在的fileprivate。現在的private則是真正的私有,離開了這個類或者結構體的作用域外面就無法訪問。
open則是彌補public語義上的不足。
現在的pubic有兩層含義:
繼承是一件危險的事情。尤其對於一個framework或者module的設計者而言。在自身的module內,類或者屬性對於作者而言是清晰的,能否被繼承或者override都是可控的。但是對於使用它的人,作者有時會希望傳達出這個類或者屬性不應該被繼承或者修改。這個對應的就是 final。
final的問題在於在標記之後,在任何地方都不能override。而對於lib的設計者而言,希望得到的是在module內可以被override,在被import到其他地方後其他用戶使用的時候不能被override。
這就是 open產生的初衷。通過open和public標記區別一個元素在其他module中是只能被訪問還是可以被override。
下面是例子:
/// ModuleA:
// 這個類在ModuleA的范圍外是不能被繼承的,只能被訪問
public class NonSubclassableParentClass {
public func foo() {}
// 這是錯誤的寫法,因為class已經不能被繼承,
// 所以他的方法的訪問權限不能大於類的訪問權限
open func bar() {}
// final的含義保持不變
public final func baz() {}
}
// 在ModuleA的范圍外可以被繼承
open class SubclassableParentClass {
// 這個屬性在ModuleA的范圍外不能被override
public var size : Int
// 這個方法在ModuleA的范圍外不能被override
public func foo() {}
// 這個方法在任何地方都可以被override
open func bar() {}
///final的含義保持不變
public final func baz() {}
}
/// final的含義保持不變
public final class FinalClass { }
/// ModuleB:
import ModuleA
// 這個寫法是錯誤的,編譯會失敗
// 因為NonSubclassableParentClass類訪問權限標記的是public,只能被訪問不能被繼承
class SubclassA : NonSubclassableParentClass { }
// 這樣寫法可以通過,因為SubclassableParentClass訪問權限為 `open`.
class SubclassB : SubclassableParentClass {
// 這樣寫也會編譯失敗
// 因為這個方法在SubclassableParentClass 中的權限為public,不是`open'.
override func foo() { }
// 這個方法因為在SubclassableParentClass中標記為open,所以可以這樣寫
// 這裡不需要再聲明為open,因為這個類是internal的
override func bar() { }
}
open class SubclassC : SubclassableParentClass {
// 這種寫法會編譯失敗,因為這個類已經標記為open
// 這個方法override是一個open的方法,則也需要表明訪問權限
override func bar() { }
}
open class SubclassD : SubclassableParentClass {
// 正確的寫法,方法也需要標記為open
open override func bar() { }
}
open class SubclassE : SubclassableParentClass {
// 也可以顯式的指出這個方法不能在被override
public final override func bar() { }
}
Swift 3.0 中方法的返回值必須有接收否則會報警告,當然其實主要目的是為了避免開發人員忘記接收返回值的情況,但是有些情況下確實不需要使用返回值可以使用"_"接收來忽略返回值。當然你也可以增加@discardableResult聲明,告訴編譯器此方法可以不用接收返回值。
struct Caculator {
func sum(a:Int,b:Int) -> Int {
return a + b
}
@discardableResult
func func1(a:Int,b:Int) ->Int {
return a - b + 1
}
}
let ca = Caculator()
ca.sum(a: 1, b: 2) // 此處會警告,因為方法有返回值但是沒有接收
let _ = ca.sum(a: 1, b: 2) // 使用"_"接收無用返回值
ca.func1(a: 1, b: 2) // 由於func1添加了@discardableResult聲明,即使不接收返回值也不會警告
在Swift的歷史版本中出現過在調用函數時不需要指定任何函數參數(或者從第二個參數開始指定參數名),在調用方法時則必須從第二個參數開始必須指定參數名等多種情況,而在Swift3.0中不管是函數還是方法都必須從第一個參數開始必須指定參數名(當然可以使用“_”明確指出調用時省略參數)。
// 從第一個參數就必須指定參數名,除非使用"_"明確指出省略參數
func sum(num1:Int,num2:Int)->Int{
return num1 + num2
}
sum(num1: 1, num2: 2) // old: sum(1,2)或者sum(1, num2: 2)
取消var參數
//func increase(var a:Int){
// a += 1
//}
// 上面的代碼會報錯,可改寫成
func increase(a:Int){
var a = a
a += 1
}
inout參數修飾改放到類型前
//func increase(inout a:Int) {
// a += 1
//}
// 上面的代碼會報錯,可改為
func increase( a:inout Int) {
a += 1
}
Swift 3.0 中方法的返回值必須有接收否則會報警告,當然其實主要目的是為了避免開發人員忘記接收返回值的情況,但是有些情況下確實不需要使用返回值可以使用"_"接收來忽略返回值。當然你也可以增加@discardableResult聲明,告訴編譯器此方法可以不用接收返回值。
struct Caculator {
func sum(a:Int,b:Int) -> Int {
return a + b
}
@discardableResult
func func1(a:Int,b:Int) ->Int {
return a - b + 1
}
}
let ca = Caculator()
ca.sum(a: 1, b: 2) // 此處會警告,因為方法有返回值但是沒有接收
let _ = ca.sum(a: 1, b: 2) // 使用"_"接收無用返回值
ca.func1(a: 1, b: 2) // 由於func1添加了@discardableResult聲明,即使不接收返回值也不會警告
Swift3.0對於可選類型控制更加嚴謹,隱式可選類型和其他類型的運算之後獲得的是可選類型而不是隱式可選類型。
let a:Int! = 1 let b = a + 1 // 此時強制解包,b是Int型 let c = a // 注意此時c是Int? 在之前的Swift版本中c是Int!
Selector的改變其實從1.0到3.0經歷了多次變化,從最早的@Selector("method:")到現在的#selector(method(param1:))可以說經歷了多次修改,好在它變得越來越好,畢竟字符串操作對於語法檢查來說是很無助的。
class MyClass {
@objc func sum(a:Int,b:Int) -> Int {
return a + b
}
func func1(){
let _ = #selector(sum(a:b:))
}
}
// old: Swift 2.2
//class MyClass {
// @objc func sum(a:Int,b:Int) -> Int {
// return a + b
// }
//
// func func1(){
// let _ = #selector(sum(_:b:))
// }
//}
在Swift3.0之前如果要定義協議中可選方法,只需要給協議加上@objc之後方法使用optional修飾就可以了,但是Swift3.0中除了協議需要@objc修飾,可選方法也必須使用@objc來修飾。
@objc protocol MyProtocol {
@objc optional func func1() //old: optional func func1()
func func2()
}
var d = 1 d++ //報錯,可以改寫成 d += 1 或者 d = d + 1
//for var i = 0 ;i < 10 ; i += 1 {
// debugPrint(i)
//}
// 上面的代碼會報錯,可改寫成如下代碼
for i in 0 ..< 10 {
debugPrint(i)
}
// 1.去掉前綴
let url1 = URL(string: "www.cmjstudio.com")
let isFileURL = url1?.isFileURL //old:url1.fileURL ,現在更加注重語意
let data1 = Data() //NSData
// 2.方法名使用動詞,其他名詞、介詞等作為參數或移除
var array1 = [1,2,3]
array1.append(contentsOf: [4,5,6]) // old:array1.appendContentsOf([4,5,6])
array1.remove(at: 0) // old:array1.removeAtIndex(0)
// 3.不引起歧義的情況下盡量消除重復
let color1 = UIColor.red() // old:var color1 = UIColor.redColor()
// 4.枚舉成員首字母變成小寫
let label1 = UILabel()
label1.textAlignment = .center // old:label1.textAlignment = .Center
// 5.按鈕的Normal狀態去掉
let btn1 = UIButton()
btn1.setTitle("hello", for: UIControlState()) // 相當於Normal狀態
Swift發展初期很多類庫的引入依然保持的ObjC風格,但是ObjC由於根出C語言,因此很多操作其實並不是對象和方法操作而是C語言的函數形式。到了Swift3.0之後這一現狀將發生變化,全局函數將會變成某些類型的方法;某些常量定義將以某個枚舉類型的成員來表示。
let rect1 = CGRect(x: 0, y: 0, width: 100, height: 100)
// 下面的代碼將要報錯,3.0完全廢除這種類C的風格
//let rect1 = CGRectMake(0, 0, 100, 100)
if let context1 = UIGraphicsGetCurrentContext() {
CGContext.fillPath(context1) // old:CGContextFillPath(context1!)
}
// GCD的改變
let queue = DispatchQueue(label: "myqueue")
queue.async {
debugPrint("hello world!")
}
// old:
//let queue = dispatch_queue_create("myqueue", nil)
//dispatch_async(queue) {
// debugPrint("hello world!")
//}
// 相關常量定義被移到枚舉內部
NotificationCenter.defaultCenter().addObserver(self, selector: #selector(userDefaultChange()), name: UserDefaults.didChangeNotification, object: nil)
//old:NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(userDefaultChange()), name: NSUserDefaultsDidChangeNotification, object: nil)
let array1 = [1,2,3]
let next = array1.index(after: 0) // old:let start = array1.startIndex let next = start.successor()
let first = array1.first { (element) -> Bool in // 增加新的方法
element > 1
}
let r = Range(0..<3) //old: let _ = NSRange(location: 0, length: 3)
// 下面的代碼必須在控制器中執行,用於遍歷當前view及其父視圖
for subview in sequence(first: self.view, next: { $0?.superview }){
debugPrint(subview)
}
Float、Double、CGFloat使用了新的協議,提供了提供 IEEE-754標准的屬性和方法。
let a = 2 * Float.pi // old: let a = 2 * M_PI let b = 2.0 * .pi // 注意前面是浮點型,後面可以省略Float
random()->arc4random()