iOS利用block实现链式编程方法(Objective-C链式编程)
objc利用block实现链式编程方法
因为不好读。block和其他语言的匿名函数一样,很多程序员刚开始很难主动去用他。
本文描述block作为属性的实际使用,看懂block,并讲解如何利用block实现链式编程方法。
//【New】支持多对象链式编程
//1.如果使用多个对象的链式编程,需要取值的时候请调用ends()获取多个结果
//2.如果调用end()只会取得第一个对象的链条返回值
//3.使用endsAt()可以获取指定index对象的链条返回值
//4.如果链条结尾返回的是“值类型”,那么该值是第一个对象的链条返回值
//5.使用项目中的“方法”对多对象的链式编程不起作用
//写法一
UIView* viewA = UIViewNew.viewSetFrame(,,,);
UIView* viewB = UIViewNew.viewSetFrame(,,,);
linkObjs(viewA, viewB).viewAddToView(self.view).viewBGColor([UIColor lightGrayColor]);
//写法二
NSMutableArray* arrA = [NSMutableArray arrayWithObjects:@"A", nil];
NSMutableArray* arrB = [NSMutableArray arrayWithObjects:@"B", nil];
NSMutableArray* arrC = [NSMutableArray arrayWithObjects:@"C", nil];
NSArray* linkResults = @[arrA,arrB,arrC].makeLinkObjs.m_arrAddObj(@"E").ends();
//写法三
linkResults = arrA.linkAnd(arrB).linkAnd(arrC).m_arrAddObj(@"D").ends();
//写法四,简单粗暴的重复执行之后链条100次,这种情况不需要for循环了
linkResults = arrA.linkLoop().m_arrAddObj(@"F").ends();
【一】遭遇
到今天iOS开发中最常用的语言还是objc,市场就像泰坦尼克号,人虽然在上楼,但是船在下沉,所以人还是在下沉。虽然swift出现的迅猛,但是大部分开发者面对的还是objc。什么时候swift替代objc,今天的objc开发人员也不用太着急,说不定船都沉了(苹果保佑)。
objc最令人头疼的是他看起来像部落语言一样的表达。比如下面这行新手代码,但是就像求偶讯号一样晦涩:
[[NSMutableDictionary alloc] initWithContentsOfURL:[NSURL URLWithString:@"a.txt"]][@"persons"][];
但它并没有告诉别人任何有意义的事情,分解一下语法就像下面一样:
NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithContentsOfURL:[NSURL URLWithString:@"a.txt"]];
NSArray* arr = dict[@"persons"];
arr[];
语言发展到今天,代码的普世价值不是效率,而是开发效率。这就是为什么java能够成为王者的原因。因为java写起来快,好读。
【二】探索
objc比起java又慢又不好读。那么如何让objc看起来像java一样呢。纵观objc全部语法,只有block能够办到。我们来看block的定义
returnType (^name) (val1, val2, ...);
返回类型 变量名 参数列表 //我们可以从表面认为他是一根指向函数的指针,这个函数的样子如下
(returnType)name(val1, val2, ...)
{
//......
}
block具有一个函数的外观,又被当作一个变量。那么block就具备两个功能,第一:可以作为类的属性被'点'出来。第二:可以当作函数直接调用。下面逐个解释,第一个类的属性可以点出来,比如person.name;这很好理解,你一定见过,str.length;对吧。第二个呢,block作为一个变量,但是又可以把它当作指向函数的指针一样调用。
NSString*(^myBlock)() = ^(){ return @"菊乐"; };//定义一个返回值为NSString类型,无参的,并且名字叫做myBlock的block myBlock();//这一行就是调用 NSString result = myBlock();//这是取出block执行后拿到的返回值,也就是@"菊乐"
这里再解释一次block的定义:上面的myBlock可以认为是指向一个定义如下的函数的指针,这个函数是
(NSString*) myBlock()
{
//...
} //指向函数的指针
NSString* (*p) (); //可以认为指针p就是myBlock(其实block的真实身份更加复杂)
【三】推理
我们来幻想一下objc写起来是这样的
//设置视图位置和大小,设置视图背景色
view.setFrame(,,,).setBackgroundColor( @"#0c0c0c".toColor() );
//移除空格并在控制台打印字诗句
@" 白 日 依 山 尽 , 黄 河 入 海 流 ".removeStr(@" ").nslog();
而实际上要写5行的代码,一行3秒,3行15秒。就这样错过了一次摇一摇的机会,人生在缓慢的艰难,而我们却还一无所知。
view.frame = CGRectMake(, , , );
view.backgroundColor= [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0]; NSString* str= @" 白 日 依 山 尽 , 黄 河 入 海 流 ";
str= [str stringByReplacingCharactersInRange:@" " withString:@""];
NSLog(str);
现在我们来倒着分析如何使用block来实现链式编程,再来观察这句话:
view.setFrame(,,,).setBackgroundColor( @"#0c0c0c".toColor() );
其中的setFrame和setBackgroundColor他们是什么?大家一定知道是属性。可是属性是不带括号的,setFrame()他是属性吗?那他又是什么呢?setFrame()是属性,并且是block类型的,因为block可以加括号调用。就像这样调用block();那么还有一个问题很关键:view.setFrame().setBackgroundColor();怎么可以连续点出来,这刚开始很不好理解。这并不难,我们往前看那句话:
NSString result = myBlock();
细心的去发现,block是可以有返回值的,那么我们就依靠这个返回值就可以点出一堆属性,一直点下去,这样我们的链式编程的思路就通了。
【四】实践
我们现在就来实现view.setFrame().setBackgroundColor();
首先准备一个分类。头文件定义如下:
// UIView+Extension.h #import <UIKit/UIKit.h> @interface UIView(Extesion)
@property (nonatomic,copy) UIView* (^setFrame)(CGFloat x, CGFloat y, CGFloat w, CGFloat h);
@property (nonatomic,copy) UIView* (^setBackgroundColor)(UIColor* color);
@end
我们在UIView类上扩展了两个额外属性setFrame和setBackgroundColor,这意味着只要是继承自UIView的对象就可以点出来这两个属性。
现在我们需要理解一下两个的区别:
view.setFrame;//这是获取属性,它返回一个block
view.setFrame();//这是获取属性,它返回一个block,最后我们使用括号调用了这个block,这个block的返回值是UIView类型的对象,那么他就可以继续点出下一个属性了
view.setFrame()这句话的末尾因为加上了括号所以执行了block,而他的返回类型是一个UIView对象,所以可以调用setBackgroundColor();
这里解释一下为什么用copy,block这种类型本来是值类型的,他本来是在栈上的,在ARC环境下如果被任何强指针过了一次,编译器就会把他进行一次copy,放到堆内存中。block的retain行为默认是用copy的行为实现的,因为block变量默认是声明为栈变量的,为了能够在block的声明域外使用应该使用copy。
接下来实现.m文件内的代码:
- (UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame
{
return ?;
}
不难分析出,这个属性的类型是block,具体是UIView* (^)(CGFloat , CGFloat , CGFloat , CGFloat )类型。那么‘?’就是这样一个类型的对象。可是这个对象从哪里获得呢,如果是个NSString我还存储的有,而这个对象只能无从获取。这并不重要,因为我们并不要要block对象本身,我们需要的是:block对象他能够执行一些功能就够了。所以属性内部返回一个临时block对象,这个block内部呢去执行一些功能,最重要的是block内部一定要返回UIView类型的对象。编译器会对block的类型进行苛刻的检测。
- (UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame
{
return ^(CGFloat x, CGFloat y, CGFloat w, CGFloat h){//返回临时变量的block
self.frame = CGRectMake(x, y, w, h);//block执行一些功能
return self;//block执行完毕的返回值
};
}
下面是.m文件的实现
// UIView+Extension.m #import "UIView+Extension.h" @implementation UIView(Extesion)
- (UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame
{
return ^(CGFloat x, CGFloat y, CGFloat w, CGFloat h){
self.frame = CGRectMake(x, y, w, h);
return self;
};
}
- (void)setSetFrame:(UIView *(^)(CGFloat, CGFloat, CGFloat, CGFloat))setFrame{};//该属性不需要从外部设置,不想报警告就写空方法 - (UIView *(^)(UIColor *))setBackgroundColor
{
return ^(UIColor* color){
self.backgroundColor= color;
return self;
};
}
- (void)setSetBackgroundColor:(UIView *(^)(UIColor *))setBackgroundColor{};
@end
【五】崩溃
objc中向nil对象发送任何消息都不会崩溃,但是发送他不能处理的消息类型就会崩溃,这也是最经常遇到了情况。还有这样也会崩溃:nil.length; view.length;调用不存在的属性也会崩溃。那么这是我们要处理的一个重要的情况。
试想,一行代码中间突然有一个处理返回了nil给下一个环节,一调用就崩溃,关键是断点并不能明确告诉你在哪一行。我们只能从控制台中获得调试信息。这也是链式编程的弊端之一。
为了处理这种情况,我写了一个链式编程框架LinkBlock来统一处理,针对几乎所有常用功能进行了链式的封装,并没有什么门槛,希望大家帮我点一颗星星,支持天朝做良好的程序员。
【六】效率
关于效率,肯定比原生低那么一丁点的,几乎可以忽略的,只是多了一次属性调用,一次一临时block的创建,一次block的执行。博主的观点是为了提高开发效率,而降低一些运行效率。有时候也是值得的。最后苹果也是大力提倡我们使用block的。祝愉快。
iOS利用block实现链式编程方法(Objective-C链式编程)的更多相关文章
- iOS:用Block写一个链式编程
一.介绍 链式编程是一个比较新颖的编程方式,简单直观,用起来也比较舒服.目前比较有名的Mansory和BabyBlueTooth就是使用链式编程写的第三方框架. 二.写法 链式编程写法不同于传统方式, ...
- iOS 中Block以及Blocks的使用,闭包方法调用
OC: -(void)dataWithUrl:(NSString*)string AndId:(NSInteger)id returnName:(void(^)(NSString*name))back ...
- UIView封装动画--iOS利用系统提供方法来做转场动画
UIView封装动画--iOS利用系统提供方法来做转场动画 UIViewAnimationOptions option; if (isNext) { option=UIViewAnimationOpt ...
- UIView封装动画--iOS利用系统提供方法来做关键帧动画
iOS利用系统提供方法来做关键帧动画 ios7以后才有用. /*关键帧动画 options:UIViewKeyframeAnimationOptions类型 */ [UIView animateKey ...
- UIView封装动画--iOS 利用系统提供方法来做弹性运动
iOS 利用系统提供方法来做弹性运动 /*创建弹性动画 damping:阻尼,范围0-1,阻尼越接近于0,弹性效果越明显 velocity:弹性复位的速度 */ [UIView animateWith ...
- iOS中Block使用探索
Block介绍 Block在ios 4.0之后加入,并大量使用在新的ios api中.block是一个匿名的代码块,可以传递给其他对象的参数,并得到返回值.从本质上讲,block同其他普通的变量类似, ...
- 异步式I/O与实践式编程
阻塞 线程在执行中如果遇到磁盘读写或网络通信(统称为I/O操作)通常要消耗很长时间 这时操作系统会剥夺这个线程的CPU控制权,使其暂停执行,同时将资源让给其他工作线程 异步I/O 非阻塞IO 针对所有 ...
- paip.函数式编程方法概述以及总结
paip.函数式编程方法概述以及总结 1 函数式编程:函数式风格..很多命令式语言里支持函数式编程风格 1.1 起源 (图灵机,Lisp机器, 神经网络计算机) 1.2 函 ...
- Objective-C运行时编程 - 方法混写 Method Swizzling
摘要: 本文描述方法混写对实例.类.父类.不存在的方法等情况处理,属于Objective-C(oc)运行时(runtime)编程范围. 编程环境:Xcode 6.1.1, Yosemite,iOS 8 ...
随机推荐
- drupal7 profile2模块获取个人信息
一.问题背景: 用profile2模块,扩展个人信息,增加了“手机号”等信息,一些地方想要获取当前用户的手机号 二.解决办法: 用profile2自带的方法:profile2_load_by_user ...
- XAMPP添加二级域名
1.在hosts中添加域名 2.点击config选择httpd.conf,去掉Include前面的#号 3.取消NameVirtualHost前面的##,在页面底端添加二级域名 4.重启xampp
- android studio 3.0 以上 查看sharedpreference
android studio 3.0 以上 查看sharedpreference 点击android studio 右侧的device file explore,找到data / data 目录: 找 ...
- jpa No Dialect mapping for JDBC type:-9
1.自定义个一个继承SQLServerDialect的dialect类 public class MySQLServer2008Dialect extends SQLServer2008Dialect ...
- toLocaleTimeString()方法在IE和谷歌浏览器上 根据本地时间格式,把 Date 对象的时间部分(不含日期)转换为“时间字符串”存在区别
这两天修改一个bug,发现一个问题: toLocaleTimeString()方法在IE和谷歌浏览器上 根据本地时间格式,把 Date 对象的时间部分(不含日期)转换为“时间字符串”存在区别.方法原 ...
- MyBatis基本配置和实践(三)
一.输入映射和输出映射 mapper.xml映射文件中定义了操作数据库的sql,每条sql就是一个statement,映射文件是MyBatis的核心. 1.parameterType(输入类型) 简单 ...
- 上传通用化 VHD 并使用它在 Azure 中创建新 VM
本主题逐步讲解如何使用 PowerShell 将通用化 VM 的 VHD 上传到 Azure.从该 VHD 创建映像,然后从该映像创建新 VM. 可以上传从本地虚拟化工具或其他云导出的 VHD. 对新 ...
- Effective C++(9) 构造函数调用virtual函数会发生什么
问题聚焦: 不要在构造函数和析构函数中调用virtual函数,因为这样的调用不会带来你预想的结果. 让我先来看一下在构造函数里调用一个virtual函数会发生什么结果 Demo class Trans ...
- CSS学习---css基础知识0105
CSS, Cascading Style Sheets的简称,中文称为层叠样式表,用来控制网页数据的表现,可以使网页的表现与数据内容分离. 举例:颜色,大小,高度.宽度.内外边距.边框.浮动.定位.字 ...
- Asp.Net MVC 开发技巧(二)
Linq查询 Linq的使用大体分为两种:语句表达式 和 方法 首先,我们要在控制器中定义好context private ApplicationDbContext db = new Appli ...