下载:
链接: https://pan.baidu.com/s/1_DLzF52rRaZMVPbFRzW5Jg 提取码: atht
响应式编程
概念:响应式编程本质:众所周知,响应式编程基于观察者模式,即发布-订阅模式。
观察者模式是一种思想,即发布-订阅的思想,从中可以衍生出很多模型,本人见过的有发布者-订阅者模型、事件-事件源-监听器模型、被观察者-发射器-订阅者模型,就这三种模型而言,肯定都是属于响应式编程思想。只要是观察者模式,都是响应式编程。
作为一个iOS 开发者,那么你一定用过Masnory/ SnapKit;每个优秀的框架都有一套自己的编程思想,Masnory这里使用的是经典的链式编程思想。我们不妨来学习一波。
我们平时是这样用Masnory给UI写约束布局的
UIView *view = [UIView new];
[self.view addSubview:view];
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.equalTo(self.view);
make.right.equalTo(self.view).offset(-20);
make.bottom.equalTo(self.view).offset(-200);
}];
上述代码思想是通过.符号把多行代码链接成一句代码,增强了可读性,例如a.b(2).c(3)一系列操作,那么在OC中要实现b(参数)这种调用方式,那么必然就是Block了。没错,链式编程的方法调用返回的是Block,而且调用完Block之后必然会有返回值(该返回值就是操作对象本身),这样才能链式调用,而且Block的参数就是需要内部对象操作的值。
翻看Masonry源码
封装自己链式代码
通过查看Masonry源码,我们可以借鉴其优秀的思想,封装一下自己的代码,完成响应式编程创建UI控件。
平时我们自己创建一个UIButton的代码
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn addTarget:self action:@selector(loginBtnClick:) forControlEvents:UIControlEventTouchUpInside];
[btn setTitle:@"登录" forState:UIControlStateNormal];
btn.titleLabel.font = [UIFont systemFontOfSize:17];
[btn setBackgroundImage:[UIImage imageNamed:@"btn_bg_video_normal"] forState:UIControlStateNormal];>[btn setBackgroundImage:[UIImage imageNamed:@"btn_bg_video_pressed"] forState:UIControlStateHighlighted];
[btn setBackgroundImage:[[UIImage imageNamed:@"btn_switch_av"] mg_stretchableImage] forState:UIControlStateDisabled];
btn.layer.cornerRadius = 20.0;
btn.clipsToBounds = YES;
[self.view addSubview:btn];
UIButton *registBtn = [UIButton >buttonWithType:UIButtonTypeCustom];
[registBtn setTitle:@"注册" forState:UIControlStateNormal];
[registBtn setTitleColor:[UIColor colorWithHexString:@"#498ad9"] forState:UIControlStateNormal];
[registBtn addTarget:self action:@selector(registBtnClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:registBtn];
再看一下封装后的代码,看起来会比较紧凑,逼格高了 一个档次
// 登录按钮 UIButton *loginBtn = [UIButton speedCreatButtonWith:^(UIButton *button) { button.mg_Config() .mg_CornerRadius(20.0) .mg_FontSize(17) .mg_NormalText(@"登录") .mg_NormalBackgroundImage([UIImage imageNamed:@"btn_bg_video_normal"]) .mg_SelectedBackgroundImage([UIImage imageNamed:@"btn_bg_video_pressed"]) .mg_DisabledBackgroundImage([[UIImage imageNamed:@"btn_switch_av"] mg_stretchableImage]) .mg_Selector(self, @selector(loginBtnAction), UIControlEventTouchUpInside) .mg_AddTo(self.view); }]; // 注册 UIButton *registBtn = [UIButton speedCreatButtonWith:^(UIButton *button) { button.mg_Config() .mg_NormalText(@"注册") .mg_NormalTextColor([UIColor colorWithHexString:@"#498ad9"]) .mg_Selector(self, @selector(registBtnAction:), UIControlEventTouchUpInside) .mg_AddTo(self.view); }];
有人担心这样的代码会造成循环引用?
- 首先产生循环引用的原因:
- block任何时候都会强引用在block代码块内部的对象,block消失,则强引用消失,block一直留存,强引用一直在,所以问题的关键是block是否会消失,如果A对象直接或者间接强引用一个block,block正好又强引用对象,那么就产生的循环引用。
- 然后参考系统UIView封装动画的Block:
- 首先循环引用发生的条件就是持有这个block的对象,被block里边加入的对象持有。当然是强引用。所以UIView的动画block不会造成循环引用的原因就是,这是个类方法,当前控制器不可能强引用一个类,所以循环无法形成。
UIView中的block持有当前控制器,但是当前控制器中是没有持有UIView类的,没有形成循环.当动画结束时,UIView会结束持有这个block,如果没有别的对象持有block的话,block对象就会被释放掉,从而block会释放掉对self的持有,整个内存引用关系被解除.
- 首先循环引用发生的条件就是持有这个block的对象,被block里边加入的对象持有。当然是强引用。所以UIView的动画block不会造成循环引用的原因就是,这是个类方法,当前控制器不可能强引用一个类,所以循环无法形成。
- 最后查看masonry源码:
- masonry中设置布局的方法中的block对象并没有被View所引用,而是直接在方法内部同步执行,执行完以后block将释放,其中捕捉的外部变量的引用计数也将还原到之前。
本文参考Masonry的Block,是不存在循环引用的,可以放心大胆的使用self,不必担心循环引用造成内存泄漏.
总结通用情况 : 在block本身不被self持有,而被别的对象持有,同时不产生循环引用的时候,就不需要使用weakself了。
封装一个UILabel
只展示部分代码
.h实现
#import <UIKit/UIKit.h>
#pragma mark - UILabel
@interface UILabel (SpeedCreat)
/// 初始化
+ (UILabel *)label;
/// 初始化设置
@property (nonatomic,copy,readonly) UILabel *(^mg_Config)(void);
/// 是否可用
@property (nonatomic,assign,readonly) UILabel *(^mg_Enabled)(BOOL value);
/// 是否允许交互
@property (nonatomic,assign,readonly) UILabel *(^mg_UserInteractionEnabled)(BOOL value);
/// 是否隐藏
@property (nonatomic,assign,readonly) UILabel *(^mg_Hidden)(BOOL value);
/// frame
@property (nonatomic,assign,readonly) UILabel *(^mg_Frame)(CGRect value);
/// 文本
@property (nonatomic,copy,readonly) UILabel *(^mg_Text)(NSString *value)
@end
.m实现
#import "UIView+SpeedCreat.h"
@implementation UILabel (SpeedCreat)
+ (UILabel *)label {
return [[UILabel alloc] init];
}
- (UILabel*(^)(void))mg_Config {
return ^ () {
self.text = @"label";
[self sizeToFit];
self.font = [UIFont systemFontOfSize:14];
self.textColor = [UIColor blackColor];
self.textAlignment = NSTextAlignmentLeft;
self.numberOfLines = 1;
self.backgroundColor = [UIColor clearColor];
self.lineBreakMode = NSLineBreakByWordWrapping;
self.shadowColor = [UIColor clearColor];
self.shadowOffset = CGSizeMake(0, 0);
return self;
};
}
- (UILabel * _Nonnull (^)(BOOL))mg_Enabled {
return ^(BOOL value) {
self.enabled = value;
return self;
};
}
- (UILabel * _Nonnull (^)(BOOL))mg_UserInteractionEnabled {
return ^(BOOL value) {
self.userInteractionEnabled = value;
return self;
};
}
- (UILabel * _Nonnull (^)(CGFloat))mg_Alpha {
return ^(CGFloat value) {
self.alpha = value;
return self;
};
}
- (UILabel * _Nonnull (^)(BOOL))mg_Hidden {
return ^(BOOL value) {
self.hidden = value;
return self;
};
}
/// frame
- (UILabel*(^)(CGRect value))mg_Frame {
return ^ (CGRect value) {
self.frame = value;
return self;
};
}
/// 文字
- (UILabel*(^)(NSString *value))mg_Text {
return ^ (NSString *value) {
self.text = value;
return self;
};
}
@end
怎么使用:
导入头文件 #import "UIView+SpeedCreat.h"就可以愉快的玩耍啦
UILabel *titleLabel = [UILabel speedCreatLabelWith:^(UILabel *lb) {
lb.mg_TextAlignment(NSTextAlignmentCenter)
.mg_Text(titleText)
.mg_Font([UIFont fontWithName:@"PingFangSC-Regular" size:22])
.mg_TextColor(HEX_ARGB(@"000000"))
.mg_AddTo(self.contentView);
}];
提取码: atht
github
项目 | 简介 |
---|---|
MGDS_Swif | 逗视视频直播 |
MGMiaoBo | 喵播视频直播 |
MGDYZB | 斗鱼视频直播 |
MGDemo | n多小功能合集 |
MGBaisi | 高度仿写百思 |
MGSinaWeibo | 高度仿写Sina |
MGLoveFreshBeen | 一款电商App |
MGWeChat | 小部分实现微信功能 |
MGTrasitionPractice | 自定义转场练习 |
DBFMDemo | 豆瓣电台 |
MGPlayer | 一个播放视频的Demo |
MGCollectionView | 环形图片排布以及花瓣形排布 |
MGPuBuLiuDemo | 瀑布流–商品展 |
MGSlideViewDemo | 一个简单点的侧滑效果,仿QQ侧滑 |
MyResume | 一个展示自己个人简历的Demo |
GoodBookDemo | 好书 |
-
轻轻点击,关注我