@(第三方库源码学习)

[TOC]

Masonry框架的类结构

学习一、Masonry采用了经典的组合设计模式(Composite Pattern)。

1、定义

将对象组合成树状结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象(Leaf)和组合对象(Composite)的使用具有一致性。 注意:这个组合模式不是“组合优于继承”的那种组合,是狭义的指代一种特定的场景(树状结构)

2、理解这个模式要知道三个设定:
  • Component协议:树中的组件(Leaf、Composite)都需要实现这个协议
  • Leaf组件:树结构中的一个没有子元素的组件
  • Composite组件:容器,与Leaf不同的是有子元素,用来存储Leaf和其他Composite
3、什么时候使用?
  • 1、想获得对象抽象的树形表示(部分——整体层次结构)像文件夹、公司组织架构等,需要处理分支和节点的树状结构场景
  • 2、想让客户端统一处理组合结构中的所有对象
4、在Cocoa Touch框架中,UIView被组织成一个组合结构。
5、优点:对不同的对象以相同的方式处理
6、swift实现Demo
import Foundation// 一:Component协议:树中的组件(Leaf、Composite)都需要实现这个协议
protocol File {var name: String { get set }func showInfo()
}// 二:Leaf:树结构中的一个没有子元素的组件
class TextFile: File {var name: Stringinit(name: String) {self.name = name}func showInfo() {print("\(name) is TextFile")}
}class ImageFile: File {var name: Stringinit(name: String) {self.name = name}func showInfo() {print("\(name) is ImageFile")}
}class VideoFile: File {var name: Stringinit(name: String) {self.name = name}func showInfo() {print("\(name) is VideoFile")}
}// 三:Composite:容器,与Leaf不同的是有子元素,用来存储Leaf和其他Composite
class Fold: File {var name: Stringprivate(set) var files: [File] = []init(name: String) {self.name = name}func showInfo() {print("\(name) is Fold")files.forEach { (file) infile.showInfo()}}func addFile(file: File)  {files.append(file)}
}class Client {init() {}func test() {let fold1: Fold = Fold.init(name: "fold1")let fold2: Fold = Fold.init(name: "fold2")let text1: TextFile = TextFile.init(name: "text1")let text2: TextFile = TextFile.init(name: "text2")let image1: ImageFile = ImageFile.init(name: "image1")let image2: ImageFile = ImageFile.init(name: "image2")let video1: VideoFile = VideoFile.init(name: "video1")let video2: VideoFile = VideoFile.init(name: "video2")fold1.addFile(file: text1)fold2.addFile(file: text2)fold1.addFile(file: image1)fold2.addFile(file: image2)fold1.addFile(file: video1)fold2.addFile(file: video2)fold1.addFile(file: fold2)fold1.showInfo()}
}
复制代码
7、参考资料:

iOS_Design_Patterns_Swift

iOS应用开发中运用设计模式中的组合模式的实例解析

设计模式读书笔记-----组合模式

23个经典设计模式的Swift实现

【设计模式】19 – 组合模式 (Composite Pattern)

学习二、Masonry采用了经典的工厂设计模式(Factory Pattern)。

1、定义

工厂方法模式实质是“定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行"。 工厂方法要解决的问题对象的创建时机,它提供了一种扩展策略,很好地符合了开放封闭原则。与直接创建新的对象相比,使用工厂方法创建对象可算作一种最佳做法。工厂方法模式让客户程序可以要求由工厂方法创建的对象拥有一组共同的行为。所以往类层次结构中引入新的具体产品并不需要修改客户端代码,因为返回的任何具体对象的接口都跟客户端一直在用的从前的接口相同。

2、优点:
  • 1、将对象的创建和对象本身的业务处理分离可以降低系统的耦合度,使得两者的修改相对容易。
  • 2、扩展性高,屏蔽产品的具体实现,调用者只关心产品的接口
3、缺点:
  • 增加产品的时候,使得系统中的类个数大量增加,延长App的启动时间。
