iOS应用架构设计模式深度解析:MVC、MVP、MVVM与VIPER实战指南

在iOS应用开发中,选择合适的架构设计模式对于构建可维护、可测试和可扩展的应用至关重要。本文将深入探讨iOS开发中常用的架构模式,包括MVC、MVP、MVVM和VIPER,分析它们的优缺点,并提供实际的代码示例和最佳实践。

架构设计模式概述

为什么需要架构设计模式

1. 代码组织

将代码按照职责分离,提高可读性

降低模块间的耦合度

便于团队协作开发

2. 可维护性

易于修改和扩展功能

减少bug的产生

提高代码质量

3. 可测试性

支持单元测试

便于模拟和隔离测试

提高测试覆盖率

4. 可扩展性

支持功能的快速迭代

便于添加新特性

适应业务需求变化

架构模式选择原则

@interface ArchitectureEvaluator : NSObject

// 评估标准

@property (nonatomic, assign) NSInteger teamSize;

@property (nonatomic, assign) NSInteger projectComplexity;

@property (nonatomic, assign) NSInteger testingRequirement;

@property (nonatomic, assign) NSInteger maintenanceRequirement;

@property (nonatomic, assign) NSInteger developmentSpeed;

- (NSString *)recommendedArchitecture;

- (NSDictionary *)evaluateArchitectures;

@end

@implementation ArchitectureEvaluator

- (NSString *)recommendedArchitecture {

NSDictionary *scores = [self evaluateArchitectures];

NSString *bestArchitecture = nil;

NSInteger highestScore = 0;

for (NSString *architecture in scores.allKeys) {

NSInteger score = [scores[architecture] integerValue];

if (score > highestScore) {

highestScore = score;

bestArchitecture = architecture;

}

}

return bestArchitecture;

}

- (NSDictionary *)evaluateArchitectures {

NSMutableDictionary *scores = [NSMutableDictionary dictionary];

// MVC评分

NSInteger mvcScore = 0;

if (_teamSize <= 3) mvcScore += 2;

if (_projectComplexity <= 3) mvcScore += 2;

if (_developmentSpeed >= 4) mvcScore += 2;

scores[@"MVC"] = @(mvcScore);

// MVP评分

NSInteger mvpScore = 0;

if (_teamSize <= 5) mvpScore += 1;

if (_testingRequirement >= 3) mvpScore += 2;

if (_projectComplexity >= 3) mvpScore += 1;

scores[@"MVP"] = @(mvpScore);

// MVVM评分

NSInteger mvvmScore = 0;

if (_testingRequirement >= 4) mvvmScore += 2;

if (_maintenanceRequirement >= 4) mvvmScore += 2;

if (_projectComplexity >= 4) mvvmScore += 1;

scores[@"MVVM"] = @(mvvmScore);

// VIPER评分

NSInteger viperScore = 0;

if (_teamSize >= 5) viperScore += 2;

if (_projectComplexity >= 4) viperScore += 2;

if (_maintenanceRequirement >= 4) viperScore += 1;

if (_testingRequirement >= 4) viperScore += 1;

scores[@"VIPER"] = @(viperScore);

return scores;

}

@end

5. Router实现

// UserListRouter.h

@interface UserListRouter : NSObject

@property (nonatomic, weak) UIViewController *viewController;

@end

// UserListRouter.m

@implementation UserListRouter

- (void)navigateToUserDetail:(UserEntity *)user {

// 创建用户详情模块

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

UIViewController *userDetailVC = [storyboard instantiateViewControllerWithIdentifier:@"UserDetailViewController"];

// 传递用户数据

// [userDetailVC configureWithUser:user];

[self.viewController.navigationController pushViewController:userDetailVC animated:YES];

}

- (void)navigateToAddUser {

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

UIViewController *addUserVC = [storyboard instantiateViewControllerWithIdentifier:@"AddUserViewController"];

UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addUserVC];

[self.viewController presentViewController:navController animated:YES completion:nil];

}

@end

6. View实现

// UserListVIPERViewController.h

@interface UserListVIPERViewController : UIViewController

@property (nonatomic, weak) IBOutlet UITableView *tableView;

@property (nonatomic, weak) IBOutlet UIActivityIndicatorView *loadingIndicator;

@property (nonatomic, strong) id presenter;

@end

// UserListVIPERViewController.m

@implementation UserListVIPERViewController

- (void)viewDidLoad {

[super viewDidLoad];

[self setupUI];

[self setupTableView];

[self.presenter viewDidLoad];

}

- (void)setupUI {

self.title = @"Users (VIPER)";

UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh

target:self

action:@selector(refreshUsers)];

UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd

target:self

action:@selector(addUser)];

self.navigationItem.rightBarButtonItems = @[addButton, refreshButton];

}

- (void)setupTableView {

self.tableView.delegate = self;

self.tableView.dataSource = self;

UINib *cellNib = [UINib nibWithNibName:@"UserTableViewCell" bundle:nil];

[self.tableView registerNib:cellNib forCellReuseIdentifier:@"UserTableViewCell"];

self.tableView.rowHeight = 80;

self.tableView.estimatedRowHeight = 80;

UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];

[refreshControl addTarget:self action:@selector(refreshUsers) forControlEvents:UIControlEventValueChanged];

self.tableView.refreshControl = refreshControl;

}

- (void)refreshUsers {

[self.presenter refreshUsers];

}

- (void)addUser {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Add User"

message:@"Enter user information"

preferredStyle:UIAlertControllerStyleAlert];

[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {

textField.placeholder = @"Username";

}];

[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {

textField.placeholder = @"Email";

textField.keyboardType = UIKeyboardTypeEmailAddress;

}];

UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"

style:UIAlertActionStyleCancel

handler:nil];

UIAlertAction *addAction = [UIAlertAction actionWithTitle:@"Add"

style:UIAlertActionStyleDefault

handler:^(UIAlertAction *action) {

NSString *username = alert.textFields[0].text;

NSString *email = alert.textFields[1].text;

[self.presenter addUserWithUsername:username email:email];

}];

[alert addAction:cancelAction];

[alert addAction:addAction];

[self presentViewController:alert animated:YES completion:nil];

}

#pragma mark - UserListViewProtocol

- (void)showUsers:(NSArray *)users {

[self.tableView reloadData];

}

- (void)showLoading:(BOOL)loading {

if (loading) {

[self.loadingIndicator startAnimating];

} else {

[self.loadingIndicator stopAnimating];

[self.tableView.refreshControl endRefreshing];

}

self.tableView.userInteractionEnabled = !loading;

}

- (void)showError:(NSString *)errorMessage {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error"

message:errorMessage

preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK"

style:UIAlertActionStyleDefault

handler:nil];

[alert addAction:okAction];

[self presentViewController:alert animated:YES completion:nil];

}

- (void)showUserAdded {

NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.presenter numberOfUsers] - 1 inSection:0];

[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

}

- (void)showUserDeleted {

[self.tableView reloadData];

}

#pragma mark - UITableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

return [self.presenter numberOfUsers];

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

UserTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UserTableViewCell" forIndexPath:indexPath];

UserEntity *userEntity = [self.presenter userAtIndex:indexPath.row];

