iOS老司机可落地的中大型iOS项目中的设计模式优化Tips_命令及单例模式

4,725 阅读4分钟

我正在参加「掘金·启航计划」

1. 前言: 设计模式可以在大型项目中有哪些可落地的优化?

  • 笔者目前负责一个中大型iOS项目,用PPRows跑一下项目根文件,目前代码量约28W。
  • 在这样一个大型项目中,我们组用了哪些方法对业务逻辑及代码架构进行解耦呢?
  • 在这种代码量级的项目中,有哪些接地气的,可落地的优化经验呢?
  • 在此抛砖引玉,欢迎大家一起相互探讨。

image.png

2. 落地

2.1 采用单例模式 做一个更加单一职责的广告管理类.

2.1.1 单例模式导图

image.png

2.1.2 单例模式, 核心类文件构成

image.png

2.1.3 采用单例模式解耦, 核心代码文件讲解

2.1.3.1 定义一个继承自NSObject的单例广告管理类ADManager

  • ADManager.h文件
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ADManager : NSObject

/// 供外部统一使用的单例类方法
+ (instancetype)sharedInstance;

@end

NS_ASSUME_NONNULL_END
  • ADManager.m文件
#import "ADManager.h"

@implementation ADManager

+ (instancetype)sharedInstance {
    // 静态局部变量
    static ADManager *adManager = nil;

    // 通过dispatch_once方式, 确保instance在多线程环境下只被创建一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 调用父类的方法创建实例, 防止跟重写自身的allocWithZone发生循环调用
        adManager = [[super allocWithZone:NULL] init];
    });
    return adManager;
}

// 重写自身的allocWithZone, 应对不使用sharedInstance方法直接通过alloc创建对象的情况
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    return [self sharedInstance];
}

// MRC下重写copyWithZone, 应对通过copy复制对象的情况, OBJC_ARC_UNAVAILABLE
+ (id)copyWithZone:(struct _NSZone *)zone {
    return self;
}

@end

2.1.3.2 实际业务使用单例模式示例

//
//  Viewontroller.m
//  appDesignPattern
//
//  Created by JackLee on 2022/9/21.
//

#import "ViewController.h"

#import "ADManager.h"

@interface ViewController ()

@end


@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 测试单例模式
    [self testSingleADManager];
}

/// 测试单例模式
- (void)testSingleADManager {
    ADManager *adManager = [ADManager sharedInstance];
    ADManager *adManager2 = [ADManager new];
    
    NSLog(@"adManager === %@ ptr === %p", adManager, adManager);
    NSLog(@"adManager2 === %@ ptr === %p", adManager2, adManager2);
}

image.png

2.2 采用命令模式使行为参数化, 降低代码重合度

2.2.1 命令模式导图

image.png

2.2.2 命令模式, 核心类文件构成

image.png

2.2.3 命令模式对行为进行参数化处理以解耦, 核心代码文件讲解

2.2.3.1 定义一个抽象命令类Command

  • Command.h文件
#import <Foundation/Foundation.h>

@class Command;
typedef  void(^CommandCompletionCallBack)(Command *cmd);

@interface Command : NSObject

// 命令完成的回调
@property (nonatomic, copy) CommandCompletionCallBack completion;

/// 执行命令
- (void)execute;
/// 取消命令
- (void)cancel;

/// 完成命令
- (void)done;

@end
  • Command.m文件
#import "Command.h"
#import "CommandManager.h"

@implementation Command

- (void)execute {
    // override to subclass, 交给子类复写具体实现
//    [self done];
}

- (void)cancel {
    self.completion = nil;
}

- (void)done {
    // 考虑到多线程的情况, 异步回到主队列
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.completion) {
            self.completion(self);
        }
        
        // 释放
        self.completion = nil;
        [[CommandManager sharedInstance].arrayCommands removeObject:self];
    });
}

@end
  • 具体的点赞命令LikedCommand.h