4、简单工厂模式、工厂模式、抽象工厂模式的区别
区别 简单工厂模式 工厂模式 抽象工厂模式
开放封闭程度
抽象产品的个数 一个 一个 多个
抽象工厂的个数 没有 一个 多个

工厂模式生产的是整车,抽象工厂模式生产的是零件组合成整车。

5、swift实现Demo

5.1、简单工厂模式

import UIKitenum VenderType {case razer, logitech, steelSeries
}
/// 抽象产品类
protocol Mouse {func click()
}
/// 具体产品类:雷蛇鼠标
class RazerMouse: Mouse {func click() {print("Razer click")}
}
/// 具体产品类:罗技鼠标
class LogitechMouse: Mouse {func click() {print("Logitech click")}
}
/// 具体产品类:赛睿鼠标
class SteelSeriesMouse: Mouse {func click() {print("SteelSeries click")}
}// 简单工厂模式
class SimpleFactoryClient {func create(type: VenderType) -> Mouse {switch type {case .razer:return self.razer()case .logitech:return self.logitech()case .steelSeries:return self.steelSeries()}}private func razer() -> Mouse {let mouse: RazerMouse = RazerMouse.init()return mouse}private func logitech() -> Mouse {let mouse: LogitechMouse = LogitechMouse.init()return mouse}private func steelSeries() -> Mouse {let mouse: SteelSeriesMouse = SteelSeriesMouse.init()return mouse}
}
复制代码

5.2、工厂模式

// 工厂模式
/// 抽象工厂类
protocol MouseProductable {func productMouse() -> Mouse
}
/// 具体工厂类:雷蛇工厂
class RazerFactory: MouseProductable {func productMouse() -> Mouse {let mouse: RazerMouse = RazerMouse.init()return mouse}
}
/// 具体工厂类:罗技工厂
class LogitechFactory: MouseProductable {func productMouse() -> Mouse {let mouse: LogitechMouse = LogitechMouse.init()return mouse}
}
/// 具体工厂类:赛睿工厂
class SteelSeriesFactory: MouseProductable {func productMouse() -> Mouse {let mouse: SteelSeriesMouse = SteelSeriesMouse.init()return mouse}
}
class FactoryClient {func create(type: VenderType) -> Mouse {switch type {case .razer:return self.razer()case .logitech:return self.logitech()case .steelSeries:return self.steelSeries()}}private func razer() -> Mouse {let factory: RazerFactory = RazerFactory.init()return factory.productMouse()}private func logitech() -> Mouse {let factory: LogitechFactory = LogitechFactory.init()return factory.productMouse()}private func steelSeries() -> Mouse {let factory: SteelSeriesFactory = SteelSeriesFactory.init()return factory.productMouse()}
}
复制代码

5.3、抽象工厂模式