User *user = [userEntity toUser];

[cell configureWithUser:user];

return cell;

}

#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

[tableView deselectRowAtIndexPath:indexPath animated:YES];

[self.presenter selectUserAtIndex:indexPath.row];

}

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {

return YES;

}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

if (editingStyle == UITableViewCellEditingStyleDelete) {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Delete User"

message:@"Are you sure you want to delete this user?"

preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"

style:UIAlertActionStyleCancel

handler:nil];

UIAlertAction *deleteAction = [UIAlertAction actionWithTitle:@"Delete"

style:UIAlertActionStyleDestructive

handler:^(UIAlertAction *action) {

[self.presenter deleteUserAtIndex:indexPath.row];

}];

[alert addAction:cancelAction];

[alert addAction:deleteAction];

[self presentViewController:alert animated:YES completion:nil];

}

}

@end

7. 模块装配器

// UserListVIPERAssembler.h

@interface UserListVIPERAssembler : NSObject

+ (UIViewController *)assembleUserListModule;

@end

// UserListVIPERAssembler.m

@implementation UserListVIPERAssembler

+ (UIViewController *)assembleUserListModule {

// 创建各个组件

UserListVIPERViewController *view = [[UserListVIPERViewController alloc] init];

UserListPresenter *presenter = [[UserListPresenter alloc] init];

UserListInteractor *interactor = [[UserListInteractor alloc] init];

UserListRouter *router = [[UserListRouter alloc] init];

// 建立连接

view.presenter = presenter;

presenter.view = view;

presenter.interactor = interactor;

presenter.router = router;

interactor.output = presenter;

router.viewController = view;

return view;

}

@end

VIPER优缺点分析

优点:

职责分离非常清晰

高度可测试性

易于团队协作开发

支持复杂业务逻辑

导航逻辑独立管理

缺点:

代码量大,开发复杂度高

学习成本高

过度设计,适合大型项目

需要大量的协议定义

调试相对困难

架构模式对比分析

复杂度对比

@interface ArchitectureComplexityAnalyzer : NSObject

+ (NSDictionary *)analyzeComplexity;

+ (NSString *)recommendArchitectureForProjectSize:(NSInteger)projectSize

teamSize:(NSInteger)teamSize

complexity:(NSInteger)complexity;

@end

@implementation ArchitectureComplexityAnalyzer

+ (NSDictionary *)analyzeComplexity {

return @{

@"MVC": @{

@"learning_curve": @1,

@"code_volume": @1,

@"testability": @2,

@"maintainability": @2,

@"scalability": @2

},

@"MVP": @{

@"learning_curve": @2,

@"code_volume": @2,

@"testability": @4,

@"maintainability": @3,

@"scalability": @3

},

@"MVVM": @{

@"learning_curve": @3,

@"code_volume": @3,

@"testability": @4,

@"maintainability": @4,

@"scalability": @4

},

@"VIPER": @{

@"learning_curve": @4,

@"code_volume": @4,

@"testability": @5,

@"maintainability": @5,

@"scalability": @5

}

};

}

+ (NSString *)recommendArchitectureForProjectSize:(NSInteger)projectSize

teamSize:(NSInteger)teamSize

complexity:(NSInteger)complexity {

NSInteger totalScore = projectSize + teamSize + complexity;

if (totalScore <= 6) {

return @"MVC";

} else if (totalScore <= 9) {

return @"MVP";

} else if (totalScore <= 12) {

return @"MVVM";

} else {

return @"VIPER";

}

}

@end

性能对比

架构模式

内存占用

CPU开销

启动时间

运行效率

MVC

MVP

MVVM

中高

中高

VIPER

中低

适用场景分析

MVC适用场景:

小型项目(< 20个页面)

快速原型开发

团队规模小(1-3人)

简单的业务逻辑

MVP适用场景:

中小型项目(20-50个页面)

需要单元测试

团队规模中等(3-5人)

中等复杂度业务逻辑

MVVM适用场景:

中大型项目(50-100个页面)

复杂的数据绑定需求

团队有响应式编程经验

需要高度可测试性

VIPER适用场景:

大型企业级项目(> 100个页面)

复杂的业务逻辑

大型团队(> 5人)

长期维护项目

架构设计最佳实践

1. 渐进式架构演进

@interface ArchitectureEvolutionManager : NSObject

@property (nonatomic, strong) NSString *currentArchitecture;

@property (nonatomic, strong) NSArray *evolutionPath;

- (void)planEvolution:(NSString *)targetArchitecture;

- (BOOL)canEvolveToArchitecture:(NSString *)architecture;

- (NSArray *)getEvolutionSteps:(NSString *)targetArchitecture;

@end

@implementation ArchitectureEvolutionManager

- (instancetype)init {

self = [super init];

if (self) {

_currentArchitecture = @"MVC";

_evolutionPath = @[@"MVC", @"MVP", @"MVVM", @"VIPER"];

}

return self;

}

- (void)planEvolution:(NSString *)targetArchitecture {

NSArray *steps = [self getEvolutionSteps:targetArchitecture];

NSLog(@"Architecture Evolution Plan:");

for (NSInteger i = 0; i < steps.count; i++) {

NSLog(@"Step %ld: %@", (long)(i + 1), steps[i]);

}

}

- (BOOL)canEvolveToArchitecture:(NSString *)architecture {

NSInteger currentIndex = [self.evolutionPath indexOfObject:self.currentArchitecture];

NSInteger targetIndex = [self.evolutionPath indexOfObject:architecture];

return targetIndex > currentIndex;

}

- (NSArray *)getEvolutionSteps:(NSString *)targetArchitecture {

NSInteger currentIndex = [self.evolutionPath indexOfObject:self.currentArchitecture];

NSInteger targetIndex = [self.evolutionPath indexOfObject:targetArchitecture];

if (targetIndex <= currentIndex) {

return @[];

}

NSRange range = NSMakeRange(currentIndex + 1, targetIndex - currentIndex);

return [self.evolutionPath subarrayWithRange:range];

}

@end

2. 架构质量评估

@interface ArchitectureQualityAssessment : NSObject

- (NSDictionary *)assessCodebase:(NSString *)projectPath;

- (NSInteger)calculateMaintainabilityIndex:(NSDictionary *)metrics;

- (NSArray *)identifyArchitecturalSmells:(NSDictionary *)metrics;

- (NSDictionary *)generateImprovementSuggestions:(NSDictionary *)assessment;

@end

@implementation ArchitectureQualityAssessment

- (NSDictionary *)assessCodebase:(NSString *)projectPath {

// 模拟代码质量评估

return @{

@"coupling": @{

@"score": @75,

@"threshold": @80,

@"status": @"good"

},

@"cohesion": @{

@"score": @85,

@"threshold": @80,

@"status": @"excellent"

},

@"complexity": @{

@"score": @65,

@"threshold": @70,

@"status": @"warning"

},

@"testability": @{

@"score": @70,

@"threshold": @75,

@"status": @"warning"

},

@"maintainability": @{

@"score": @78,

@"threshold": @75,

@"status": @"good"

}

};

}

