
傳送門
Objective-C 一個(gè)一直以來令人詬病的地方就是沒有命名空間,在應(yīng)用開發(fā)時(shí),所有的代碼和引用的靜態(tài)庫最終都會(huì)被編譯到同一個(gè)域和二進(jìn)制中。這樣的后果是一旦我們有重復(fù)的類名的話,就會(huì)導(dǎo)致編譯時(shí)的沖突和失敗。為了避免這種事情的發(fā)生,Objective-C 的類型一般都會(huì)加上兩到三個(gè)字母的前綴,比如 Apple 保留的 NS 和 UI 前綴,各個(gè)系統(tǒng)框架的前綴 SK (StoreKit),CG (CoreGraphic) 等。Objective-C 社區(qū)的大部分開發(fā)者也遵守了這個(gè)約定,一般都會(huì)將自己名字縮寫作為前綴,把類庫命名為 AFNetworking 或者 MBProgressHUD 這樣。這種做法可以解決部分問題,至少我們?cè)谥苯右貌煌说膸鞎r(shí)沖突的概率大大降低了,但是前綴并不意味著不會(huì)沖突,有時(shí)候我們確實(shí)還是會(huì)遇到即使使用前綴也仍然相同的情況。另外一種情況是可能你想使用的兩個(gè)不同的庫,分別在它們里面引用了另一個(gè)相同的很流行的第三方庫,而又沒有更改名字。在你分別使用這兩個(gè)庫中的一個(gè)時(shí)是沒有問題的,但是一旦你將這兩個(gè)庫同時(shí)加到你的項(xiàng)目中的話,這個(gè)大家共用的第三方庫就會(huì)和自己發(fā)生沖突了。
在 Swift 中,由于可以使用命名空間了,即使是名字相同的類型,只要是來自不同的命名空間的話,都是可以和平共處的。和 C# 這樣的顯式在文件中指定命名空間的做法不同,Swift 的命名空間是基于 module 而不是在代碼中顯式地指明,每個(gè) module 代表了 Swift 中的一個(gè)命名空間。也就是說,同一個(gè) target 里的類型名稱還是不能相同的。在我們進(jìn)行 app 開發(fā)時(shí),默認(rèn)添加到 app 的主 target 的內(nèi)容都是處于同一個(gè)命名空間中的,我們可以通過創(chuàng)建 Cocoa (Touch) Framework 的 target 的方法來新建一個(gè) module,這樣我們就可以在兩個(gè)不同的 target 中添加同樣名字的類型了:
- 在swift 中存在命名空間,同一命名空間下全局共享.
- 第三方框架使用:直接拖入項(xiàng)目中,從屬于一個(gè)命名空間,很可能沖突(所以盡量用cocoapod)
- NSClassFromString(反射機(jī)制): 最重要的目的,就是解耦 。例子:(反射機(jī)制和工廠方法)
// MyFramework.swift
// 這個(gè)文件存在于 MyFramework.framework 中
public class MyClass {
public class func hello() {
print("hello from framework")
}
}
// MyApp.swift
// 這個(gè)文件存在于 app 的主 target 中
class MyClass {
class func hello() {
print("hello from app")
}
}
在使用時(shí),如果出現(xiàn)可能沖突的時(shí)候,我們需要在類型名稱前面加上 module 的名字 (也就是 target 的名字):
MyClass.hello()
// hello from app
MyFramework.MyClass.hello()
// hello from framework
因?yàn)槭窃?app 的 target 中調(diào)用的,所以第一個(gè) MyClass 會(huì)直接使用 app 中的版本,第二個(gè)調(diào)用我們指定了 MyFramework 中的版本。
另一種策略是使用類型嵌套的方法來指定訪問的范圍。常見做法是將名字重復(fù)的類型定義到不同的 struct 中,以此避免沖突。這樣在不使用多個(gè) module 的情況下也能取得隔離同樣名字的類型的效果:
struct MyClassContainer1 {
class MyClass {
class func hello() {
print("hello from MyClassContainer1")
}
}
}
struct MyClassContainer2 {
class MyClass {
class func hello() {
print("hello from MyClassContainer2")
}
}
}
使用時(shí):
MyClassContainer1.MyClass.hello()
MyClassContainer2.MyClass.hello()
其實(shí)不管哪種方式都和傳統(tǒng)意義上的命名空間有所不同,把它叫做命名空間,更多的是一種概念上的宣傳。不過在實(shí)際使用中只要遵守這套規(guī)則的話,還是能避免很多不必要的麻煩的,至少唾手可得的是我們不再需要給類名加上各種奇怪的前綴了。
實(shí)戰(zhàn):通過字符串獲取 類
在 AppDelegate 中設(shè)置window 的根控制器
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow()
window?.backgroundColor = UIColor.white
// 設(shè)置根控制器,需要添加命名空間 -》 通常是 "項(xiàng)目名.ClassName"
// 注意: 項(xiàng)目名 不能有 數(shù)字 特殊符號(hào)
// (1) 獲取命名空間
let nameSpace = Bundle.main.infoDictionary?["CFBundleName"] as! String
//(2) 拼接
let clsName = nameSpace + "." + "ViewController"
//AnyClass? ->視圖控制器的類型
let cls = NSClassFromString(clsName) as? UIViewController.Type
// 使用類名創(chuàng)建 控制器
let vc = cls?.init()
window?.rootViewController = vc
window?.makeKeyAndVisible()
return true
}