当前位置:主页 > 资料 >

Objective-C实现链式编程语法
栏目分类:资料   发布日期:2018-08-03   浏览次数:

导读:本文为去找网小编(www.7zhao.net)为您推荐的Objective-C实现链式编程语法,希望对您有所帮助,谢谢! 前言 熟悉Objective-C这一门编程语言的人都知道,Objective-C中方法的调用都是通过中括号

本文为去找网小编(www.7zhao.net)为您推荐的Objective-C实现链式编程语法,希望对您有所帮助,谢谢!

本文来自去找www.7zhao.net



前言

本文来自去找www.7zhao.net

熟悉Objective-C这一门编程语言的人都知道,Objective-C中方法的调用都是通过中括号[]实现的。比如[self.view addSubview:xxxView];如果想要在一个对象上连续调用多个方法,就要使用多组中括号嵌套(当然要保证每个方法都能把该对象作为返回值return)。比如[[[UILabel alloc] init] setText:@"xxx"];。这对于有其他编程语言经验的开发者而言,Objective-C无异于就是众多语言中的一朵奇葩。因为其他多数的高级语言方法调用都是以点语法.的形式实现的。好在Objective-C在iOS4.0之后推出了block这个语法(相当于其他语言中的匿名函数)。我们可以利用block的来实现Objective-C方法的链式调用。像这种用于特定领域的表达方式,我们叫做 DSL (Domain Specific Language),本文就介绍一下如何让Objective-C实现链式调用,其最终调用方式如下: 去找(www.7zhao.net欢迎您

DSLObject *obj = DSLObject.new.name(@"ws").age(27).address(@"beijing");
 

去找(www.7zhao.net欢迎您

很明显,相比较传统的Objective-C的方法调用方式,使用点语法进行方法调用更加简洁连贯、一气呵成。

本文来自去找www.7zhao.net

不难看出,这种点语法连续调用的方式,需要保证每次调用都能返回对象本身,这样链式调用才得以继续,并且在必要的时候还可以传入参数,比如上例中的“ws”、“27”、“beijing”。

www.7zhao.net

而至于为什么使用block来实现DSL链式调用语法?正是因为block完全符合构造链式调用的要求:既可以接收参数,又可以有返回值。

去找(www.7zhao.net欢迎您

不喜欢读文章的可以 。 内容来自www.7zhao.net

链式调用的实现

现在要给系统原生的类增扩展链式调用语法。比如给UIView的frame、backgroundColor增加链式调用,目前能想到的有以下两种实现方式。

欢迎访问www.7zhao.net

1. 第一种方式是使用category给UIView类扩展一些方法,每个方法的返回值都是一个block,block的参数是要给UIView对象的属性设置的值(比如frame),block的返回值是一个UIView对象。block接收到传入的参数后,会对view对象的响应属性进行赋值,然后把view对象作为返回值返回。开发者想使用链式调用,必须要调用category中的方法。

本文来自去找www.7zhao.net

2. **第二种方式是为我们要支持链式调用的系统类(比如UIView类)增加一个中间类(比如叫做DSLViewMaker),DSLViewMaker对象内部持有一个UIView对象,然后DSLViewMaker会声明并实现一些和UIView同名的方法。和方式一一样,每个方法的返回值也是一个block,block的参数是要给UIView对象的属性设置的值,block的返回值是这个UIView对象**。然后在合适的时候把这个view对象返回给调用者。

www.7zhao.net

下面针对于两种实现方式分别说明。

内容来自www.7zhao.net

category方式实现 本文来自去找www.7zhao.net

/// category 头文件
@interface UIView (DSL)
- (UIView* (^)(CGRect))DSL_frame;
- (UIView* (^)(UIColor *))DSL_backgroundColor;
@end
 

去找(www.7zhao.net欢迎您

/// category 实现文件
#import "UIView+DSL.h"
#define weak_Self __weak typeof(self) weakSelf = self
#define strong_Self __strong typeof((weakSelf)) strongSelf = (weakSelf)

@implementation UIView (DSL)
- (UIView *(^)(CGRect))DSL_frame {
    weak_Self;
    return ^UIView* (CGRect frame) {
        strong_Self;
        strongSelf.frame = frame;
        return strongSelf;
    };
}

- (UIView *(^)(UIColor *))DSL_backgroundColor {
    weak_Self;
    return ^UIView* (UIColor *backgroundColor) {
        strong_Self;
        strongSelf.backgroundColor = backgroundColor;
        return strongSelf;
    };
}
@end
 

内容来自www.7zhao.net

/// 客户端调用
/// 客户端调用category指定的带有“DSL_”前缀的方法
UIView *view = UIView.new.DSL_frame(CGRectMake(0, 0, 100, 250)).DSL_backgroundColor([UIColor orangeColor]);
 欢迎访问www.7zhao.net 

那么问题来了,现在要给UIImageView的一些方法和属性增加DSL的链式调用语法。因为UIImageView继承自UIView,这就代表UIImageView还要拥有UIView的DSL_frame方法和DSL_backgroundColor方法。经过简单的实现,大致如下: 本文来自去找www.7zhao.net

/// UIImageView category的头文件
@interface UIImageView (DSL)

- (UIImageView* (^)(UIImage *))DSL_image;
- (UIImageView* (^)(UIImage *))DSL_HighlightedImage;
- (UIImageView* (^)(BOOL))DSL_UserInteractionEnabled;
- (UIImageView* (^)(BOOL))DSL_highlighted;
- (UIImageView* (^)(NSArray <UIImage *> *))DSL_AnimationImages;
- (UIImageView* (^)(NSArray <UIImage *> *))DSL_HighlightedAnimationImages;
- (UIImageView* (^)(NSTimeInterval))DSL_AnimationDuration;
- (UIImageView* (^)(NSInteger))SDL_AnimationRepeatCount;
- (UIImageView* (^)(UIColor *))DSL_TintColor;

@end
 www.7zhao.net 
#import "UIImageView+DSL.h"
#define weak_Self __weak typeof(self) weakSelf = self
#define strong_Self __strong typeof((weakSelf)) strongSelf = (weakSelf)

@implementation UIImageView (DSL)
- (UIImageView* (^)(UIImage *))DSL_image {
    weak_Self;
    return ^UIImageView *(UIImage *image) {
        strong_Self;
        strongSelf.image = image;
        return strongSelf;
    };
}
- (UIImageView* (^)(UIImage *))DSL_HighlightedImage {
    weak_Self;
    return ^UIImageView *(UIImage *highlightedImage) {
        strong_Self;
        strongSelf.highlightedImage = highlightedImage;
        return self;
    };
}
- (UIImageView* (^)(BOOL))DSL_UserInteractionEnabled {
    weak_Self;
    return ^UIImageView *(BOOL userInteractionEnabled) {
        strong_Self;
        strongSelf.userInteractionEnabled = userInteractionEnabled;
        return strongSelf;
    };
}

/// 此处省略...,请自行脑补...

@end
 

本文来自去找www.7zhao.net

/// 客户端调用
UIImageView *imageView = UIImageView.new
.DSL_frame(CGRectMake(100, 100, 100, 60))
.DSL_image([UIImage imageNamed:@"imgxxx"]);
 
内容来自www.7zhao.net

基于以上代码,然后进行编译,编译器会报以下错误: copyright www.7zhao.net

本文来自去找www.7zhao.net

DSL_image这个东西在UIView中找不到,为什么是UIView呢?明明我们创建的是一个UIImageView。原因很简单,因为我们的DSL_frame是在UIView的category中声明并实现的,更要命的是,UIView(DSL)中声明的DSL_frame这个方法返回的block的返回值是一个UIView对象,UIView对象当然没有DSL_image方法。当DSL_frame返回的block返回了一个UIView类型的对象后,这个imageView就会被当成UIView使用,后面所有对UIImageView的方法的调用都不会成功,UIView(DSL)声明的方法如下: copyright www.7zhao.net

 - (UIView* (^)(CGRect))DSL_frame;,
 copyright www.7zhao.net 

针对于这个问题,目前笔者只想到一种解决方法:把在UIView(DSL)中声明的方法拷贝一份到UIImageView(DSL).h中,并修改block的返回值类型为UIImageView。最终的UIImageView(DSL)头文件 如下:

copyright www.7zhao.net

@interface UIImageView (DSL)
#pragma mark - UIView
/// 这些是在UIView(DSL)中拷贝过来的方法,不同的是,需要修改block的返回值类型为UIImageView,而不是原来的UIView,如下所示:
- (UIImageView* (^)(CGRect))DSL_frame;
- (UIImageView* (^)(UIColor *))DSL_backgroundColor;

#pragma mark - UIImageView
- (UIImageView* (^)(UIImage *))DSL_image;
- (UIImageView* (^)(UIImage *))DSL_HighlightedImage;
- (UIImageView* (^)(BOOL))DSL_UserInteractionEnabled;
- (UIImageView* (^)(BOOL))DSL_highlighted;
- (UIImageView* (^)(NSArray <UIImage *> *))DSL_AnimationImages;
- (UIImageView* (^)(NSArray <UIImage *> *))DSL_HighlightedAnimationImages;
- (UIImageView* (^)(NSTimeInterval))DSL_AnimationDuration;
- (UIImageView* (^)(NSInteger))SDL_AnimationRepeatCount;
- (UIImageView* (^)(UIColor *))DSL_TintColor;
@end
 
copyright www.7zhao.net

而UIImageView(DSL).m实现文件中不需要再实现DSL_frame和DSL_backgroundColor这两个方法,因为已经在UIView(DSL).m中实现过。只需要消除对应的警告即可。 去找(www.7zhao.net欢迎您

综上,通过category的方式实现链式调用好处在于每次调用都会返回对象本身,缺点在于category中的方法不能和系统的方法重名,因此笔者在这里使用了一个前缀DSL_来进行区分。而中间类方式实现链式调用就可以避免前缀的问题。 内容来自www.7zhao.net

中间类方式实现

上面已经说过,使用category的方式给类扩展链式调用的方法,我们必须要和原生的方法进行区分(比如增加前缀)。这样的缺点在于开发者开发者链式调用的时候还必须要时刻谨记调用指定前缀的方法,使用起来不是很友好。 内容来自www.7zhao.net

所以,还有另一种方法,我们可以使用一个中间类,中间类持有一个UIView对象,给这个中间类增加和UIView同名的方法,通过调用这个中间类的方法来间接调用UIView对象的方法。具体实现如下: 去找(www.7zhao.net欢迎您

/// DSLViewMaker.h文件

@interface DSLViewMaker : NSObject
DSLViewMaker *alloc_view(void);

/// 一些和UIView同名的方法
- (DSLViewMaker *(^)(CGRect))frame;
- (DSLViewMaker *(^)(UIColor *))backgroundColor;
/// 返回DSLViewMaker配置的对象
- (id)view;

@end
 

内容来自www.7zhao.net

/// DSLViewMaker.m文件

#import "DSLViewMaker.h"
#define weak_Self __weak typeof(self) weakSelf = self
#define strong_Self __strong typeof((weakSelf)) strongSelf = (weakSelf)

@interface DSLViewMaker()
@property(nonatomic, strong) UIView *view;
@end

DSLViewMaker *alloc_view(void) {
    return DSLViewMaker.new;
}

@implementation DSLViewMaker
- (instancetype)init {
    if (self = [super init]) {
        _view = [UIView new];
    }
    return self;
}

- (DSLViewMaker *(^)(CGRect))frame {
    weak_Self;
    return ^DSLViewMaker *(CGRect frame) {
        strong_Self;
        strongSelf.view.frame = frame;
        return strongSelf;
    };
}

- (DSLViewMaker *(^)(UIColor *))backgroundColor {
    weak_Self;
    return ^DSLViewMaker *(UIColor *backgroundColor) {
        strong_Self;
        strongSelf.view.backgroundColor = backgroundColor;
        return strongSelf;
    };
}

- (id)view {
    return _view;
}
@end
 去找(www.7zhao.net欢迎您 
/// 客户端调用
    UIView *view = alloc_view().frame(CGRectMake(0, 20, 100, 100)).backgroundColor([UIColor redColor]).view;
    [self.view addSubview:view];
 www.7zhao.net 

看完上面的代码,你可能会有几个疑惑:

www.7zhao.net

  1. 为什么客户端进行链式调用是以一个函数开头的? 内容来自www.7zhao.net

  2. 为什么最后要使用一个.view来返回我们创建的view? 去找(www.7zhao.net欢迎您

针对于第一个问题,我们是以一个中间类DSLViewMaker来创建了一个view,然后链式调用DSLViewMaker的对象方法对这个view进行配置。为了不让外部调用的客户端感知到DSLViewMaker的存在,所有使用了一个函数直接返回一个DSLViewMaker对象。

copyright www.7zhao.net

针对于第二个问题,还是因为中间类,因为链式调用要保证每次都要返回链式调用的对象(这里是指的maker对象),而客户端无法拿到maker配置好的view,为了让客户端能够获取链式调用配置好的view对象,所以暴露了一个view方法供外部调用。 内容来自www.7zhao.net

如果你觉得使用函数作为链式调用的开头不够面向对象。那么还可以给UIView增加一个如下分类: 内容来自www.7zhao.net

/// category头文件
#import

@class DSLViewMaker;

@interface UIView (DSLMaker)
欢迎访问www.7zhao.net

+ (DSLViewMaker *)make;
@end

www.7zhao.net

/// category实现文件
#import "UIView+DSLMaker.h"
#import "DSLViewMaker.h"

@implementation UIView (DSLMaker)
+ (DSLViewMaker *)make {
    return [DSLViewMaker new];
}

@end
 

欢迎访问www.7zhao.net

然后客户端的调用就变成了这样:

内容来自www.7zhao.net

//    UIView *view = alloc_view().frame(CGRectMake(0, 20, 100, 100)).backgroundColor([UIColor redColor]).view;
//    [self.view addSubview:view];

UIView *view = UIView.make.frame(CGRectMake(0, 20, 100, 100)).backgroundColor([UIColor redColor]).view;
[self.view addSubview:view];
 
去找(www.7zhao.net欢迎您

总结

综上,Objective-C语言实现链式语法可以有两种形式,但最终都是使用block实现的。使用category实现链式语法,需要加前缀。使用中间类来实现链式语法,需要有一个特定的方法返回被配置的对象。两种方式各有利弊。

copyright www.7zhao.net

最后附上 。

内容来自www.7zhao.net

PS:如非特别说明,所有文章均为原创作品,著作权归作者所有,转载请联系作者获得授权,并注明出处

本文来自去找www.7zhao.net

作者:VV木公子

本文来自去找www.7zhao.net

链接:https://www.jianshu.com/p/82012265e882

欢迎访问www.7zhao.net

欢迎访问www.7zhao.net


本文原文地址:http://www.cocoachina.com/ios/20180803/24446.html

以上为Objective-C实现链式编程语法文章的全部内容,若您也有好的文章,欢迎与我们分享! 本文来自去找www.7zhao.net

Copyright ©2008-2017去找网版权所有   皖ICP备12002049号-2 皖公网安备 34088102000435号   关于我们|联系我们| 免责声明|友情链接|网站地图|手机版