- (NSInteger)calculateMaintainabilityIndex:(NSDictionary *)metrics {

NSInteger coupling = [metrics[@"coupling"][@"score"] integerValue];

NSInteger cohesion = [metrics[@"cohesion"][@"score"] integerValue];

NSInteger complexity = [metrics[@"complexity"][@"score"] integerValue];

NSInteger testability = [metrics[@"testability"][@"score"] integerValue];

return (coupling + cohesion + complexity + testability) / 4;

}

- (NSArray *)identifyArchitecturalSmells:(NSDictionary *)metrics {

NSMutableArray *smells = [NSMutableArray array];

if ([metrics[@"coupling"][@"score"] integerValue] < 70) {

[smells addObject:@"High Coupling - Consider dependency injection"];

}

if ([metrics[@"cohesion"][@"score"] integerValue] < 70) {

[smells addObject:@"Low Cohesion - Review single responsibility principle"];

}

if ([metrics[@"complexity"][@"score"] integerValue] < 60) {

[smells addObject:@"High Complexity - Consider breaking down large classes"];

}

if ([metrics[@"testability"][@"score"] integerValue] < 60) {

[smells addObject:@"Poor Testability - Increase dependency injection usage"];

}

return [smells copy];

}

- (NSDictionary *)generateImprovementSuggestions:(NSDictionary *)assessment {

NSMutableDictionary *suggestions = [NSMutableDictionary dictionary];

for (NSString *metric in assessment.allKeys) {

NSDictionary *metricData = assessment[metric];

NSString *status = metricData[@"status"];

if ([status isEqualToString:@"warning"] || [status isEqualToString:@"critical"]) {

if ([metric isEqualToString:@"coupling"]) {

suggestions[metric] = @"Implement dependency injection and use protocols for abstraction";

} else if ([metric isEqualToString:@"complexity"]) {

suggestions[metric] = @"Break down large classes and methods, apply single responsibility principle";

} else if ([metric isEqualToString:@"testability"]) {

suggestions[metric] = @"Increase unit test coverage and use mock objects for dependencies";

}

}

}

return [suggestions copy];

}

@end

总结

在iOS应用开发中,选择合适的架构设计模式对于项目的成功至关重要。每种架构模式都有其适用的场景和优缺点:

关键要点:

MVC:适合小型项目和快速开发,但容易导致Controller臃肿

MVP:通过Presenter分离业务逻辑,提高可测试性

MVVM:利用数据绑定机制,适合复杂的UI交互场景

VIPER:最细粒度的职责分离,适合大型企业级项目

最佳实践建议:

渐进式演进:从简单架构开始,随着项目复杂度增长逐步演进

团队能力匹配:选择团队能够掌握和维护的架构模式

项目特点考虑:根据项目规模、复杂度和维护周期选择合适架构

质量持续监控:定期评估架构质量,及时重构和优化

文档和培训:确保团队成员理解架构设计原则和实现细节

通过合理选择和实施架构设计模式,可以显著提高iOS应用的代码质量、可维护性和开发效率,为项目的长期成功奠定坚实基础。

MVC架构模式

MVC基本原理

MVC(Model-View-Controller)是iOS开发中最传统的架构模式,将应用分为三个核心组件:

Model:数据模型和业务逻辑

View:用户界面展示

Controller:协调Model和View的交互

MVC实现示例

1. Model层实现

// User.h

@interface User : NSObject

@property (nonatomic, strong) NSString *userId;

@property (nonatomic, strong) NSString *username;

@property (nonatomic, strong) NSString *email;

@property (nonatomic, strong) NSDate *createdAt;

@property (nonatomic, assign) BOOL isActive;

- (instancetype)initWithDictionary:(NSDictionary *)dictionary;

- (NSDictionary *)toDictionary;

- (BOOL)isValidEmail;

@end

// User.m

@implementation User

- (instancetype)initWithDictionary:(NSDictionary *)dictionary {

self = [super init];

if (self) {

_userId = dictionary[@"id"];

_username = dictionary[@"username"];

_email = dictionary[@"email"];

_isActive = [dictionary[@"is_active"] boolValue];

NSString *dateString = dictionary[@"created_at"];

if (dateString) {

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'Z'";

_createdAt = [formatter dateFromString:dateString];

}

}

return self;

}

- (NSDictionary *)toDictionary {

NSMutableDictionary *dict = [NSMutableDictionary dictionary];

if (_userId) dict[@"id"] = _userId;

if (_username) dict[@"username"] = _username;

if (_email) dict[@"email"] = _email;

dict[@"is_active"] = @(_isActive);

if (_createdAt) {

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'Z'";

dict[@"created_at"] = [formatter stringFromDate:_createdAt];

}

return dict;

}

- (BOOL)isValidEmail {

NSString *emailRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}";

NSPredicate *emailPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];

return [emailPredicate evaluateWithObject:_email];

}

@end

// UserService.h

@interface UserService : NSObject

+ (instancetype)sharedService;

- (void)fetchUsersWithCompletion:(void(^)(NSArray *users, NSError *error))completion;

- (void)createUser:(User *)user completion:(void(^)(User *user, NSError *error))completion;

- (void)updateUser:(User *)user completion:(void(^)(User *user, NSError *error))completion;

- (void)deleteUserWithId:(NSString *)userId completion:(void(^)(BOOL success, NSError *error))completion;

@end

// UserService.m

@implementation UserService

+ (instancetype)sharedService {

static UserService *sharedInstance = nil;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

sharedInstance = [[UserService alloc] init];

});

return sharedInstance;

}

- (void)fetchUsersWithCompletion:(void(^)(NSArray *users, NSError *error))completion {

// 模拟网络请求

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 模拟数据

NSArray *mockData = @[

@{

@"id": @"1",

@"username": @"john_doe",

@"email": @"john@example.com",

@"is_active": @YES,

@"created_at": @"2023-01-15T10:30:00Z"

},

@{

@"id": @"2",

@"username": @"jane_smith",

@"email": @"jane@example.com",

@"is_active": @YES,

@"created_at": @"2023-02-20T14:45:00Z"

}

];

NSMutableArray *users = [NSMutableArray array];

for (NSDictionary *userData in mockData) {

User *user = [[User alloc] initWithDictionary:userData];

[users addObject:user];

}

dispatch_async(dispatch_get_main_queue(), ^{

if (completion) {

completion([users copy], nil);

}

});

});

}

- (void)createUser:(User *)user completion:(void(^)(User *user, NSError *error))completion {

// 验证用户数据

if (!user.username || user.username.length == 0) {

NSError *error = [NSError errorWithDomain:@"UserServiceError"

code:1001

userInfo:@{NSLocalizedDescriptionKey: @"Username is required"}];

if (completion) completion(nil, error);

return;

}

if (![user isValidEmail]) {

NSError *error = [NSError errorWithDomain:@"UserServiceError"

code:1002

userInfo:@{NSLocalizedDescriptionKey: @"Invalid email format"}];

if (completion) completion(nil, error);

return;

}

// 模拟网络请求

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 模拟服务器响应

user.userId = [[NSUUID UUID] UUIDString];

user.createdAt = [NSDate date];

user.isActive = YES;

dispatch_async(dispatch_get_main_queue(), ^{

if (completion) {

completion(user, nil);

}

});

});

}