// 抽象工厂模式
/// 抽象产品类
protocol Keyboard {func enter()
}
/// 具体产品类:雷蛇键盘
class RazerKeyboard: Keyboard {func enter() {print("RazerKeyboard enter")}
}
/// 具体产品类:罗技鼠标键盘
class LogitechKeyboard: Keyboard {func enter() {print("LogitechKeyboard enter")}
}
/// 具体产品类:赛睿鼠标键盘
class SteelSeriesKeyboard: Keyboard {func enter() {print("SteelSeriesKeyboard enter")}
}/// 抽象工厂类
protocol KeyboardProductable {func productKeyBoard() -> Keyboard
}/// 具体工厂类:雷蛇工厂(生产鼠标和键盘)
class RazerFactory: MouseProductable, KeyboardProductable {func productKeyBoard() -> Keyboard {let keyboard: RazerKeyboard = RazerKeyboard.init()return keyboard}func productMouse() -> Mouse {let mouse: RazerMouse = RazerMouse.init()return mouse}
}
/// 具体工厂类:罗技工厂(生产鼠标和键盘)
class LogitechFactory: MouseProductable, KeyboardProductable {func productKeyBoard() -> Keyboard {let keyboard: LogitechKeyboard = LogitechKeyboard.init()return keyboard}func productMouse() -> Mouse {let mouse: LogitechMouse = LogitechMouse.init()return mouse}
}
/// 具体工厂类:赛睿工厂(生产鼠标和键盘)
class SteelSeriesFactory: MouseProductable, KeyboardProductable {func productKeyBoard() -> Keyboard {let keyboard: SteelSeriesKeyboard = SteelSeriesKeyboard.init()return keyboard}func productMouse() -> Mouse {let mouse: SteelSeriesMouse = SteelSeriesMouse.init()return mouse}
}class FactoryClient {/// 生产鼠标////// - Parameter type: 厂商类型/// - Returns: 鼠标func createMouse(type: VenderType) -> Mouse {switch type {case .razer:return self.razerMouse()case .logitech:return self.logitechMouse()case .steelSeries:return self.steelSeriesMouse()}}private func razerMouse() -> Mouse {let factory: RazerFactory = RazerFactory.init()return factory.productMouse()}private func logitechMouse() -> Mouse {let factory: LogitechFactory = LogitechFactory.init()return factory.productMouse()}private func steelSeriesMouse() -> Mouse {let factory: SteelSeriesFactory = SteelSeriesFactory.init()return factory.productMouse()}/// 生产键盘////// - Parameter type: 厂商类型/// - Returns: 键盘func createKeyBoard(type: VenderType) -> Keyboard {switch type {case .razer:return self.razerKeyboard()case .logitech:return self.logitechKeyboard()case .steelSeries:return self.steelSeriesKeyboard()}}private func razerKeyboard() -> Keyboard {let factory: RazerFactory = RazerFactory.init()return factory.productKeyBoard()}private func logitechKeyboard() -> Keyboard {let factory: LogitechFactory = LogitechFactory.init()return factory.productKeyBoard()}private func steelSeriesKeyboard() -> Keyboard {let factory: SteelSeriesFactory = SteelSeriesFactory.init()return factory.productKeyBoard()}
}
复制代码

从上面的代码可以看出,抽象工厂模式的扩展性最好。

6、工厂模式在CocoaTouch中的应用
[NSNumber numberWithBool:YES];
[NSNumber numberWithInteger:1];
[NSNumber numberWithInt:1];
复制代码
7、参考资料:

Swift-工厂方法(Factory Method) 抽象工厂模式

学习三、链式语法

实现的核心:重写Block属性的Get方法,在Block里返回对象本身

#import "ChainProgramVC.h"@class ChainAnimal;
typedef void(^GeneralBlockProperty)(int count);
typedef ChainAnimal* (^ChainBlockProperty)(int count);@interface ChainAnimal : NSObject
@property (nonatomic, strong) GeneralBlockProperty     eat1;
@property (nonatomic, strong) ChainBlockProperty   eat2;
@end
@implementation ChainAnimal
/**函数返回一个block,block返回void*/
-(GeneralBlockProperty)eat1 {return ^(int count) {NSLog(@"%s count = %d", __func__, count);};
}
/**函数返回一个block,block返回ChainAnimal对象*/
- (ChainBlockProperty)eat2 {return ^(int count){NSLog(@"%s count = %d", __func__, count);return self;};
}
@end@interface ChainProgramVC ()
@property (nonatomic, strong) ChainAnimal *dog;
@end
@implementation ChainProgramVC
- (ChainAnimal *)dog {if (!_dog) {_dog = [[ChainAnimal alloc] init];}return _dog;
}
- (void)viewDidLoad {[super viewDidLoad];[super viewDidLoad];self.dog.eat1(1);self.dog.eat2(2).eat2(3).eat2(4).eat1(5);
}
@end
复制代码

学习四、接口简洁

把复杂留给自己,把简单留给别人

学习五、抽象方法小技巧

