@(第三方庫源碼學習)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];
}
複製代碼