- (void)updateUser:(User *)user completion:(void(^)(User *user, NSError *error))completion {

if (!user.userId) {

NSError *error = [NSError errorWithDomain:@"UserServiceError"

code:1003

userInfo:@{NSLocalizedDescriptionKey: @"User ID is required for update"}];

if (completion) completion(nil, error);

return;

}

// 模拟网络请求

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

dispatch_async(dispatch_get_main_queue(), ^{

if (completion) {

completion(user, nil);

}

});

});

}

- (void)deleteUserWithId:(NSString *)userId completion:(void(^)(BOOL success, NSError *error))completion {

if (!userId || userId.length == 0) {

NSError *error = [NSError errorWithDomain:@"UserServiceError"

code:1004

userInfo:@{NSLocalizedDescriptionKey: @"User ID is required for deletion"}];

if (completion) completion(NO, error);

return;

}

// 模拟网络请求

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

dispatch_async(dispatch_get_main_queue(), ^{

if (completion) {

completion(YES, nil);

}

});

});

}

@end

2. View层实现

// UserTableViewCell.h

@interface UserTableViewCell : UITableViewCell

@property (nonatomic, weak) IBOutlet UILabel *usernameLabel;

@property (nonatomic, weak) IBOutlet UILabel *emailLabel;

@property (nonatomic, weak) IBOutlet UILabel *statusLabel;

@property (nonatomic, weak) IBOutlet UIImageView *avatarImageView;

- (void)configureWithUser:(User *)user;

@end

// UserTableViewCell.m

@implementation UserTableViewCell

- (void)awakeFromNib {

[super awakeFromNib];

[self setupUI];

}

- (void)setupUI {

// 设置头像圆角

self.avatarImageView.layer.cornerRadius = 25;

self.avatarImageView.layer.masksToBounds = YES;

self.avatarImageView.backgroundColor = [UIColor lightGrayColor];

// 设置标签样式

self.usernameLabel.font = [UIFont boldSystemFontOfSize:16];

self.emailLabel.font = [UIFont systemFontOfSize:14];

self.emailLabel.textColor = [UIColor grayColor];

self.statusLabel.font = [UIFont systemFontOfSize:12];

self.statusLabel.layer.cornerRadius = 8;

self.statusLabel.layer.masksToBounds = YES;

self.statusLabel.textAlignment = NSTextAlignmentCenter;

}

- (void)configureWithUser:(User *)user {

self.usernameLabel.text = user.username;

self.emailLabel.text = user.email;

if (user.isActive) {

self.statusLabel.text = @"Active";

self.statusLabel.backgroundColor = [UIColor greenColor];

self.statusLabel.textColor = [UIColor whiteColor];

} else {

self.statusLabel.text = @"Inactive";

self.statusLabel.backgroundColor = [UIColor redColor];

self.statusLabel.textColor = [UIColor whiteColor];

}

// 设置默认头像

self.avatarImageView.image = [UIImage imageNamed:@"default_avatar"];

}

- (void)prepareForReuse {

[super prepareForReuse];

self.usernameLabel.text = nil;

self.emailLabel.text = nil;

self.statusLabel.text = nil;

self.avatarImageView.image = nil;

}

@end

3. Controller层实现

// UserListViewController.h

@interface UserListViewController : UIViewController

@property (nonatomic, weak) IBOutlet UITableView *tableView;

@property (nonatomic, weak) IBOutlet UIActivityIndicatorView *loadingIndicator;

@property (nonatomic, strong) NSMutableArray *users;

@property (nonatomic, strong) UserService *userService;

@end

// UserListViewController.m

@implementation UserListViewController

- (void)viewDidLoad {

[super viewDidLoad];

[self setupUI];

[self setupTableView];

[self loadUsers];

}

- (void)setupUI {

self.title = @"Users";

// 添加刷新按钮

UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh

target:self

action:@selector(refreshUsers)];

// 添加新增按钮

UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd

target:self

action:@selector(addUser)];

self.navigationItem.rightBarButtonItems = @[addButton, refreshButton];

// 初始化数据

self.users = [NSMutableArray array];

self.userService = [UserService sharedService];

}

- (void)setupTableView {

self.tableView.delegate = self;

self.tableView.dataSource = self;

// 注册cell

UINib *cellNib = [UINib nibWithNibName:@"UserTableViewCell" bundle:nil];

[self.tableView registerNib:cellNib forCellReuseIdentifier:@"UserTableViewCell"];

// 设置行高

self.tableView.rowHeight = 80;

self.tableView.estimatedRowHeight = 80;

// 添加下拉刷新

UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];

[refreshControl addTarget:self action:@selector(refreshUsers) forControlEvents:UIControlEventValueChanged];

self.tableView.refreshControl = refreshControl;

}

- (void)loadUsers {

[self showLoading:YES];

[self.userService fetchUsersWithCompletion:^(NSArray *users, NSError *error) {

[self showLoading:NO];

if (error) {

[self showErrorAlert:error.localizedDescription];

} else {

[self.users removeAllObjects];

[self.users addObjectsFromArray:users];

[self.tableView reloadData];

}

}];

}

- (void)refreshUsers {

[self.userService fetchUsersWithCompletion:^(NSArray *users, NSError *error) {

[self.tableView.refreshControl endRefreshing];

if (error) {

[self showErrorAlert:error.localizedDescription];

} else {

[self.users removeAllObjects];

[self.users addObjectsFromArray:users];

[self.tableView reloadData];

}

}];

}

- (void)addUser {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Add User"

message:@"Enter user information"

preferredStyle:UIAlertControllerStyleAlert];

[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {

textField.placeholder = @"Username";

}];

[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {

textField.placeholder = @"Email";

textField.keyboardType = UIKeyboardTypeEmailAddress;

}];

UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"

style:UIAlertActionStyleCancel

handler:nil];

UIAlertAction *addAction = [UIAlertAction actionWithTitle:@"Add"

style:UIAlertActionStyleDefault

handler:^(UIAlertAction *action) {

NSString *username = alert.textFields[0].text;

NSString *email = alert.textFields[1].text;

if (username.length > 0 && email.length > 0) {

[self createUserWithUsername:username email:email];

} else {

[self showErrorAlert:@"Please fill in all fields"];

}

}];

[alert addAction:cancelAction];

[alert addAction:addAction];

[self presentViewController:alert animated:YES completion:nil];

}

- (void)createUserWithUsername:(NSString *)username email:(NSString *)email {

User *newUser = [[User alloc] init];

newUser.username = username;

newUser.email = email;

[self showLoading:YES];

[self.userService createUser:newUser completion:^(User *user, NSError *error) {

[self showLoading:NO];

if (error) {

[self showErrorAlert:error.localizedDescription];

} else {

[self.users addObject:user];

NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.users.count - 1 inSection:0];

[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

}

}];

}

