高质量的项目

一、管理

1、规范代码管理

2、搭建项目框架,制定代码规范

3、制订接口文档规范

4、统一封装满足多样化网络请求的网络库基础接口

5、组件的解耦封装

6、在当前工程中导入另一个工程文件

可扩展性:构建可扩展的iOS应用架构

稳定性治理:监控和分析崩溃日志来识别和修复潜在的问题。内存泄露。

iOS应用的稳定性可以通过多种方式来提升。首先,通过监控和分析崩溃日志来识别和修复潜在的问题。其次,通过优化代码和内存管理来减少卡死和崩溃的发生。例如,避免死锁、线程饥饿和内存泄漏等问题。此外,使用性能监控工具来检测应用的性能瓶颈,如卡顿和延迟,也是提高稳定性的重要手段。

灵活性:面向协议编程

二、风险预测

1、风险可能

1、不要过分相信服务器返回的数据会永远的正确。

2、在对数据处理上,要进行容错处理,进行相应判断之后再处理数据,这是一个良好的编程习惯。

2、思考:如何防止存在潜在崩溃方法的崩溃

  • 众所周知,Foundation框架里有非常多常用的方法有导致崩溃的潜在危险。对于一个已经将近竣工的项目,若起初没做容错处理又该怎么办?你总不会一行行代码去排查有没有做容错处理吧!——– 别逗逼了,老板催你明天就要上线了!
  • 那有没有一种一劳永逸的方法?无需动原本的代码就可以解决潜在崩溃的问题呢?

3、解决方案

解决方案:拦截存在潜在崩溃危险的方法,在拦截的方法里进行相应的处理,就可以防止方法的崩溃

步骤:

1
2
3
1、通过category给类添加方法用来替换掉原本存在潜在崩溃的方法。
2、利用runtime方法交换技术,将系统方法替换成我们给类添加的新方法。
3、利用异常的捕获来防止程序的崩溃,并且进行相应的处理。

4、消息转发机制

iOS的消息转发机制详解

5、消息转发机制的运用

1、简单问题引导

用Runtime解决服务器返回NSNull问题

思路:重写NSNull的消息转发方法, 让他能处理这些异常的方法.

使用 Method Swizzling 交换 objectForKey: 和 objectAtIndex: 是 方法交换(Method Swizzling),并不是 消息转发机制(Message Forwarding)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
✅ 使用 Runtime 交换方法,拦截 NSNull,避免手动检查
✅ 提升代码健壮性,防止 NSNull 访问崩溃
✅ 适用于 NSDictionaryNSArray,避免手动 if ([obj isKindOfClass:[NSNull class]])
#import <objc/runtime.h>

@implementation NSDictionary (Safe)

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originalMethod = class_getInstanceMethod(self, @selector(objectForKey:));
Method swizzledMethod = class_getInstanceMethod(self, @selector(safe_objectForKey:));
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}

- (id)safe_objectForKey:(id)key {
id value = [self safe_objectForKey:key]; // 调用原方法(已被交换)
if ([value isKindOfClass:[NSNull class]]) {
return nil; // 拦截 NSNull,返回 nil
}
return value;
}

@end



@implementation NSArray (Safe)

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originalMethod = class_getInstanceMethod(self, @selector(objectAtIndex:));
Method swizzledMethod = class_getInstanceMethod(self, @selector(safe_objectAtIndex:));
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}

- (id)safe_objectAtIndex:(NSUInteger)index {
if (index >= self.count) {
return nil; // 避免数组越界
}
id value = [self safe_objectAtIndex:index]; // 调用原方法
if ([value isKindOfClass:[NSNull class]]) {
return nil; // 拦截 NSNull
}
return value;
}

@end

🔥 使用消息转发机制(Message Forwarding)解决 NSNull 问题

🔹 原理

​ 1. 当访问 NSNull 的方法时,系统先在 NSNull 查找对应的方法

​ 2. 如果 NSNull 没有该方法,会触发消息转发流程

​ 3. 重写 forwardingTargetForSelector: 或 methodSignatureForSelector: + forwardInvocation: 来拦截并提供安全返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
NSNull *nullValue = [NSNull null];
// ✅ 伪装成 NSString
NSLog(@"Length: %lu", (unsigned long)[nullValue length]); // 输出 0
// ✅ 伪装成 NSNumber
NSLog(@"Integer: %ld", (long)[nullValue integerValue]); // 输出 0



✅ 方案 1:使用 forwardingTargetForSelector:(快速消息转发),返回要让哪个tagert去执行aSelector方法。
#import <Foundation/Foundation.h>

@interface NSNull (SafeForwarding)
@end

@implementation NSNull (SafeForwarding)

- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(@"⚠️ NSNull 被调用方法: %@", NSStringFromSelector(aSelector));

static NSString *emptyString = @"";
static NSNumber *zeroNumber = @0;

// 常见的返回值处理
if ([emptyString respondsToSelector:aSelector]) {
return emptyString; // 例如 `length` 方法返回空字符串的长度
} else if ([zeroNumber respondsToSelector:aSelector]) {
return zeroNumber; // 例如 `integerValue` 返回 0
}

return nil; // 其他情况返回 nil,避免崩溃
}

@end

✅ 方案 2:完全转发,略
// 方法签名的理解:方法签名(NSMethodSignature)是一个 描述方法参数和返回值信息的对象。NSMethodSignature 允许我们查询方法的返回值类型和参数类型。
假设 NSString 有这样一个方法:
- (NSUInteger)length;
它的 方法签名 大致如下:
NSMethodSignature *sig = [NSString instanceMethodSignatureForSelector:@selector(length)];

防止Crash 组件

2、系统性解决:

iOS runtime实用篇 —避免常见崩溃

其中AvoidCrash的代码如下:AvoidCrash源代码

附:NSException

iOS被开发者遗忘在角落的NSException-其实它很强大

利用OC的消息转发机制实现多重代理

三、资源

app瘦身

四、代码

1、单元测试

单元测试分为3种:

1
2
3
逻辑测试:测试逻辑方法
异步测试:测试耗时方法(用来测试包含多线程的方法)
性能测试:测试某一方法运行所消耗的时间

为什么要使用单元测试:

1
2
3
经济上的问题:

假设要开发的是对接获取验证码接口的方法,难道运行一次就真的请求一个短信验证码?短信下发平台可是会¥扣钱¥的。更何况,万一第三方平台没有响应或超时,我们的测试就失败了,这种异步的、不确定的测试,无论从金钱还是时间上衡量,都不够经济,因此很难实现。

什么情况下时序使用单元测试(单元测试使用的注意事项):

1
2
3
4
5
6
1、不是所有的方法都需要测试。
例如:私有方法不需要测试!只有暴露在 .h 中的方法需要测试!面向对象有一个原则:开闭原则!
2、所有跟 UI 有关的都不需要测试,也不好测试。
把 业务逻辑 代码封装出来!变成可以测试的代码,让程序更加健壮!
3、一般而言,代码的覆盖度大概在 50% ~ 70%
从github上得知:YYModel测试覆盖度为83%,AFNetworking测试覆盖度为77%,两者都是比较高的。

2、优化多线程处理,改善多线程嵌套严重,请求耗时的问题

解决:优化多线程处理,改善多线程嵌套严重,请求耗时的问题。

详细:原本项目,采用多线程嵌套的同步方式处理多个线程请求到数据后,再执行最后操作。经优化多线程处理为异步执行时,改善了多线程嵌套严重,请求耗时的问题。

3、定时器使用的优化

4、UITableView等的性能优化

5、优化代码逻辑处理(耗电量、耗流量)

性能优化