写在前面
命名空间namespace在C++、C#里面是一个常见概念,Swift中也引入了这样一个机制,下面来探索一下这个命名空间的来龙去脉。
Objective-C没有命名空间,为了避免冲突,Objective-C的类型一般都会加上两到三个字母的前缀,比如Apple保留的NS和UI前缀,各个系统框架的前缀,各个系统框架的前缀SK(StoreKit),CG(CoreGraphic)等。
Swift的命名空间是基于module而不是在代码中显示地指明,每个module代表了Swift中的一个命名空间。也就是说,同一个target里的类型名称还是不能相同的。
为什么需要命名空间
为了防止代码冲突
在开发中,尤其是在多模块开发中,很难保证模块之间的类名不会重复,为了保证不同模块下同名的类可以正常使用而不报错,引入命名空间来保证即使创建的类名一样,只要命名空间不一样,这些类也是不一样的,所以,这是一种安全机制,用命名空间来防止冲突。可以看出,Swift中的类名的完整形式其实是“命名空间+类名”。我们可以尝试在类中打印当前类来查看一下完整名字:
override func viewDidLoad() {
super.viewDidLoad()
print(self)
}
//打印结果是:<aa.viewcontroller: 0x7fec6a00e5c0></aa.viewcontroller: 0x7fec6a00e5c0>
命名空间查看与修改
从上面的打印结果来看,命名空间是我们项目的名字,那么如果查看呢?我们需要用源代码的形式打开Info.plist,可以看到里面有一个字段CFBundleExecutable,它对应的值就是命名空间。
如果要修改命名空间,注意不要直接编辑Info.plist,可以进入Build Settings中搜索Product Name,然后进行修改。
命名空间如何获取
既然知道可以通过Info.plist获取命名空间,那么如何在程序中获取呢?很显然需要解析Info.plist文件,拿到CFBundleExecutable对应的value值。
let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"]
// 返回的是一个可选型
print(namespace!)
命名空间在开发中的使用
由于Objective-C中没有命名空间,所以写起来很轻松。
//viewDidLoad中添加一个个控制器 - (void)viewDidLoad { [super viewDidLoad]; [self addNavigationChildVC:@"ContactViewController" :@"联系人" :@"tabbar_contacts" :@"tabbar_contactsHL"]; } //自定的方法中根据传进来的字符串创建控制器 -(void)addNavigationChildVC: (NSString *) vcName :(NSString *)title :(NSString *)nomalImageName :(NSString *)selectedImageName { //创建控制器 Class class = NSClassFromString(vcName); UIViewController *vc = [[class alloc]init]; ... }
Swift中命名空间的存在,如果按照上述做法得不到想要的结果,这时候就需要想办法进行处理
//viewDidLoad中添加一个个控制器 override func viewDidLoad() { super.viewDidLoad() addChildViewController(vcName: "ContactsViewController", title: "联系人", image: "tabbar_contacts", selectedImage: "tabbar_contactsHL") } //创建一个函数来将控制器的名字转成具体的类 func stringToVC(vcName:String) -> UIViewController? { //获取命名空间 guard let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as? String else { print("获取失败") return nil } //拼接完整的类 guard let vcClass = NSClassFromString(namespace + "." + vcName) else { print("拼接失败") return nil } //转换成UIViewController guard let vcType = vcClass as? UIViewController.Type else { print("转换失败") return nil } //根据类型创建对应的控制器 let vc = vcType.init() return vc }