- (void)deleteUserAtIndexPath:(NSIndexPath *)indexPath {

User *user = self.users[indexPath.row];

[self showLoading:YES];

[self.userService deleteUserWithId:user.userId completion:^(BOOL success, NSError *error) {

[self showLoading:NO];

if (error) {

[self showErrorAlert:error.localizedDescription];

} else if (success) {

[self.users removeObjectAtIndex:indexPath.row];

[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

}

}];

}

- (void)showLoading:(BOOL)show {

if (show) {

[self.loadingIndicator startAnimating];

} else {

[self.loadingIndicator stopAnimating];

}

self.tableView.userInteractionEnabled = !show;

}

- (void)showErrorAlert:(NSString *)message {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error"

message:message

preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK"

style:UIAlertActionStyleDefault

handler:nil];

[alert addAction:okAction];

[self presentViewController:alert animated:YES completion:nil];

}

#pragma mark - UITableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

return self.users.count;

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

UserTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UserTableViewCell" forIndexPath:indexPath];

User *user = self.users[indexPath.row];

[cell configureWithUser:user];

return cell;

}

#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

[tableView deselectRowAtIndexPath:indexPath animated:YES];

User *user = self.users[indexPath.row];

// 可以在这里导航到用户详情页面

NSLog(@"Selected user: %@", user.username);

}

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {

return YES;

}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

if (editingStyle == UITableViewCellEditingStyleDelete) {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Delete User"

message:@"Are you sure you want to delete this user?"

preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"

style:UIAlertActionStyleCancel

handler:nil];

UIAlertAction *deleteAction = [UIAlertAction actionWithTitle:@"Delete"

style:UIAlertActionStyleDestructive

handler:^(UIAlertAction *action) {

[self deleteUserAtIndexPath:indexPath];

}];

[alert addAction:cancelAction];

[alert addAction:deleteAction];

[self presentViewController:alert animated:YES completion:nil];

}

}

@end

MVC优缺点分析

优点:

简单易懂,学习成本低

开发速度快,适合小型项目

iOS原生支持,框架集成度高

代码结构清晰,职责分离明确

缺点:

Controller容易变得臃肿(Massive View Controller)

难以进行单元测试

View和Controller耦合度高

复杂业务逻辑难以维护

MVP架构模式

MVP基本原理

MVP(Model-View-Presenter)模式通过引入Presenter层来解决MVC中Controller臃肿的问题:

Model:数据模型和业务逻辑

View:用户界面展示和用户交互

Presenter:处理业务逻辑,协调Model和View

MVP实现示例

1. View协议定义

// UserListViewProtocol.h

@protocol UserListViewProtocol

- (void)showUsers:(NSArray *)users;

- (void)showLoading:(BOOL)loading;

- (void)showError:(NSString *)errorMessage;

- (void)showUserAdded:(User *)user;

- (void)showUserDeleted:(NSIndexPath *)indexPath;

- (void)refreshUserList;

@end

2. Presenter实现

// UserListPresenter.h

@interface UserListPresenter : NSObject

@property (nonatomic, weak) id view;

@property (nonatomic, strong) UserService *userService;

@property (nonatomic, strong) NSMutableArray *users;

- (instancetype)initWithView:(id)view;

- (void)viewDidLoad;

- (void)refreshUsers;

- (void)addUserWithUsername:(NSString *)username email:(NSString *)email;

- (void)deleteUserAtIndex:(NSInteger)index;

- (User *)userAtIndex:(NSInteger)index;

- (NSInteger)numberOfUsers;

@end

// UserListPresenter.m

@implementation UserListPresenter

- (instancetype)initWithView:(id)view {

self = [super init];

if (self) {

_view = view;

_userService = [UserService sharedService];

_users = [NSMutableArray array];

}

return self;

}

- (void)viewDidLoad {

[self loadUsers];

}

- (void)loadUsers {

[self.view showLoading:YES];

[self.userService fetchUsersWithCompletion:^(NSArray *users, NSError *error) {

[self.view showLoading:NO];

if (error) {

[self.view showError:error.localizedDescription];

} else {

[self.users removeAllObjects];

[self.users addObjectsFromArray:users];

[self.view showUsers:[self.users copy]];

}

}];

}

- (void)refreshUsers {

[self.userService fetchUsersWithCompletion:^(NSArray *users, NSError *error) {

if (error) {

[self.view showError:error.localizedDescription];

} else {

[self.users removeAllObjects];

[self.users addObjectsFromArray:users];

[self.view refreshUserList];

}

}];

}

- (void)addUserWithUsername:(NSString *)username email:(NSString *)email {

// 输入验证

if (!username || username.length == 0) {

[self.view showError:@"Username is required"];

return;

}

if (!email || email.length == 0) {

[self.view showError:@"Email is required"];

return;

}

User *newUser = [[User alloc] init];

newUser.username = username;

newUser.email = email;

[self.view showLoading:YES];

[self.userService createUser:newUser completion:^(User *user, NSError *error) {

[self.view showLoading:NO];

if (error) {

[self.view showError:error.localizedDescription];

} else {

[self.users addObject:user];

[self.view showUserAdded:user];

}

}];

}

- (void)deleteUserAtIndex:(NSInteger)index {

if (index < 0 || index >= self.users.count) {

[self.view showError:@"Invalid user index"];

return;

}

User *user = self.users[index];

[self.view showLoading:YES];

[self.userService deleteUserWithId:user.userId completion:^(BOOL success, NSError *error) {

[self.view showLoading:NO];

if (error) {

[self.view showError:error.localizedDescription];

} else if (success) {

[self.users removeObjectAtIndex:index];

NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];

[self.view showUserDeleted:indexPath];

}

}];

}

- (User *)userAtIndex:(NSInteger)index {

if (index >= 0 && index < self.users.count) {

return self.users[index];

}

return nil;

}

- (NSInteger)numberOfUsers {

return self.users.count;

}

@end

3. View实现

// UserListMVPViewController.h

@interface UserListMVPViewController : UIViewController

@property (nonatomic, weak) IBOutlet UITableView *tableView;

@property (nonatomic, weak) IBOutlet UIActivityIndicatorView *loadingIndicator;

@property (nonatomic, strong) UserListPresenter *presenter;

@end

// UserListMVPViewController.m

@implementation UserListMVPViewController

- (void)viewDidLoad {

[super viewDidLoad];

[self setupUI];

[self setupTableView];

[self setupPresenter];

}

- (void)setupUI {

self.title = @"Users (MVP)";

UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh

target:self

action:@selector(refreshUsers)];

UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd

target:self

action:@selector(addUser)];

self.navigationItem.rightBarButtonItems = @[addButton, refreshButton];

}

- (void)setupTableView {

self.tableView.delegate = self;

self.tableView.dataSource = self;

UINib *cellNib = [UINib nibWithNibName:@"UserTableViewCell" bundle:nil];

[self.tableView registerNib:cellNib forCellReuseIdentifier:@"UserTableViewCell"];

self.tableView.rowHeight = 80;

self.tableView.estimatedRowHeight = 80;

UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];

[refreshControl addTarget:self action:@selector(refreshUsers) forControlEvents:UIControlEventValueChanged];

self.tableView.refreshControl = refreshControl;

}

- (void)setupPresenter {

self.presenter = [[UserListPresenter alloc] initWithView:self];

[self.presenter viewDidLoad];

}

- (void)refreshUsers {

[self.presenter refreshUsers];

}