#define MASMethodNotImplemented() \@throw [NSException exceptionWithName:NSInternalInconsistencyException \reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)] \userInfo:nil]- (MASConstraint *)addConstraintWithLayoutAttribute:(NSLayoutAttribute __unused)layoutAttribute {MASMethodNotImplemented();
}
复制代码

自己实现类似需求的时候,可以采用这个技巧阻止直接使用抽象方法。

实践:实现一个自定义转场动画的基类
#import <UIKit/UIKit.h>NS_ASSUME_NONNULL_BEGIN@interface BaseAnimatedTransiton : NSObject<UIViewControllerAnimatedTransitioning>
@property (nonatomic, assign) NSTimeInterval p_transitionDuration;
+(instancetype)initWithTransitionDuration:(NSTimeInterval)transitionDuration;
-(instancetype)initWithTransitionDuration:(NSTimeInterval)transitionDuration NS_DESIGNATED_INITIALIZER;
@end#pragma mark - (Abstract)
@interface BaseAnimatedTransiton (Abstract)
// 子类实现,父类NSException
-(void)animate:(nonnull id<UIViewControllerContextTransitioning>)transitionContext;
@endNS_ASSUME_NONNULL_END
复制代码
#import "BaseAnimatedTransiton.h"@implementation BaseAnimatedTransiton
+(instancetype)initWithTransitionDuration:(NSTimeInterval)transitionDuration {BaseAnimatedTransiton* obj = [[BaseAnimatedTransiton alloc] init];obj.p_transitionDuration = transitionDuration;return obj;
}
-(instancetype)initWithTransitionDuration:(NSTimeInterval)transitionDuration {if (self = [super init]) {self.p_transitionDuration = transitionDuration;}return self;
}
-(instancetype)init {return [self initWithTransitionDuration:0.25];
}
-(void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {[self animate:transitionContext];
}
-(NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext {return self.p_transitionDuration;
}
-(void)animate:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {[self throwException:_cmd];
}
/**在Masonry的源码中使用的是宏(感觉宏不是很直观)@param aSelector 方法名字*/
-(void)throwException:(SEL)aSelector {@throw [NSException exceptionWithName:NSInternalInconsistencyExceptionreason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(aSelector)]userInfo:nil];
}
@end
复制代码

学习六、包装任何值类型为一个对象

我们添加约束的时候使用equalTo传入的参数只能是id类型的,而mas_equalTo可以任何类型的数据。

[view mas_makeConstraints:^(MASConstraintMaker *make) {make.size.mas_equalTo(CGSizeMake(100, 100));make.center.equalTo(self.view);// 下面这句效果与上面的效果一样//make.center.mas_equalTo(self.view);
}];
复制代码
#define mas_equalTo(...)                 equalTo(MASBoxValue((__VA_ARGS__)))
复制代码
/***  Given a scalar or struct value, wraps it in NSValue*  Based on EXPObjectify: https://github.com/specta/expecta*/
static inline id _MASBoxValue(const char *type, ...) {va_list v;va_start(v, type);id obj = nil;if (strcmp(type, @encode(id)) == 0) {id actual = va_arg(v, id);obj = actual;} else if (strcmp(type, @encode(CGPoint)) == 0) {CGPoint actual = (CGPoint)va_arg(v, CGPoint);obj = [NSValue value:&actual withObjCType:type];} else if (strcmp(type, @encode(CGSize)) == 0) {CGSize actual = (CGSize)va_arg(v, CGSize);obj = [NSValue value:&actual withObjCType:type];} else if (strcmp(type, @encode(MASEdgeInsets)) == 0) {MASEdgeInsets actual = (MASEdgeInsets)va_arg(v, MASEdgeInsets);obj = [NSValue value:&actual withObjCType:type];} else if (strcmp(type, @encode(double)) == 0) {double actual = (double)va_arg(v, double);obj = [NSNumber numberWithDouble:actual];} else if (strcmp(type, @encode(float)) == 0) {float actual = (float)va_arg(v, double);obj = [NSNumber numberWithFloat:actual];} else if (strcmp(type, @encode(int)) == 0) {int actual = (int)va_arg(v, int);obj = [NSNumber numberWithInt:actual];} else if (strcmp(type, @encode(long)) == 0) {long actual = (long)va_arg(v, long);obj = [NSNumber numberWithLong:actual];} else if (strcmp(type, @encode(long long)) == 0) {long long actual = (long long)va_arg(v, long long);obj = [NSNumber numberWithLongLong:actual];} else if (strcmp(type, @encode(short)) == 0) {short actual = (short)va_arg(v, int);obj = [NSNumber numberWithShort:actual];} else if (strcmp(type, @encode(char)) == 0) {char actual = (char)va_arg(v, int);obj = [NSNumber numberWithChar:actual];} else if (strcmp(type, @encode(bool)) == 0) {bool actual = (bool)va_arg(v, int);obj = [NSNumber numberWithBool:actual];} else if (strcmp(type, @encode(unsigned char)) == 0) {unsigned char actual = (unsigned char)va_arg(v, unsigned int);obj = [NSNumber numberWithUnsignedChar:actual];} else if (strcmp(type, @encode(unsigned int)) == 0) {unsigned int actual = (unsigned int)va_arg(v, unsigned int);obj = [NSNumber numberWithUnsignedInt:actual];} else if (strcmp(type, @encode(unsigned long)) == 0) {unsigned long actual = (unsigned long)va_arg(v, unsigned long);obj = [NSNumber numberWithUnsignedLong:actual];} else if (strcmp(type, @encode(unsigned long long)) == 0) {unsigned long long actual = (unsigned long long)va_arg(v, unsigned long long);obj = [NSNumber numberWithUnsignedLongLong:actual];} else if (strcmp(type, @encode(unsigned short)) == 0) {unsigned short actual = (unsigned short)va_arg(v, unsigned int);obj = [NSNumber numberWithUnsignedShort:actual];}va_end(v);return obj;
}#define MASBoxValue(value) _MASBoxValue(@encode(__typeof__((value))), (value))
复制代码

其中@encode()是一个编译时特性,其可以将传入的类型转换为标准的OC类型字符串

学习七、Block避免循环应用

Masonry中,Block持有View所在的ViewController,但是ViewController并没有持有Blcok,因此不会导致循环引用。

[self.view mas_makeConstraints:^(MASConstraintMaker *make) {make.centerY.equalTo(self.otherView.mas_centerY);
}];
复制代码

源码:仅仅是block(constrainMaker),没有被self持有

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {self.translatesAutoresizingMaskIntoConstraints = NO;MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];block(constraintMaker);return [constraintMaker install];
}
复制代码

