objc引用swift - CSDN
  • SwiftObjC互相调用

    千次阅读 2016-06-20 22:33:59
    Swift的设计的初衷就是摆脱ObjC沉重的历史包袱,毕竟ObjC的历史太过悠久,相比于很多现代化语言它缺少一些很酷的语法特性,而且ObjC的语法和其他语言相比差别很大。但是Apple同时也不能忽视ObjC的地位,毕竟ObjC经过...

    Swift的设计的初衷就是摆脱ObjC沉重的历史包袱,毕竟ObjC的历史太过悠久,相比于很多现代化语言它缺少一些很酷的语法特性,而且ObjC的语法和其他语言相比差别很大。但是Apple同时也不能忽视ObjC的地位,毕竟ObjC经过二十多年的历史积累了大量的资源(开发者、框架、类库等),因此在Swift推出的初期必须考虑兼容ObjC。但同时Swift和ObjC是基于两种不同的方式来实现的(例如ObjC可以在运行时决定对象类型,但是Swift为了提高效率要求在编译时就必须确定对象类型),所以要无缝兼容需要做大量的工作。而作为开发人员我们有必要了解两种语言之间的转化关系才能对Swift有更深刻的理解。

    Swift和ObjC映射关系

    其实从前面的例子中大家不难发现Swift和ObjC必然存在着一定的映射关系,例如对于文件的操作使用了字符串的writeToFile方法,在网络请求时使用的NSURLSession,虽然调用方式不同但是其参数完全和做ObjC开发时调用方式一致。原因就是Swift编译器自动做了映射,下面列举了部分Swift和ObjC的映射关系帮助大家理解:

    Swift ObjC 备注
    AnyObject id(ObjC中的对象任意类型) 由于ObjC中的对象可能为nil,所以Swift中如果用到ObjC中类型的参数会标记为对应的可选类型
    Array、Dictionary、Set NSArray、NSDictionary、NSSet 注意:ObjC中的数组和字典不能存储基本数据类型,只能存储对象类型,这样一来对于Swift中的Int、UInt、Float、Double、Bool转化时会自动桥接成NSNumber
    Int NSInteger、NSUInteger 其他基本类型情况类似,不再一一列举
    NSObjectProtocol NSObject协议(注意不是NSObject类) 由于Swift在继承或者实现时没有类的命名空间的概念,而ObjC中既有NSObject类又有NSObject协议,所以在Swift中将NSObject协议对应成了NSObjectProtocol
    CGContext CGContextRef

    Core Foundation中其他情况均是如此,由于Swift本身就是引用类型,在Swift不需要再加上“Ref”

    ErrorType NSError  
    “ab:" @selector(ab:)

    Swift可以自动将字符串转化成成selector

    @NSCopying copy属性  
    init(x:X,y:Y) initWithX:(X)x y:(Y)y 构造方法映射,Swift会去掉“With”并且第一个字母小写作为其第一个参数,同时也不需要调用alloc方法,但是需要注意ObjC中的便利工厂方法(构建对象的静态方法)对应成了Swift的便利构造方法
    func xY(a:A,b:B) void xY:(A)a b:(B)b  
    extension(扩展) category(分类) 注意:不能为ObjC中存在的方法进行extension
    Closure(闭包) block(块) 注意:Swift中的闭包可以直接修改外部变量,但是block中要修改外部变量必须声明为__block

    Swift兼容大部分ObjC(通过类似上面的对应关系),多数ObjC的功能在Swift中都能使用。当然,还是有个别地方Swift并没有考虑兼容ObjC,例如:Swift中无法使用预处理指令(例如:宏定义,事实上在Swift中推举使用常量定义);Swift中也无法使用performSelector来执行一个方法,因为Swift认为这么做是不安全的。

    相反,如果在ObjC中使用Swift也同样是可行的(除了个别Swift新增的高级功能)。Swift中如果一个类继承于NSObject,那么他会自动和ObjC兼容,这样ObjC就可以按照上面的对应关系调用Swift的方法、属性等。但是如果Swift中的类没有继承于NSObject呢?此时就需要使用一个关键字“@objc”进行标注,ObjC就可以像使用正常的ObjC编码一样调用Swift了(事实上继承于NSObject的类之所以在ObjC中能够直接调用也是因为编译器会自动给类和非private成员添加上@objc,类似的@IBoutlet、@IBAction、@NSManaged修饰的方法属性Swift编译器也会自动添加@objc标记)。

    Swift调用ObjC 

    当前ObjC已经积累了大量的第三方库,相信在Swift发展的前期调用已经存在的ObjC是比较常见的。在Swift和ObjC的兼容性允许你在一个项目中使用两种语言混合编程(称为“mix and match”),而不管这个项目原本是基于Swift的还是ObjC的。无论是Swift中调用ObjC还是ObjC中调用Swift都是通过头文件暴漏对应接口的,下图说明了这种交互方式:

    SwiftInteractObjC

    不难发现,要在Swift中调用ObjC必须借助于一个桥接头文件,在这个头文件中将ObjC接口暴漏给Swift。例如你可以创建一个“xx.h”头文件,然后使用“#import”导入需要在Swift中使用的ObjC类,同时在Build Settings的“Objective-C Bridging Header”中配置桥接文件“xx.h”。但是好在这个过程Xcode可以帮助你完成,你只需要在Swift项目中添加ObjC文件,Xcode就会询问你是否创建桥接文件,你只需要点击“Yes”就可以帮你完成上面的操作:

    CreateObjCBridgingHeaderTip

    为了演示Swift中调用ObjC的简洁性, 下面创建一个基于Swift的Single View Application类型的项目,现在有一个基于ObjC的“KCLoadingView”类,它可以在网络忙时显示一个加载动画。整个类的实现很简单,就是通过一个基础动画实现一个图片的旋转。

    KCLoadingView.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #import <UIKit/UIKit.h>
     
    /**
     *  加载视图,显示加载效果
     */
    @interface KCLoadingView UIImageView
     
    /**
     *  启动,开始旋转
     */
    - (void)start;
     
    /**
     *  停止
     */
    - (void)stop;
     
    @end

    KCLoadingView.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    #import "KCLoadingView.h"
     
    static NSString *const kAnimationKey = @"rotationAnimation";
    @interface KCLoadingView ()
    @property(strong, nonatomic) CABasicAnimation *rotationAnimation;
    @end
     
    @implementation KCLoadingView
    #pragma mark - 生命周期及其基类方法
    - (instancetype)initWithFrame:(CGRect)frame {
        if (self = [super initWithFrame:frame]) {
            [self setup];
        }
        return self;
    }
     
    #pragma mark - 公共方法
    - (void)start {
        [self.layer addAnimation:self.rotationAnimation forKey:kAnimationKey];
    }
     
    - (void)stop {
        [self.layer removeAnimationForKey:kAnimationKey];
    }
     
    #pragma mark - 私有方法
    - (void)setup {
        self.image = [UIImage imageNamed:@"loading"];
     
        CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
        rotationAnimation.toValue = [NSNumber numberWithFloat:M_PI * 2.0];
        rotationAnimation.duration = 0.7;
        rotationAnimation.cumulative = YES;
        rotationAnimation.repeatCount = HUGE_VALF;
        self.rotationAnimation = rotationAnimation;
        [self.layer addAnimation:rotationAnimation forKey:kAnimationKey];
    }
     
    @end

    当将这个类加入到项目时就会提示你是否创建一个桥接文件,在这个文件中导入上面的“KCLoadingView”类。现在这个文件只有一行代码

    ObjCBridge-Bridging-Header.h

    1
    #import "KCLoadingView.h" 

    接下来就可以调用这个类完成一个加载动画,调用关系完全顺其自然,开发者根本感觉不到这是在调用一个ObjC类。

    ViewController.swfit

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    import UIKit
     
    class ViewControllerUIViewController {
        lazy var loadingView:KCLoadingView = {
            var size=UIScreen.mainScreen().bounds.size
            var lv KCLoadingView()
            lv.frame.size=CGSizeMake(37.037.0)
            lv.center=CGPointMake(size.width*0.5size.height*0.5)
            return lv
        }()
         
        lazy private var converView:UIView = {
            var cv UIView(frameUIScreen.mainScreen().bounds)
            cv.backgroundColor UIColor(red0.0green0.0blue0.0alpha0.5)
            return cv
        }()
         
        override func loadView() {
            //设置背景
            var image UIImage(named"iOS9")
            var background UIImageView(frameUIScreen.mainScreen().bounds)
            background.userInteractionEnabled=true
            background.image=image
            self.view background
        }
         
        override func viewDidLoad() {
            super.viewDidLoad()
             
            //设置蒙层
            self.view.addSubview(self.converView)
             
            //添加加载控件
            self.view.addSubview(self.loadingView)
             
            loadingView.start()
        }
         
        override func touchesBegan(touchesSetwithEvent eventUIEvent) {
            loadingView.stop()
        }
    }

    运行效果

    SwiftUseObjC 

    ObjC调用Swift

    从前面的Swift和ObjC之间的交互图示可以看到ObjC调用Swift是通过Swift生成的一个头文件实现的,好在这个头文件是由编译器自动完成的,开发者不需要关注,只需要记得他的格式即可“项目名称-Swift.h”。如果在ObjC项目中使用了Swift,只要在ObjC的“.m”文件中导入这个头文件就可以直接调用Swift,注意这个生成的文件并不在项目中,它在项目构建的一个文件夹中(可以按住Command点击头文件查看)。同样通过前面的例子演示如何在ObjC中调用Swift,新建一个基于ObjC的项目(项目名称“UseSwiftInObjC”),并且这次加载动画控件使用Swift编写。

    LoadingView.swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    import UIKit
     
    public class LoadingView:UIImageView {
        let basicAnimationKey "rotationAnimation"
        lazy var rotationAnimation:CABasicAnimation = {
            var animation CABasicAnimation(keyPath"transform.rotation.z")
            animation.toValue 2*M_PI
            animation.duration 0.7
            animation.cumulative true
            animation.repeatCount = .infinity
            return animation
        }()
         
        convenience init(){
            self.init(frameCGRectZero)
        }
         
        override init(frameCGRect) {
            super.init(frameframe)
            self.image UIImage(named"loading")
        }
     
        required public init(coder aDecoderNSCoder) {
            super.init(coderaDecoder)
            self.image UIImage(named"loading")
        }
         
        public func start() {
            self.layer.addAnimation(self.rotationAnimationforKeybasicAnimationKey)
        }
         
        public func stop() {
            self.layer.removeAnimationForKey(basicAnimationKey)
        }
    }

     然后可以直接在ObjC代码中导入自动生成的文件“UseSwiftInObjC-Swift.h”并调用。

    ViewController.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    #import "ViewController.h"
    #import "UseSwiftInObjC-Swift.h"
     
    @interface ViewController ()
    @property (strong,nonatomic) UIView *converView;
    @property (strong,nonatomic) LoadingView *loadingView;
    @end
     
    @implementation ViewController
    -(void)loadView{
        UIImage *image = [UIImage imageNamed:@"iOS9"];
        UIImageView *background = [[UIImageView alloc]initWithImage:image];
        background.userInteractionEnabled = YES;
        self.view = background;
    }
     
    - (void)viewDidLoad {
        [super viewDidLoad];
         
        [self.view addSubview:self.converView];
        [self.view addSubview:self.loadingView];
        [self.loadingView start];
    }
     
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
        [self.loadingView stop];
    }
     
    #pragma mark - 属性
    /**
     *  遮罩层
     */
    -(UIView *)converView{
        if (!_converView) {
            _converView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
            _converView.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5];
        }
        return _converView;
    }
    /**
     *  加载指示器
     */
    -(LoadingView *)loadingView{
        if(!_loadingView){
            CGSize screenSize = [UIScreen mainScreen].bounds.size;
            CGFloat loadingViewWidth = 37.0;
            _loadingView=[[LoadingView alloc]init];
            _loadingView.frame=CGRectMake((screenSize.width-loadingViewWidth)*0.5, (screenSize.height - loadingViewWidth)*0.5, loadingViewWidth, loadingViewWidth);
        }
        return _loadingView;
    }
     
    @end

    虽然生成的头文件并不会直接放到项目中,但是可以直接按着Command键查看生成的文件内容,当然这个文件比较长,里面使用了很多宏定义判断,这里只关心最主要部分。

    UseSwiftInObjC-Swift.h

    1
    2
    3
    4
    5
    6
    SWIFT_CLASS("_TtC14UseSwiftInObjC11LoadingView")
    @interface LoadingView : UIImageView
    - (SWIFT_NULLABILITY(nonnull) instancetype)initWithCoder:(NSCoder * __nonnull)aDecoder OBJC_DESIGNATED_INITIALIZER;
    - (void)start;
    - (void)stop;
    @end

    可以清晰的看到Swift确实进行了桥接,通过头文件将接口暴漏给了ObjC。但是注意前面说过的访问控制,如果类和方法在Swift中不声明为public,那么在ViewController.m中是无法调用的。事实上,如果方法不是public在UseSwiftInObjC-Swift.h中根本不会生成对应的方法声明。

    扩展—Swift调用C

    由于ObjC是C的超集,使得在ObjC可以无缝访问C语言。但是Swift的产生就是ObjC without C,因此在Swift中不可能像在ObjC中混编入C一样简单。但是考虑到C语言的强大以及历时那么多年留下了丰富的类库,有时候又不得不使用它,Swift中还是保留了与一定数量的C语言类型和特性的兼容。前面介绍过关于如何在Swift中使用ObjC的知识,事实上在Swift中使用C也是类似的(因为ObjC是C的超集,ObjC既然可以桥接,C自然也可以),你需要一个桥接文件,不同的是ObjC中的很多内容在桥接到Swift时都是类似,很容易上手。例如ObjC中使用的NSObject,在Swift中仍然对应NSObject,很多时候开发人员感觉不到这种转化,只是编程语言发生了变化。但是C导入Swift就需要必须要了解具体的对应关系:

    C类型 Swift类型 说明
    基本类型    
    char,signed char CChar 类似的unsigned char对应CUnsignedChar
    int CInt 类似的unsigned int对应CUnsignedInt
    short CShort 类似的unsigned short对应CUnsignedShort
    long CLong 类似的unsigned long对应CUnsignedLong
    long long CLongLong 类似的unsigned long long 对应 CUnsignedLongLong
    float CFloat  
    double CDouble  
    构造体类型   注意:结构体实现
    枚举typedef NS_ENUM(NSInteger,A){AB,AC} enum A:Int{case B,C} 去掉对应的前缀 ,注意C中的NS_Options会对应成Swift中实现OptionSetType的结构体
    结构体 对应Swift中的结构体   
    联合   Swift中不能完全支持联合,建议使用枚举关联值代替
    指针类型   C语言中的指针类型映射成了Swift中的泛型
    Type * UnsafeMutablePointer<Type> 作为返回类型、变量、参数类型时
    const Type * UnsafePointer<Type> 作为返回类型、变量、参数类型时
    Type *const * UnsafePointer<Type> 对于类类型
    Type *__strong * UnsafeMutablePointer<Type> 对于类类型
    Type * * AutoreleasingUnsafePointer<Type> 对于类类型
    函数指针 闭包  

     对于其他类型的映射关系都很容易理解,这里主要说一下指针的内容。通过上表可以看到在C中定义的一些指针类型当在Swift中使用时会有对应的类型,但是如果一个参数为某种指针类型,实际调用时应该使用何种Swift数据类型的数据作为参数调用呢?例如参数为UnsafePointer<Type>,是否只能传入UnsafePointer<Type>呢,其实也可以传入nil,并且最终调用时将会转化为null指针来调用。下表列出了这种参数调用对应关系:

    可用类型 最终转化类型
     UnsafePointer<Type>   注意:如果Type为Void则可以代表任何类型
     nil  null
     UnsafePointer<Type>、UnsafeMutablePointer<Type>、AutoreleasingUnsafeMutablePointer<Type>  UnsafePointer<Type>
     String  如果Type为Int、或者Int8将最终转化为UTF8字符串
     &typeValue  元素地址
     Type类型的数组([typeValue1,typeValue2])  数组首地址
     UnsafeMutablePointer<Type>  注意:如果Type为Void则可以代表任何类型
     nil null 
     UnsafeMutablePointer<Type>  UnsafeMutablePointer<Type> 
     &typeValue  元素地址
     Type类型的数组的地址(&[typeValue1,typeValue2])  数组地址
     AutoreleasingUnsafeMutablePointer<Type>  
     nil null 
     AutoreleasingUnsafeMutablePointer<Type>  AutoreleasingUnsafeMutablePointer<Type>
     &typeValue  元素地址

    下面不妨看一下如何在Swift中使用C语言,假设现在有一个用于字符串拼接的C库函数“stringAppend(char*,const char *)”,将其对应的文件导入到一个Swift项目中按照提示添加桥接头文件并在桥接头文件中引入对应的C文件。

    string.h

    1
    2
    3
    4
    5
    6
    7
    8
    #ifndef __UseCInSwift__Common__
    #define __UseCInSwift__Common__void stringAppend(char *source, char *toAppend)
     
    #include
     
    void stringAppend(char *source,const char *toAppend);
     
    #endif

    string.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include "string.h"
     
    void stringAppend(char *source,const char *toAppend) {
        unsigned long sourceLen = strlen(source);
        char *pSource = source + sourceLen;
        const char *pAppend = toAppend;
        while (*pAppend != '\0') {
            *pSource++ = *pAppend++;
        }
    }

    UseCInSwift-Bridging-Header.h

    1
    #import "string.h"

    然后在Swift中调用上面的C函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import Foundation
     
    var sourceStr:String "Hello"
    var appendStr:String ",World!"
     
    var sourceCStr = (sourceStr as NSString).UTF8String
    var sourceMutablePointer:UnsafeMutablePointer UnsafeMutablePointer(sourceCStr)
     
    stringAppend(sourceMutablePointer,appendStr)
     
    println(String.fromCString(sourceMutablePointer)!) //结果:Hello,World!

     可以看到“char *”参数转化成了Swift中的UnsafeMutablePointer<Int8>,而将”const char *”转化成了UnsafePointer<Int8>。根据上面表格中的调用关系,如果参数为UnsafeMutablePointer<Type>可以传入nil、UnsafeMutablePointer<Type>或者元素地址,很明显这里需要使用UnsafeMutablePointer<Int8>;而如果参数为UnsafePointer<Type>并且Type为Int8或者Int则可以直接传入String类型的参数,因此也就有了上面的调用关系。

    当然,上面这种方式适合所有在Swift中引入C语言的情况,但是为了方便调用,在Swift中默认已经module了常用的C语言类库Darwin,这个类库就作为了标准的Swift类库不需要再进行桥接,可以直接导入模块(例如import Darwin,但是事实上Foundation模块已经默认导入了Darwin,而UIKit又导入了Foundation模块,因此通常不需要手动导入Darwin)。那么对于没有模块化的C语言类库(包括第三方类库和自己定义的C语言文件等)能不能不使用桥接文件呢?答案就是使用隐藏符号“@asmname”,通过@asmname可以将C语言的函数不经过桥接文件直接映射为Swift函数。例如可以移除上面的桥接头文件,修改main.swift函数,通过@asmname加stringAppend映射成为Swift函数(注意重新映射的Swift函数名称不一定和C语言函数相同):

     main.swift

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import Foundation
     
    //通过asmname将C函数stringAppend()映射到Swift函数,事实上这里的Swift函数名可以任意命名
    @asmname("stringAppend"func stringAppend(var sourceStr:UnsafeMutablePointer,var apendStr:UnsafePointer ) -> Void
     
    var sourceStr:String "Hello"
    var appendStr:String ",World!"
     
    var sourceCStr = (sourceStr as NSString).UTF8String
    var sourceMutablePointer:UnsafeMutablePointer UnsafeMutablePointer(sourceCStr)
     
    stringAppend(sourceMutablePointer,appendStr)
     
    println(String.fromCString(sourceMutablePointer)!) //结果:Hello,World!

    更多Swift标准类库信息可以查看:https://github.com/andelf/Defines-Swift 

    反射

    熟悉C#、Java的朋友不难理解反射的概念,所谓反射就是可以动态获取类型、成员信息,在运行时可以调用方法、属性等行为的特性。 在使用ObjC开发时很少强调其反射概念,因为ObjC的Runtime要比其他语言中的反射强大的多。在ObjC中可以很简单的实现字符串和类型的转换(NSClassFromString()),实现动态方法调用(performSelector: withObject:),动态赋值(KVC)等等,这些功能大家已经习以为常,但是在其他语言中要实现这些功能却要跨过较高的门槛,而且有些根本就是无法实现的。不过在Swift中并不提倡使用Runtime,而是像其他语言一样使用反射(Reflect),即使目前Swift中的反射还没有其他语言中的反射功能强大(Swift还在发展当中,相信后续版本会加入更加强大的反射功能)。

    在Swift中反射信息通过MirrorType协议来描述,而Swift中所有的类型都能通过reflect函数取得MirrorType信息。先看一下MirrorType协议的定义(为了方便大家理解,添加了相关注释说明):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    protocol MirrorType {
         
        /// 被反射的成员,类似于一个实例做了as Any操作
        var valueAny get }
         
        /// 被反射成员的类型
        var valueTypeAny.Type get }
         
        /// 被反射成员的唯一标识
        var objectIdentifierObjectIdentifier? { get }
         
        /// 被反射成员的子成员数(例如结构体的成员个数,数组的元素个数等)
        var countInt get }
         
        //  取得被反射成员的字成员,返回值对应字成员的名称和值信息
        subscript (iInt) -> (StringMirrorType) { get }
         
        /// 对于反射成员的描述
        var summaryString get }
         
        /// 显示在Playground中的“值”信息
        var quickLookObjectQuickLookObject? { get }
         
        /// 被反射成员的类型的种类(例如:基本类型、结构体、枚举、类等)
        var dispositionMirrorDisposition get }
    }

    获取到一个变量(或常量)的MirrorType之后就可以访问其类型、值、类型种类等元数据信息。在下面的示例中将编写一个函数简单实现一个类似于ObjC中“valueForKey:”的函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    import UIKit
     
    struct Person {
        var name:String
        var age:Int 0
         
        func showMessage(){
            print("name=\(name),age=\(age)")
        }
    }
     
     
    //定义一个方法获取实例信息
    func valueForKey(key:String,obj:Any) -> Any?{
        //获取元数据信息
        var objInfo:MirrorType reflect(obj)
        //遍历子成员
        for index in 0..<objInfo.count {
            //如果子成员名称等于key则获取对应值
            let (name,mirror) = objInfo[index]
            if name == key {
                return mirror.value
            }
        }
        return nil;
    }
     
    var p Person(name"Kenshin"age29)
    //先查看一下对象描述信息,然后对照结果是否正确
    dump(p)