- (void)addUser {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Add User"

message:@"Enter user information"

preferredStyle:UIAlertControllerStyleAlert];

[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {

textField.placeholder = @"Username";

}];

[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {

textField.placeholder = @"Email";

textField.keyboardType = UIKeyboardTypeEmailAddress;

}];

UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"

style:UIAlertActionStyleCancel

handler:nil];

UIAlertAction *addAction = [UIAlertAction actionWithTitle:@"Add"

style:UIAlertActionStyleDefault

handler:^(UIAlertAction *action) {

NSString *username = alert.textFields[0].text;

NSString *email = alert.textFields[1].text;

[self.presenter addUserWithUsername:username email:email];

}];

[alert addAction:cancelAction];

[alert addAction:addAction];

[self presentViewController:alert animated:YES completion:nil];

}

#pragma mark - UserListViewProtocol

- (void)showUsers:(NSArray *)users {

[self.tableView reloadData];

}

- (void)showLoading:(BOOL)loading {

if (loading) {

[self.loadingIndicator startAnimating];

} else {

[self.loadingIndicator stopAnimating];

}

self.tableView.userInteractionEnabled = !loading;

}

- (void)showError:(NSString *)errorMessage {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error"

message:errorMessage

preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK"

style:UIAlertActionStyleDefault

handler:nil];

[alert addAction:okAction];

[self presentViewController:alert animated:YES completion:nil];

}

- (void)showUserAdded:(User *)user {

NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.presenter numberOfUsers] - 1 inSection:0];

[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

}

- (void)showUserDeleted:(NSIndexPath *)indexPath {

[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

}

- (void)refreshUserList {

[self.tableView.refreshControl endRefreshing];

[self.tableView reloadData];

}

#pragma mark - UITableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

return [self.presenter numberOfUsers];

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

UserTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UserTableViewCell" forIndexPath:indexPath];

User *user = [self.presenter userAtIndex:indexPath.row];

[cell configureWithUser:user];

return cell;

}

#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

[tableView deselectRowAtIndexPath:indexPath animated:YES];

User *user = [self.presenter userAtIndex:indexPath.row];

NSLog(@"Selected user: %@", user.username);

}

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {

return YES;

}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

if (editingStyle == UITableViewCellEditingStyleDelete) {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Delete User"

message:@"Are you sure you want to delete this user?"

preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"

style:UIAlertActionStyleCancel

handler:nil];

UIAlertAction *deleteAction = [UIAlertAction actionWithTitle:@"Delete"

style:UIAlertActionStyleDestructive

handler:^(UIAlertAction *action) {

[self.presenter deleteUserAtIndex:indexPath.row];

}];

[alert addAction:cancelAction];

[alert addAction:deleteAction];

[self presentViewController:alert animated:YES completion:nil];

}

}

@end

MVP优缺点分析

优点:

业务逻辑与UI分离,便于测试

Presenter可以独立测试

View变得更加简单

代码复用性更好

缺点:

代码量增加,复杂度提升

需要定义更多的协议和接口

Presenter可能变得臃肿

学习成本相对较高

MVVM架构模式

MVVM基本原理

MVVM(Model-View-ViewModel)模式通过数据绑定机制实现View和ViewModel的自动同步:

Model:数据模型和业务逻辑

View:用户界面展示

ViewModel:视图模型,处理业务逻辑和数据绑定

MVVM实现示例

1. ViewModel实现

// UserListViewModel.h

@interface UserListViewModel : NSObject

// 数据属性

@property (nonatomic, strong, readonly) NSArray *users;

@property (nonatomic, assign, readonly) BOOL isLoading;

@property (nonatomic, strong, readonly) NSString *errorMessage;

// 命令属性

@property (nonatomic, strong, readonly) RACCommand *loadUsersCommand;

@property (nonatomic, strong, readonly) RACCommand *refreshUsersCommand;

@property (nonatomic, strong, readonly) RACCommand *addUserCommand;

@property (nonatomic, strong, readonly) RACCommand *deleteUserCommand;

// 信号属性

@property (nonatomic, strong, readonly) RACSignal *usersSignal;

@property (nonatomic, strong, readonly) RACSignal *loadingSignal;

@property (nonatomic, strong, readonly) RACSignal *errorSignal;

- (instancetype)initWithUserService:(UserService *)userService;

- (User *)userAtIndex:(NSInteger)index;

- (NSInteger)numberOfUsers;

@end

// UserListViewModel.m

@implementation UserListViewModel {

UserService *_userService;

NSMutableArray *_mutableUsers;

}

- (instancetype)initWithUserService:(UserService *)userService {

self = [super init];

if (self) {

_userService = userService;

_mutableUsers = [NSMutableArray array];

[self setupCommands];

[self setupSignals];

}

return self;

}

- (void)setupCommands {

// 加载用户命令

_loadUsersCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {

return [self loadUsersSignal];

}];

// 刷新用户命令

_refreshUsersCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {

return [self loadUsersSignal];

}];

// 添加用户命令

_addUserCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(NSDictionary *userInfo) {

return [self addUserSignal:userInfo];

}];

// 删除用户命令

_deleteUserCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(NSNumber *index) {

return [self deleteUserSignal:index.integerValue];

}];

}

- (void)setupSignals {

// 用户数据信号

_usersSignal = RACObserve(self, users);

// 加载状态信号

_loadingSignal = [RACSignal merge:@[

[self.loadUsersCommand.executing skip:1],

[self.refreshUsersCommand.executing skip:1],

[self.addUserCommand.executing skip:1],

[self.deleteUserCommand.executing skip:1]

]];

// 错误信号

_errorSignal = [RACSignal merge:@[

self.loadUsersCommand.errors,

self.refreshUsersCommand.errors,

self.addUserCommand.errors,

self.deleteUserCommand.errors

]];

}

- (RACSignal *)loadUsersSignal {

return [RACSignal createSignal:^RACDisposable *(id subscriber) {

[self->_userService fetchUsersWithCompletion:^(NSArray *users, NSError *error) {

if (error) {

[subscriber sendError:error];

} else {

[self->_mutableUsers removeAllObjects];

[self->_mutableUsers addObjectsFromArray:users];

[subscriber sendNext:[self->_mutableUsers copy]];

[subscriber sendCompleted];

}

}];

return nil;

}];

}

- (RACSignal *)addUserSignal:(NSDictionary *)userInfo {

return [RACSignal createSignal:^RACDisposable *(id subscriber) {

NSString *username = userInfo[@"username"];

NSString *email = userInfo[@"email"];

// 输入验证

if (!username || username.length == 0) {

NSError *error = [NSError errorWithDomain:@"ValidationError"

code:1001

userInfo:@{NSLocalizedDescriptionKey: @"Username is required"}];

[subscriber sendError:error];

return nil;

}

if (!email || email.length == 0) {

NSError *error = [NSError errorWithDomain:@"ValidationError"

code:1002

userInfo:@{NSLocalizedDescriptionKey: @"Email is required"}];

[subscriber sendError:error];

return nil;

}

User *newUser = [[User alloc] init];

newUser.username = username;

newUser.email = email;

[self->_userService createUser:newUser completion:^(User *user, NSError *error) {

if (error) {

[subscriber sendError:error];

} else {

[self->_mutableUsers addObject:user];

[subscriber sendNext:user];

[subscriber sendCompleted];

}

}];

return nil;

}];

}

