@(第三方库源码学习)html
[TOC]git
Masonry框架的类结构 github
Masonry
采用了经典的组合设计模式(Composite Pattern)。将对象组合成树状结构以表示“部分-总体”的层次结构。组合模式使得用户对单个对象(Leaf)和组合对象(Composite)的使用具备一致性。 注意:这个组合模式不是“组合优于继承”的那种组合,是狭义的指代一种特定的场景(树状结构)swift
import Foundation
// 一:Component协议:树中的组件(Leaf、Composite)都须要实现这个协议
protocol File {
var name: String { get set }
func showInfo()
}
// 二:Leaf:树结构中的一个没有子元素的组件
class TextFile: File {
var name: String
init(name: String) {
self.name = name
}
func showInfo() {
print("\(name) is TextFile")
}
}
class ImageFile: File {
var name: String
init(name: String) {
self.name = name
}
func showInfo() {
print("\(name) is ImageFile")
}
}
class VideoFile: File {
var name: String
init(name: String) {
self.name = name
}
func showInfo() {
print("\(name) is VideoFile")
}
}
// 三:Composite:容器,与Leaf不一样的是有子元素,用来存储Leaf和其余Composite
class Fold: File {
var name: String
private(set) var files: [File] = []
init(name: String) {
self.name = name
}
func showInfo() {
print("\(name) is Fold")
files.forEach { (file) in
file.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()
}
}
复制代码
【设计模式】19 – 组合模式 (Composite Pattern)框架
工厂方法模式
的实质是“定义一个建立对象的接口,但让实现这个接口的类来决定实例化哪一个类。工厂方法让类的实例化推迟到子类中进行"。 工厂方法要解决的问题是对象的建立时机
,它提供了一种扩展策略
,很好地符合了开放封闭原则
。与直接建立新的对象相比,使用工厂方法建立对象可算做一种最佳作法。工厂方法模式让客户程序能够要求由工厂方法建立的对象拥有一组共同的行为。因此往类层次结构中引入新的具体产品并不须要修改客户端代码,由于返回的任何具体对象的接口都跟客户端一直在用的从前的接口相同。ide
区别 | 简单工厂模式 | 工厂模式 | 抽象工厂模式 |
---|---|---|---|
开放封闭程度 | 弱 | 中 | 强 |
抽象产品的个数 | 一个 | 一个 | 多个 |
抽象工厂的个数 | 没有 | 一个 | 多个 |
工厂模式
生产的是整车,抽象工厂模式
生产的是零件组合成整车。
5.一、简单工厂模式
import UIKit
enum 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.二、工厂模式
// 工厂模式
/// 抽象工厂类
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.三、抽象工厂模式
// 抽象工厂模式
/// 抽象产品类
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()
}
}
复制代码
从上面的代码能够看出,抽象工厂模式的扩展性最好。
[NSNumber numberWithBool:YES];
[NSNumber numberWithInteger:1];
[NSNumber numberWithInt:1];
复制代码
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;
@end
NS_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:NSInternalInconsistencyException
reason:[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类型字符串
在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];
}
复制代码