OC中有一个使用非常广泛的框架——masonry。在masonry中,我们可以使用“点语法”实现方法调用。但是OC 的调用方法使用“[Class func]”。相比于OC中反人类的AutoLayout语法,masonry的链式语法使用的便利性,被广大iOS开发者所接受。
masonry语法:↓
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(10);
make.left.equalTo(10);
make.bottom.right.equalTo(-10);
}];
上面的示例代码中,链式编程思想被体现,并且极大的简化了自动布局代码。
今天我们由简到繁,逐步实现这样的链式语法设计。
首先创建一个Person类文件,该类文件在下面的示例中均被使用。
#import
@interface Person : NSObject
@end
我们最终想要实现的方法调用格式为:
self.p = [[Person alloc] init];
self.p.eat(2);
block定义为属性时,常备用于反向传值(此处不再讨论)。现在我们仅仅给出属性定义语法!
#import
@interface Person : NSObject
/* block类型属性:(ARC使用strong,非ARC使用copy) **/
@property (nonatomic, strong) void(^eat)();
@end
block作为代码参数使用,可以向方法传递一个可被执行的代码块。这也是很多框架中常用的一种设计方式。例如常用的AFN框架中,对网络请求结果的处理。相比于用协议实现回调功能,这样写可以使代码更加紧凑!
例如我们在Person中定义一个eat(吃)的方法:
#import
@interface Person : NSObject
/** block当参数(block代码块)*/
- (void)eat:(void (^)())block;
@end
此时作为Person的调用者,我们可以将 eat 的具体行为通过代码块的形式传递给该方法执行:
/** block当做方法的参数使用 */
- (void)demo2 {
self.p = [[Person alloc] init];
[self.p eat:^{
NSLog(@"我是怎样吃饭的");
}];
}
到此并没有结束,我们还需要在Person文件中将声明的该方法实现。并调用执行参数block。否则我们的block代码块没有被调用当然不会被执行:
#import "Person.h"
@implementation Person
// 实现声明方法并调用block
- (void)eat:(void (^)())block {
block();
}
@end
然后用实例调用该 eat 方法,我们会在控制台看到结果:
本文开头提到,使用“点语法”调用方法。既然block作为参数使用很广泛,那我们是不是可以用它作为返回值使用?答案是肯定的!
我们看masonry的方法:在bolck内点开 make.top.equalTo 方法,我们看它的返回值:
我们看到它的返回值是一个block类型,MASConstraint是返回值类型,我们暂且先不管(以 void 类型替换)。到此我们可以有一点想法了。
我们模仿masonry的该方法定义我们的自己Person类的 eat 方法:
#import
@interface Person : NSObject
/** block作为方法返回值使用 */
- (void(^)(int))eat;
@end
声明了该方法,当然还要实现它:
#import "Person.h"
@implementation Person
- (void (^)(int))eat {
return ^(int n){
NSLog(@"吃了 %d 个馒头!!!", n);
};
}
@end
到这我们貌似搞定了,接下来试试到底行不行:
- (void)demo3 {
// block当返回值
self.p = [[Person alloc] init];
self.p.eat(2);
}
运行,控制台打印出来了我们期待的结果:
写到这里,我们已经搞清楚了链式语法结构的实现原理。我们写了一个很简单的小栗子循序渐进,貌似很简单有没有。
但是我们知道masonry中我们可以用点语法连续调用,比如:make.bottom.right.equalTo(-10); 可以连续的用点语法调用。我们不妨设计一个简单的demo ,使其实现类似masonry的语法。具体如何实现,可以学习masonry的设计。(最终我们想要的结果是可以连续调用的语法结构 add(value).add(value).add(value) )
打开masonry的约束方法:
mas_makeConstraints 方法被设计在 UIView的分类中,分析我们要实现的demo的需求,应该将计算方法声明在NSObject的分类中!参数 MASConstraintMaker 为方法调用者,借此分析我们创建NSObject的分类以及计算管理者Manager,并声明计算方法:
#import
#import "Manager.h"
@interface NSObject (Tools)
/** 计算方法 */
+ (int)KY_makeCalculate:(void(^)(Manager *mgr))block;
@end
参数Manager就是我们的计算方法管理者,我们在该类中声明加法运算,并根据之前点语法的实现原理,以block作为返回值:
#import
@interface Manager : NSObject
- (void(^)(int))add;
@end
接下来我们貌似就可以调用了:
- (void)demo4 {
// 链式实现
int result = [NSObject KY_makeSum:^(Manager *mgr) {
mgr.add(5);
}];
NSLog(@"运算结果----> %d", result);
}
但是,对于连续的 add(value) 方法调用,貌似我们并没有实现!其实只要将返回值void类型改为管理者本身就可以了,另外我们再声明一个属性将计算结果临时保存一下:
#import
@interface Manager : NSObject
/** 计算结果 */
@property (nonatomic, assign) int result;
- (Manager *(^)(int))add;
@end
.m 文件中实现我们的 add 方法:
#import "Manager.h"
@implementation Manager
- (Manager *(^)(int))add {
return ^(int value) {
_result += value;
return self;
};
}
@end
接下来我们再调用一下:
// 链式实现
- (void)demo4 {
// 链式实现
int result = [NSObject KY_makeSum:^(Manager *mgr) {
mgr.add(1).add(2).add(3).add(4).add(5);
}];
NSLog(@"运算结果----> %d", result);
}
然后我们运行测试,看下结果:
真的可以了,哈哈哈哈哈????!!!!
Swift 本身就是链式的语法结构,所以写起来就相对简单的多了,在此不再过多的复数了。
响应式编程概念性的定义,很难懂!找到的定义是:响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
仅仅看定义的话真的很难理解,在网上找到一个感觉可以加深一下理解。例如,在命令式编程环境中,a=b+c表示将表达式的结果赋给a,而之后改变b或c的值不会影响a。但在响应式编程中,a的值会随着b或c的更新而更新。
响应式编程, 开发中我们常见和常用的 Target、Delegate、KVO、通知、时钟、网络异步回调等,都属于响应式编程。(有事件触发时响应相关事件)。这些我们使用过的方法或许可以帮助我们理解其概念。
OC中有一个响应式编程框架 ReactiveCocoa(RAC)
其它的资料暂时还未找到。。。~~~~(>_<)~~~~
我们常听到的应该是OOP的概念,即面向对象的编程。OOP的核心思想是使用封装和继承,将一系列相关的东西放到一起。举个小例子帮助理解一下,看一段小代码:
/// 动物类
class Animal {
print("吃东西")
}
func run() {
print(" 2 条腿跑")
}
}
/// 继承自动物类的人
class Dog: Animal {
print(" 4 条腿跑")
}
func bite() {
print("咬人")
}
}
let dog = Dog()
person.eat()
person.run()
我们定义了一个动物类Animal,然后抽象出它的leg(腿)属性,以及eat(吃)、run(跑)的方法。子类Dog继承Animal,并根据自己的特性重写了run(跑)的方法。而对于eat(吃)已经满足需求,不必重写。
Dog和Animal共享了一部分代码eat(吃),这部分代码被封装在父类中,所有继承自Animal的子类都可以使用。这也就是我们刚才提到的OOP的核心思想——使用封装和继承,将一系列相关的东西放到一起。
再看,Dog类中我们还写了一个方法bite(咬人),狗会咬人。然后我们还要写一个类Wolf(狼)继承Animal(动物),同样狼也会咬人。
/// 继承自动物类的人
class Wolf: Animal {
print("四条腿跑")
}
func bite() {
print("咬人")
}
}
但是我们不得不重新写bite方法,因为我们无法使用Dog(狗)的bite方法。这时就会看到代码的重复性。同样,我们要在Dog(狗)中添加bark(叫)方法,而狼也会叫,此时就需要在Wolf中再次添加bark(叫)方法。如此下来,代码会显得越来越臃肿,并且扩展性和维护性会大大降低。而OC和Swift代码中不能够多继承!
此时我们就用到了协议的另一个功能,定义统一的方法bark(叫),然后由Dog和Wolf分别遵守该协议:
protocol BarkProtocol {
func bark()
}
这样我们就在协议中统一规定了Dog和Wolf共有的Bark(叫)方法,但是这样貌似也需要在子类中分别实现该方法,但是,这样写的好处无疑会比之前的方法好很多。至少它可以在需要的是否对定义的方法进行统一的修改,规范性提高不少。同样我们还可以这样给协议接口一个默认实现,这样就不用在子类中去分别实现了!
import UIKit
protocol BarkProtocol {
func bark()
}
extension BarkProtocol {
func bark() {
print("狼和狗的叫声")
}
}
以上所述是一种面向协议的编程思想。通过面向协议的思想,我们的实现代码得到优化。
协议定义
提供实现的入口
遵循协议的类型需要对其进行实现
协议扩展
为入口提供默认实现
根据入口提供额外实现
之前写过的隔离地图的设计Demo也是一种面向协议的编程思想。
我们的小demo描述的都很简单,但在实际开发中,我们遇到的问题会比这复杂的多。这就需要对实际问题的具体分析,然后将这样的编程思想应用其中,提高我们的代码质量!开发中要培养良好的代码习惯,将这些我们看似简单的东西应用到项目开发中。不要一味地为了实现功能而写代码,更多的融入一些设计思想,会使我们的代码更舒适!