- (RACSignal *)deleteUserSignal:(NSInteger)index {

return [RACSignal createSignal:^RACDisposable *(id subscriber) {

if (index < 0 || index >= self->_mutableUsers.count) {

NSError *error = [NSError errorWithDomain:@"ValidationError"

code:1003

userInfo:@{NSLocalizedDescriptionKey: @"Invalid user index"}];

[subscriber sendError:error];

return nil;

}

User *user = self->_mutableUsers[index];

[self->_userService deleteUserWithId:user.userId completion:^(BOOL success, NSError *error) {

if (error) {

[subscriber sendError:error];

} else if (success) {

[self->_mutableUsers removeObjectAtIndex:index];

[subscriber sendNext:@(index)];

[subscriber sendCompleted];

}

}];

return nil;

}];

}

// 属性访问器

- (NSArray *)users {

return [_mutableUsers copy];

}

- (BOOL)isLoading {

return self.loadUsersCommand.executing.boolValue ||

self.refreshUsersCommand.executing.boolValue ||

self.addUserCommand.executing.boolValue ||

self.deleteUserCommand.executing.boolValue;

}

- (User *)userAtIndex:(NSInteger)index {

if (index >= 0 && index < _mutableUsers.count) {

return _mutableUsers[index];

}

return nil;

}

- (NSInteger)numberOfUsers {

return _mutableUsers.count;

}

@end

2. View实现

// UserListMVVMViewController.h

@interface UserListMVVMViewController : UIViewController

@property (nonatomic, weak) IBOutlet UITableView *tableView;

@property (nonatomic, weak) IBOutlet UIActivityIndicatorView *loadingIndicator;

@property (nonatomic, strong) UserListViewModel *viewModel;

@end

// UserListMVVMViewController.m

@implementation UserListMVVMViewController

- (void)viewDidLoad {

[super viewDidLoad];

[self setupUI];

[self setupTableView];

[self setupViewModel];

[self bindViewModel];

}

- (void)setupUI {

self.title = @"Users (MVVM)";

UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh

target:self

action:@selector(refreshUsers)];

UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd

target:self

action:@selector(addUser)];

self.navigationItem.rightBarButtonItems = @[addButton, refreshButton];

}

- (void)setupTableView {

self.tableView.delegate = self;

self.tableView.dataSource = self;

UINib *cellNib = [UINib nibWithNibName:@"UserTableViewCell" bundle:nil];

[self.tableView registerNib:cellNib forCellReuseIdentifier:@"UserTableViewCell"];

self.tableView.rowHeight = 80;

self.tableView.estimatedRowHeight = 80;

UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];

self.tableView.refreshControl = refreshControl;

}

- (void)setupViewModel {

UserService *userService = [UserService sharedService];

self.viewModel = [[UserListViewModel alloc] initWithUserService:userService];

}

- (void)bindViewModel {

// 绑定用户数据

[self.viewModel.usersSignal subscribeNext:^(NSArray *users) {

[self.tableView reloadData];

}];

// 绑定加载状态

[self.viewModel.loadingSignal subscribeNext:^(NSNumber *loading) {

if (loading.boolValue) {

[self.loadingIndicator startAnimating];

} else {

[self.loadingIndicator stopAnimating];

[self.tableView.refreshControl endRefreshing];

}

self.tableView.userInteractionEnabled = !loading.boolValue;

}];

// 绑定错误信息

[self.viewModel.errorSignal subscribeNext:^(NSError *error) {

[self showErrorAlert:error.localizedDescription];

}];

// 绑定刷新控件

self.tableView.refreshControl.rac_command = self.viewModel.refreshUsersCommand;

// 绑定添加用户结果

[self.viewModel.addUserCommand.executionSignals.switchToLatest subscribeNext:^(User *user) {

NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.viewModel numberOfUsers] - 1 inSection:0];

[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

}];

// 绑定删除用户结果

[self.viewModel.deleteUserCommand.executionSignals.switchToLatest subscribeNext:^(NSNumber *index) {

NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index.integerValue inSection:0];

[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

}];

// 初始加载

[self.viewModel.loadUsersCommand execute:nil];

}

- (void)refreshUsers {

[self.viewModel.refreshUsersCommand execute:nil];

}

- (void)addUser {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Add User"

message:@"Enter user information"

preferredStyle:UIAlertControllerStyleAlert];

[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {

textField.placeholder = @"Username";

}];

[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {

textField.placeholder = @"Email";

textField.keyboardType = UIKeyboardTypeEmailAddress;

}];

UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"

style:UIAlertActionStyleCancel

handler:nil];

UIAlertAction *addAction = [UIAlertAction actionWithTitle:@"Add"

style:UIAlertActionStyleDefault

handler:^(UIAlertAction *action) {

NSString *username = alert.textFields[0].text;

NSString *email = alert.textFields[1].text;

NSDictionary *userInfo = @{

@"username": username ?: @"",

@"email": email ?: @""

};

[self.viewModel.addUserCommand execute:userInfo];

}];

[alert addAction:cancelAction];

[alert addAction:addAction];

[self presentViewController:alert animated:YES completion:nil];

}

- (void)showErrorAlert:(NSString *)message {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error"

message:message

preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK"

style:UIAlertActionStyleDefault

handler:nil];

[alert addAction:okAction];

[self presentViewController:alert animated:YES completion:nil];

}

#pragma mark - UITableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

return [self.viewModel numberOfUsers];

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

UserTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UserTableViewCell" forIndexPath:indexPath];

User *user = [self.viewModel userAtIndex:indexPath.row];

[cell configureWithUser:user];

return cell;

}

#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

[tableView deselectRowAtIndexPath:indexPath animated:YES];

User *user = [self.viewModel userAtIndex:indexPath.row];

NSLog(@"Selected user: %@", user.username);

}

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {

return YES;

}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

if (editingStyle == UITableViewCellEditingStyleDelete) {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Delete User"

message:@"Are you sure you want to delete this user?"

preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"

style:UIAlertActionStyleCancel

handler:nil];

UIAlertAction *deleteAction = [UIAlertAction actionWithTitle:@"Delete"

style:UIAlertActionStyleDestructive

handler:^(UIAlertAction *action) {

[self.viewModel.deleteUserCommand execute:@(indexPath.row)];

}];

[alert addAction:cancelAction];

[alert addAction:deleteAction];

[self presentViewController:alert animated:YES completion:nil];

}

}

@end

MVVM优缺点分析

优点:

数据绑定机制,减少样板代码

ViewModel易于测试

支持双向数据绑定

代码结构清晰,职责分离好

缺点:

学习成本高,需要理解响应式编程

调试相对困难

过度使用可能导致性能问题

需要额外的响应式编程框架支持

VIPER架构模式

VIPER基本原理

VIPER(View-Interactor-Presenter-Entity-Router)是一种更加细粒度的架构模式,将应用分为五个核心组件:

View:用户界面展示和用户交互

