美文网首页SwiftSwift好文收藏
Swift中通过类名动态创建类

Swift中通过类名动态创建类

作者: 蓝色的风 | 来源:发表于2018-01-04 15:21 被阅读339次

    项目中根据类名创建类,在实际项目中用处还是挺多的。举个简单的例子,下面是支付宝的个人中心界面


    支付宝个人中心

    点击每个cell进入不同的试图控制器,常见的写法就是根据indexPath去判断,if else if添加判断。这样的代码质量不高,而且还有#import很多其他的控制器,相对而言在OC中可以根据类名动态的创建视图控制器,

    UIViewController *baseVC = [[NSClassFromString(className) alloc]initWithNibName:className bundle:nil];
    [self.navigationController pushViewController:baseVC animated:YES];
    

    在个人中心视图中创建一个试图控制器的数组,根据地点的cell的indexPath获取到对应的试图控制器的类名(他们有统一的一个基类),我们可以通过Class类型就可以调用alloc来分配内存,调用init方法来初始化。

    在swift中这样的方法就不行了,因为一看到Swift中NSClassFromString返回的是AnyClass类型,而这个AnyClass类型为public typealias AnyClass = AnyObject.Type,这个Type具体是什么呢?为什么option+点击进不去,无法查看呢?AnyObject其实只是一个空协议,难道.Type是自动有的吗?这个本人也不清楚。 className的类型是UIViewController.type

    看来我们用这种方式在swift中是不行了,获取的只是一个type类型的类,在swift中想过类名去动态创建类,我们还需要知道命名空间这个东西😂😂😂。

    Objective-C一直以来令人诟病的地方就是没有命名空间,在应用开发时,所有的代码和引用的静态库最终都会被编译到同一个域和二进制中。这样的后果是一旦我们有重复的类名的话,就会导致编译出错和冲突。为了避免这种情况,Objective-C的类型一般都会加上两到三个字母的前缀,比如Apple保留的NS和UI前缀,各个系统框架的前缀SK(StoreKit),CG(CoreGraphic)等。Objective-C社区的大部分开发者也遵循这个约定,一般都会将自己的名字作为前缀,把类库命名为AFNetworking或者MBProgressHUD这样,这种做法可以解决部分问题,至少我们在直接引用不同人的库时冲突的概率大大降低了,但是前缀并不意味着不会冲突,有时候我们确实还是会遇到即使使用前缀也仍然相同的情况。另外一种情况是可能你想使用的两个不同的库,分别在它们里面引用了另一个相同的很流行的第三方库,而又没有更改名字。在你分别使用这两个库中的一个时是没有问题的,但是一旦你将这两个库同时加到你的项目中的话,这个大家共用的第三方库就会和自己发生冲突了。

    在 Swift 中,由于可以使用命名空间了,即使是名字相同的类型,只要是来自不同的命名空间的话,都是可以和平共处的。和 C# 这样的显式在文件中指定命名空间的做法不同,Swift 的命名空间是基于 module 而不是在代码中显式地指明,每个 module 代表了 Swift 中的一个命名空间。也就是说,同一个 target 里的类型名称还是不能相同的。在我们进行 app 开发时,默认添加到 app 的主 target 的内容都是处于同一个命名空间中的,这个target的默认模块名称就是这个项目的名称(可以在target的Build Settings—Product Module Name配置)图中红框中的就是项目工程名(这是swift默认的) Swift中的命名空间

    注意点

    1:不同项目中的命名空间是不一样的, 默认情况下命名空间的名称就是当前项目的名称
    2:因为Swift可以通过命名空间来解决重名的问题,所以在做Swift开发时尽量使用cocoapods来集成三方框架,这样可以有效的避免类名重复
    3:因为Swift中有命名空间,所以通过一个字符串来创建一个类和OC中也不太一样了, OC中可以直接通过类名创建一个类,而Swift中如果想通过类名来创建一个类必须加上命名空间。
    

    下面我们采用swift的命名空间,动态的去创建并实例化一个类

    //1:动态获取命名空间  
    guard   let spaceName = Bundle.main.infoDictionary!["CFBundleExecutable"] as? String else {  
        print("获取命名空间失败")  
        return  
    }  
    let vcClass: AnyClass? = NSClassFromString(nameSpace + "." + name) //VCName:表示试图控制器的类名
     // Swift中如果想通过一个Class来创建一个对象, 必须告诉系统这个Class的确切类型
    guard let typeClass = vcClass as? UIViewController.Type else {
         print("vcClass不能当做UIViewController")
         return
     }  
    let myVC = typeClass.init() 
    //或者加载xib;   let myVC = typeClass.init(nibName: name, bundle: nil)
    

    通过这种方式就可以和Objective-C实现相同的功能了🤣🤣🤣🤣🤣

    参考文章

    命名空间
    命名空间-tips

    相关文章

      网友评论

        本文标题:Swift中通过类名动态创建类

        本文链接:https://www.haomeiwen.com/subject/xqqrnxtx.html