Objc中的动态编程

静态类型和动态类型

静态类型语言指的是在编译时检测类型的语言,也就是说,编程人员需要在代码中明确指出数据的类型,如C++,Java等。

动态类型语言和静态类型相对,指的是在运行时检测类型的语言,如Python等。

Objc属于混合型语言,C语言的部分是静态编程的,objc部分是动态编程的。只要发送的消息本身能够被对象识别,Objc的runtime不care对象的类型。

objc使用关键字id来表示在编译阶段未指定的对象的类型。那么动态类型的优势在哪里呢?通过官方例子,可以管中窥豹:

NSArray *anArray = [NSArray arrayWithObjects:@"A string", [NSDecimalNumber zero], [NSDate date], nil];
NSInteger index;
for (index = 0; index < 3; index++) {
    id anObject = [anArray objectAtIndex:index];
    NSLog(@"Object at index %d is %@", index, [anObject description]);
}

从代码中可以看到,把array中的对象赋予动态类型,我们可以通过动态绑定调用各个对象中实现的方法。

objc的动态绑定:每个对象都有一个叫做isa的实例变量来制定对象所属的类。objc的runtime使用这个指针来决定对象的类。

类的动态类型

在objc中,类本身也被表示为对象,也就是说我们可以直接查询类的属性(introspection/自省方法),或者动态改变行为(反射)。这些强大的动态类型可以使得我们在不了解对象类型的情况下调用方法和属性

// main.m
#import <Foundation/Foundation.h>
#import "Car.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Car *delorean = [[Car alloc] initWithModel:@"DeLorean"];
        
        // Get the class of an object
        NSLog(@"%@ is an instance of the %@ class",
              [delorean model], [delorean class]);
        
        // Check an object against a class and all subclasses
        if ([delorean isKindOfClass:[NSObject class]]) {
            NSLog(@"%@ is an instance of NSObject or one "
                  "of its subclasses",
                  [delorean model]);
        } else {
            NSLog(@"%@ is not an instance of NSObject or "
                  "one of its subclasses",
                  [delorean model]);
        }
        
        // Check an object against a class, but not its subclasses
        if ([delorean isMemberOfClass:[NSObject class]]) {
            NSLog(@"%@ is a instance of NSObject",
                  [delorean model]);
        } else {
            NSLog(@"%@ is not an instance of NSObject",
                  [delorean model]);
        }
        
        // Convert between strings and classes
        if (NSClassFromString(@"Car") == [Car class]) {
            NSLog(@"I can convert between strings and classes!");
        }
    }
    return 0;
}

方法的动态绑定

performSelector:withObject

Selector是objc对方法名称的内部表示,它使得方法和方法所属的对象分离开来。Ojbc中使用SEL关键字来表示这种特殊的数据类型。

// main.m
#import <Foundation/Foundation.h>
#import "Car.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Car *porsche = [[Car alloc] init];
        porsche.model = @"Porsche 911 Carrera";
        
        SEL stepOne = NSSelectorFromString(@"startEngine");
        SEL stepTwo = @selector(driveForDistance:);
        SEL stepThree = @selector(turnByAngle:quickly:);
        
        // This is the same as:
        // [porsche startEngine];
        [porsche performSelector:stepOne];

        // This is the same as:
        // [porsche driveForDistance:[NSNumber numberWithDouble:5.7]];
        [porsche performSelector:stepTwo
                      withObject:[NSNumber numberWithDouble:5.7]];
        
        if ([porsche respondsToSelector:stepThree]) {
            // This is the same as:
            // [porsche turnByAngle:[NSNumber numberWithDouble:90.0]
            //              quickly:[NSNumber numberWithBool:YES]];
            [porsche performSelector:stepThree
                          withObject:[NSNumber numberWithDouble:90.0]
                          withObject:[NSNumber numberWithBool:YES]];
        }
        NSLog(@"Step one: %@", NSStringFromSelector(stepOne));
    }
    return 0;
}

可以看到,比较遗憾的是,这种方式最多只能调用最多含有两个参数、且没有返回值的函数。更复杂的函数的动态绑定需要使用NSInvocation实现。

NSInvocation

使用NSInvocation可以方便的封装含有任意数量参数的的函数。这篇文章做了很好的介绍,而且文末还有对NSInvocation的封装,非常给力。

Protocols - 协议

Protocols指的是一组可以被任意类实现的属性和方法。和interface相比,protocols具有更大的弹性,因为两个实现该协议的类可以不具有继承关系。

使用Protocol实现动态绑定

// main.m
#import <Foundation/Foundation.h>
#import "Bicycle.h"
#import "Car.h"
#import "StreetLegal.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        id <StreetLegal> mysteryVehicle = [[Car alloc] init];
        [mysteryVehicle signalLeftTurn];
        
        mysteryVehicle = [[Bicycle alloc] init];
        [mysteryVehicle signalLeftTurn];
    }
    return 0;
}

无论Car和Bicycle是否具有一定的继承关系,他们都可以通过实现相同的protocol来进行动态绑定。

对象可以使用conformsToProtocol方法来检查是否实现了某项协议。这个方法接收一个协议对象(通过@protocol命令获取)作为参数。是不是想起了@seletor()命令?

if ([mysteryVehicle conformsToProtocol:@protocol(StreetLegal)]) {
    [mysteryVehicle signalStop];
    [mysteryVehicle signalLeftTurn];
    [mysteryVehicle signalRightTurn];
}

这个是很牛的动态类型工具,因为我们可以在对象中使用协议定义的非常好的API,而且不用在意这个对象到底是什么。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,775评论 0 9
  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,232评论 0 7
  • 还记得读小学的时候,老师会组织我们去春游,那时候的春游很简单,不需要坐车,不需要安排用餐,听老师交代一下纪律跟着老...
    予未名阅读 360评论 2 5
  • 2017年12月4日讯:“娱乐一体化”是创新,是文娱圈一个“新概念”地标性关键词。目前国内顶级娱乐公司都试图迈向“...
    素未謀面阿阅读 120评论 0 0
  • 我也不知道说什么,很久不写东西了。 最近活动变少了,什么时候才能变多? 我让同学帮忙,被拒绝了,但转眼又问我借东西...
    八月份的柚子阅读 185评论 0 0