Interactor:业务逻辑处理

Presenter:格式化数据,处理View和Interactor之间的通信

Entity:数据模型

Router:导航逻辑处理

VIPER实现示例

1. Entity层

// UserEntity.h

@interface UserEntity : NSObject

@property (nonatomic, strong) NSString *userId;

@property (nonatomic, strong) NSString *username;

@property (nonatomic, strong) NSString *email;

@property (nonatomic, strong) NSDate *createdAt;

@property (nonatomic, assign) BOOL isActive;

- (instancetype)initWithUser:(User *)user;

- (User *)toUser;

@end

// UserEntity.m

@implementation UserEntity

- (instancetype)initWithUser:(User *)user {

self = [super init];

if (self) {

_userId = user.userId;

_username = user.username;

_email = user.email;

_createdAt = user.createdAt;

_isActive = user.isActive;

}

return self;

}

- (User *)toUser {

User *user = [[User alloc] init];

user.userId = self.userId;

user.username = self.username;

user.email = self.email;

user.createdAt = self.createdAt;

user.isActive = self.isActive;

return user;

}

@end

2. 协议定义

// UserListVIPERProtocols.h

// View协议

@protocol UserListViewProtocol

- (void)showUsers:(NSArray *)users;

- (void)showLoading:(BOOL)loading;

- (void)showError:(NSString *)errorMessage;

- (void)showUserAdded;

- (void)showUserDeleted;

@end

// Presenter协议

@protocol UserListPresenterProtocol

- (void)viewDidLoad;

- (void)refreshUsers;

- (void)addUserWithUsername:(NSString *)username email:(NSString *)email;

- (void)deleteUserAtIndex:(NSInteger)index;

- (void)selectUserAtIndex:(NSInteger)index;

- (UserEntity *)userAtIndex:(NSInteger)index;

- (NSInteger)numberOfUsers;

@end

// Interactor协议

@protocol UserListInteractorProtocol

- (void)fetchUsers;

- (void)createUserWithUsername:(NSString *)username email:(NSString *)email;

- (void)deleteUserAtIndex:(NSInteger)index;

@end

// Router协议

@protocol UserListRouterProtocol

- (void)navigateToUserDetail:(UserEntity *)user;

- (void)navigateToAddUser;

@end

// Interactor输出协议

@protocol UserListInteractorOutputProtocol

- (void)usersFetched:(NSArray *)users;

- (void)usersFetchFailed:(NSString *)errorMessage;

- (void)userCreated:(UserEntity *)user;

- (void)userCreationFailed:(NSString *)errorMessage;

- (void)userDeleted:(NSInteger)index;

- (void)userDeletionFailed:(NSString *)errorMessage;

@end

3. Interactor实现

// UserListInteractor.h

@interface UserListInteractor : NSObject

@property (nonatomic, weak) id output;

@property (nonatomic, strong) UserService *userService;

@property (nonatomic, strong) NSMutableArray *users;

@end

// UserListInteractor.m

@implementation UserListInteractor

- (instancetype)init {

self = [super init];

if (self) {

_userService = [UserService sharedService];

_users = [NSMutableArray array];

}

return self;

}

- (void)fetchUsers {

[self.userService fetchUsersWithCompletion:^(NSArray *users, NSError *error) {

if (error) {

[self.output usersFetchFailed:error.localizedDescription];

} else {

[self.users removeAllObjects];

for (User *user in users) {

UserEntity *entity = [[UserEntity alloc] initWithUser:user];

[self.users addObject:entity];

}

[self.output usersFetched:[self.users copy]];

}

}];

}

- (void)createUserWithUsername:(NSString *)username email:(NSString *)email {

// 输入验证

if (!username || username.length == 0) {

[self.output userCreationFailed:@"Username is required"];

return;

}

if (!email || email.length == 0) {

[self.output userCreationFailed:@"Email is required"];

return;

}

User *newUser = [[User alloc] init];

newUser.username = username;

newUser.email = email;

[self.userService createUser:newUser completion:^(User *user, NSError *error) {

if (error) {

[self.output userCreationFailed:error.localizedDescription];

} else {

UserEntity *entity = [[UserEntity alloc] initWithUser:user];

[self.users addObject:entity];

[self.output userCreated:entity];

}

}];

}

- (void)deleteUserAtIndex:(NSInteger)index {

if (index < 0 || index >= self.users.count) {

[self.output userDeletionFailed:@"Invalid user index"];

return;

}

UserEntity *entity = self.users[index];

[self.userService deleteUserWithId:entity.userId completion:^(BOOL success, NSError *error) {

if (error) {

[self.output userDeletionFailed:error.localizedDescription];

} else if (success) {

[self.users removeObjectAtIndex:index];

[self.output userDeleted:index];

}

}];

}

@end

4. Presenter实现

// UserListPresenter.h

@interface UserListPresenter : NSObject

@property (nonatomic, weak) id view;

@property (nonatomic, strong) id interactor;

@property (nonatomic, strong) id router;

@property (nonatomic, strong) NSArray *users;

@end

// UserListPresenter.m

@implementation UserListPresenter

- (instancetype)init {

self = [super init];

if (self) {

_users = @[];

}

return self;

}

#pragma mark - UserListPresenterProtocol

- (void)viewDidLoad {

[self.view showLoading:YES];

[self.interactor fetchUsers];

}

- (void)refreshUsers {

[self.interactor fetchUsers];

}

- (void)addUserWithUsername:(NSString *)username email:(NSString *)email {

[self.view showLoading:YES];

[self.interactor createUserWithUsername:username email:email];

}

- (void)deleteUserAtIndex:(NSInteger)index {

[self.view showLoading:YES];

[self.interactor deleteUserAtIndex:index];

}

- (void)selectUserAtIndex:(NSInteger)index {

if (index >= 0 && index < self.users.count) {

UserEntity *user = self.users[index];

[self.router navigateToUserDetail:user];

}

}

- (UserEntity *)userAtIndex:(NSInteger)index {

if (index >= 0 && index < self.users.count) {

return self.users[index];

}

return nil;

}

- (NSInteger)numberOfUsers {

return self.users.count;

}

#pragma mark - UserListInteractorOutputProtocol

- (void)usersFetched:(NSArray *)users {

self.users = users;

[self.view showLoading:NO];

[self.view showUsers:users];

}

- (void)usersFetchFailed:(NSString *)errorMessage {

[self.view showLoading:NO];

[self.view showError:errorMessage];

}

- (void)userCreated:(UserEntity *)user {

self.users = [self.users arrayByAddingObject:user];

[self.view showLoading:NO];

[self.view showUserAdded];

}

- (void)userCreationFailed:(NSString *)errorMessage {

[self.view showLoading:NO];

[self.view showError:errorMessage];

}

- (void)userDeleted:(NSInteger)index {

NSMutableArray *mutableUsers = [self.users mutableCopy];

[mutableUsers removeObjectAtIndex:index];

self.users = [mutableUsers copy];

[self.view showLoading:NO];

[self.view showUserDeleted];

}

- (void)userDeletionFailed:(NSString *)errorMessage {

[self.view showLoading:NO];

[self.view showError:errorMessage];

}

@end