参考资料

读 SnapKit 和 Masonry 自动布局框架源码

iOS开发之Masonry框架源码解析

Masonry 源码解读

Masonry源码解析

Masonry 源码学习整理相关推荐

  1. 从Masonry 源码探 Objective-C [源码学习开篇]

    原文链接 : http://blog.csdn.net/sinat_30162391/article/details/53321046 前言: 这个系列可能不会分析, Masonry如何如何好, 估计 ...

  2. mutations vuex 调用_Vuex源码学习(六)action和mutation如何被调用的(前置准备篇)...

    前言 Vuex源码系列不知不觉已经到了第六篇.前置的五篇分别如下: 长篇连载:Vuex源码学习(一)功能梳理 长篇连载:Vuex源码学习(二)脉络梳理 作为一个Web前端,你知道Vuex的instal ...

  3. Vuex源码学习(五)加工后的module

    没有看过moduleCollection那可不行!Vuex源码学习(四)module与moduleCollection 感谢提出代码块和截图建议的小伙伴 代码块和截图的区别: 代码块部分希望大家按照我 ...

  4. vue源码学习--vue源码学习入门

    本文为开始学习vue源码的思路整理.在拿到vue项目源码的之后看到那些项目中的文件夹,会很困惑,不知道每个文件夹内的世界,怎么变换,怎样的魔力,最后产生了vue框架.学习源码也无从学起.我解决了这些困 ...

  5. ASP.NET Core MVC 源码学习:MVC 启动流程详解

    前言 在 上一篇 文章中,我们学习了 ASP.NET Core MVC 的路由模块,那么在本篇文章中,主要是对 ASP.NET Core MVC 启动流程的一个学习. ASP.NET Core 是新一 ...

  6. action mutation 调用_Vuex源码学习(六)action和mutation如何被调用的(前置准备篇)...

    module与moduleCollection你一定要会啊!Vuex源码学习(五)加工后的module 在组件中使用vuex的dispatch和commit的时候,我们只要把action.mutati ...

  7. gallery3d源码学习总结(一)——绘制流程drawFocusItems

    eoe·Android开发者门户 标题: gallery3d源码学习总结(一)--绘制流程drawFocusItems [打印本页] 作者: specialbrian    时间: 2010-10-2 ...

  8. Electron源码学习: Electron组成与初始化流程

    Electron源码学习: Electron组成与结构 前言 ​ 最近研究学习Electron的源码结构已经有一些小的进展, 越接触Electron就越发现组成这个软件的大集合不得了.现在学习到的仍然 ...

  9. Mybatis源码学习(三)SqlSession详解

    前言 上一章节我们学习了SqlSessionFactory的源码,SqlSessionFactory中的方法都是围绕着SqlSession来的.,那么SqlSession又是什么东东呢?这一章节我们就 ...