#import "Command.h"

NS_ASSUME_NONNULL_BEGIN

@interface LikedCommand : Command

@end

NS_ASSUME_NONNULL_END
  • 具体的点赞命令LikedCommand.m
#import "LikedCommand.h"

@implementation LikedCommand

- (void)execute {
    NSLog(@"执行点赞操作 ====");
    [self done];
}

@end
  • 具体的分享命令ShareCommand.h
#import "Command.h"

NS_ASSUME_NONNULL_BEGIN

@interface ShareCommand : Command

@end

NS_ASSUME_NONNULL_END
  • 具体的分享命令ShareCommand.m
#import "ShareCommand.h"

@implementation ShareCommand

- (void)execute {
    NSLog(@"执行分享操作 ====");
    [self done];
}

@end
  • 命令管理类CommandManager.h
#import <Foundation/Foundation.h>
#import "Command.h"

NS_ASSUME_NONNULL_BEGIN

@interface CommandManager : NSObject

/// 命令管理容器
@property(nonatomic, copy) NSMutableArray <Command *>*arrayCommands;

/// 命令管理者提供单例方法供使用者调用
+ (instancetype)sharedInstance;

/// 执行命令
+ (void)executeCommand:(Command *)cmd completion:(CommandCompletionCallBack)completion;

/// 取消命令
+ (void)cancelCommand:(Command *)cmd;  

@end

NS_ASSUME_NONNULL_END
  • 命令管理类CommandManager.m
#import "CommandManager.h"

@implementation CommandManager

+ (instancetype)sharedInstance {
    static CommandManager *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[super allocWithZone:NULL] init];
    });

    return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    return [self sharedInstance];
}

+ (void)executeCommand:(Command *)cmd completion:(CommandCompletionCallBack)completion {
    if (cmd) {
        // 如果命令正在执行不做处理, 否则添加并执行命令
        if (![self _isExecutingCommand:cmd]) {
            // 添加到命令容器当中
            [[[self sharedInstance] arrayCommands] addObject:cmd];
           
            // 设置具体命令执行完成后的回调
            cmd.completion = completion;
            
            // 调用具体命令执行方法
            [cmd execute];
        }
    }
}

+ (void)cancelCommand:(Command *)cmd {
    if (cmd) {
        // 从命令容器当中移除
        [[[self sharedInstance] arrayCommands] removeObject:cmd];

        // 取消命令执行
        [cmd cancel];
    }
}

+ (BOOL)_isExecutingCommand:(Command *)cmd {
    if (cmd) {
        NSArray *cmds = [[self sharedInstance] arrayCommands];
    
        for (Command *aCmd in cmds) {
            // 当前命令正在执行
            if (cmd == aCmd) {
                return YES;
            }
        }
    }
    return NO;
} 

@end

2.2.3.2 实际业务使用命令模式示例

//
//  Viewontroller.m
//  appDesignPattern
//
//  Created by JackLee on 2022/9/21.
//

#import "ViewController.h"

#import "LikedCommand.h"
#import "ShareCommand.h"
#import "CommandManager.h"

@interface ViewController ()

@end


@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 测试命令模式
    [self testCommand];
}

/// 测试命令模式
- (void)testCommand {
    LikedCommand *liked = [LikedCommand new];
    ShareCommand *share = [ShareCommand new];

    [CommandManager executeCommand:liked completion:^(Command *cmd) {
        NSLog(@"点赞命令完成回调 cmd === %@", cmd);
    }];

    [CommandManager executeCommand:share completion:^(Command *cmd) {
        NSLog(@"分享命令完成回调 cmd === %@", cmd);
    }];
}

@end

image.png

发文不易, 喜欢点赞的人更有好运气👍 :), 定期更新+关注不迷路~

ps:欢迎加入笔者18年建立的研究iOS审核及前沿技术的三千人扣群:662339934,坑位有限,备注“掘金网友”可被群管通过~