最新文章

  1. PBR游戏3D模型合集包 PBR Game 3D-Models Bundle February 2022
  2. html文本显示_「前端开发」HTML入门与实战
  3. php单表查询语句,单表查询
  4. php动态删除输入框,jQuery实现动态添加和删除input框实例代码
  5. 从编解码、传输到基础架构 详解Bigo多媒体技术栈
  6. spring mvc ModelAndView向前台传值
  7. 你有程序员朋友吗?告诉他,100 万等他来拿
  8. springboot map数据类型注入_Spring Boot(五):春眠不觉晓,Mybatis知多少
  9. 为什么使用 SLF4J 而不是 Log4J 来做 Java 日志
  10. EEPlat的元数据驱动的运行引擎
  11. 【瑕疵检测】基于matlab Gabor滤波布匹瑕疵检测【含Matlab源码 407期】
  12. win7电脑桌面便签哪个好用
  13. word中插入对号、错号符号
  14. 【情报百科】一张照片就能精准锁定你的位置
  15. w ndows无法连接到System,Windows无法连接到System Event Notification Service服务解决方法...
  16. cursor常用的属性值
  17. 如何快速保存PPT中的图片资源
  18. MDUKEY创始人兼CEO韩元桢明确将通过“价格保障机制”确保MDU投资人的长久利益
  19. 《zBrow的资本论》
  20. 儿童产品上架亚马逊CPC认证介绍

热门文章

  1. java游戏繁体字名字_2020好听的繁体字游戏名字
  2. 兔子LOGO - 兔子标志 - 兔子商标 - 设计和创意欣赏
  3. Mathcad 是一种工程计算软件,主要运算功能:代数运算、线性代数、微积分、符号计算、2D和3D图表、动画、函数、程序编写、逻辑运算、变量与单位的定义和计算等。...
  4. 微博消息队列架构分析
  5. 前沿视频教室,学习网页和动画的设计开发制作技巧http://learning.artech.cn/
  6. 小程序源码:新款最火表情包壁纸独立后台美化二开版本新增加喝酒神器功能
  7. 已知A,B点,和C点,让C点在AB线段上移动
  8. word2vector使用
  9. 1分36秒,100亿的背后,双11前支付宝工程师竟然要拜关公?
  10. js中的全局作用域和局部的作用域