Skip to content

Commit 90589e9

Browse files
committed
7.0
1 parent 90a68a7 commit 90589e9

231 files changed

Lines changed: 4154 additions & 403885 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
name: Bug Report
3+
about: 报告一个 Bug / Report a bug
4+
title: '[Bug] '
5+
labels: bug
6+
assignees: ''
7+
---
8+
9+
## 环境 / Environment
10+
11+
- SmartCodable 版本:
12+
- Swift 版本:
13+
- Xcode 版本:
14+
- 平台(iOS/macOS/tvOS 等):
15+
- 安装方式(SPM / CocoaPods):
16+
17+
## 描述 / Description
18+
19+
简要描述遇到的问题。
20+
21+
## 复现代码 / Reproduce
22+
23+
提供最小可复现的代码(包含 JSON 数据、Model 定义、调用代码):
24+
25+
```swift
26+
// JSON
27+
let json = """
28+
{
29+
30+
}
31+
"""
32+
33+
// Model
34+
struct Model: SmartCodableX {
35+
36+
}
37+
38+
// 调用
39+
let model = Model.deserialize(from: json)
40+
```
41+
42+
## 期望结果 / Expected
43+
44+
描述期望的解析结果。
45+
46+
## 实际结果 / Actual
47+
48+
描述实际的解析结果。
49+
50+
## SmartSentinel 日志 / Sentinel Log(可选)
51+
52+
```
53+
// 开启 SmartSentinel.debugMode = .verbose 后的输出
54+
```
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
name: Feature Request
3+
about: 建议新功能或改进 / Suggest a feature or improvement
4+
title: '[Feature] '
5+
labels: enhancement
6+
assignees: ''
7+
---
8+
9+
## 问题描述 / Problem
10+
11+
描述你想解决的问题,或当前使用中遇到的不便。
12+
13+
## 期望方案 / Proposed Solution
14+
15+
描述你期望的 API 或行为:
16+
17+
```swift
18+
// 期望的使用方式
19+
```
20+
21+
## 替代方案 / Alternatives
22+
23+
是否有现有的替代方案?如果有,为什么不够好?
24+
25+
## 补充信息 / Additional Context
26+
27+
其他相关信息、截图、链接等。

.github/pull_request_template.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
## 改动说明 / Summary
2+
3+
简要描述这个 PR 做了什么,以及为什么要做。
4+
5+
## 改动类型 / Type
6+
7+
- [ ] Bug 修复
8+
- [ ] 新功能
9+
- [ ] 重构(不改变功能)
10+
- [ ] 文档
11+
- [ ] 测试
12+
- [ ] 其他
13+
14+
## 关联 Issue / Related Issue
15+
16+
Closes #
17+
18+
## 验证方式 / How to Test
19+
20+
描述如何验证这个改动是正确的:
21+
22+
- [ ] `swift build` 通过
23+
- [ ] 手动验证了相关场景
24+
- [ ] 添加/更新了测试用例
25+
26+
## 注意事项 / Notes
27+
28+
是否有需要特别注意的地方?是否有 breaking change?

CONTRIBUTING.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Contributing to SmartCodable
2+
3+
感谢你对 SmartCodable 的关注!我们欢迎各种形式的贡献:Bug 报告、功能建议、文档改进、代码贡献。
4+
5+
## 开始之前
6+
7+
1. 阅读 [README](README.md) 了解项目功能
8+
2. 阅读 [TechnicalGuide](Document/TechnicalGuide.md) 了解项目架构和设计意图
9+
3. 检查 [Issues](https://github.com/iAmMccc/SmartCodable/issues) 确认你的问题/想法是否已被讨论
10+
11+
## 报告 Bug
12+
13+
使用 [Bug Report 模板](https://github.com/iAmMccc/SmartCodable/issues/new?template=bug_report.md) 提交,请包含:
14+
15+
- SmartCodable 版本、Swift 版本、Xcode 版本
16+
- 最小可复现代码(JSON 数据 + Model 定义 + 调用代码)
17+
- 期望结果 vs 实际结果
18+
- 如果可能,附上 `SmartSentinel.debugMode = .verbose` 的日志输出
19+
20+
## 功能建议
21+
22+
使用 [Feature Request 模板](https://github.com/iAmMccc/SmartCodable/issues/new?template=feature_request.md) 提交,请说明:
23+
24+
- 你想解决什么问题
25+
- 你期望的 API 是什么样的
26+
- 是否有替代方案
27+
28+
## 提交代码
29+
30+
### 环境准备
31+
32+
```bash
33+
git clone https://github.com/iAmMccc/SmartCodable.git
34+
cd SmartCodable
35+
swift build
36+
```
37+
38+
- 最低要求:Swift 5.0、Xcode 14
39+
- 宏相关功能需要:Swift 5.9、Xcode 15
40+
41+
### 开发流程
42+
43+
1.`main` 分支创建你的功能分支:`git checkout -b feature/your-feature`
44+
2. 进行修改
45+
3. 确保 `swift build` 通过
46+
4. 提交 PR,描述你的改动内容和原因
47+
48+
### 代码规范
49+
50+
- **不破坏公共 API**`SmartDecodable``SmartEncodable`、属性包装器的公开接口不能改签名
51+
- **向后兼容**:保持 Swift 5.0+ / iOS 13+ 的最低版本要求
52+
- **不新增 SwiftSyntax 依赖**:核心模块(`SmartCodable` target)不能依赖 SwiftSyntax
53+
- **DecodingCache 快照必须成对调用**`cacheSnapshot()``removeSnapshot()` 必须配对,注意异常路径
54+
- 修改核心解码逻辑后,至少手动验证:简单模型、嵌套模型、数组模型、类型不匹配、缺失字段
55+
56+
### Commit 规范
57+
58+
```
59+
<type>: <简短描述>
60+
61+
<详细说明(可选)>
62+
```
63+
64+
type 取值:
65+
- `fix`: Bug 修复
66+
- `feat`: 新功能
67+
- `docs`: 文档
68+
- `refactor`: 重构(不改变功能)
69+
- `test`: 测试
70+
- `chore`: 构建/CI/工具链
71+
72+
示例:
73+
```
74+
fix: 修复 UnkeyedContainer 解码 String 时 currentIndex 被递增两次
75+
76+
decodeIfPresent(String) 方法开头多了一次 currentIndex += 1,
77+
导致数组解析时跳过元素。与 decodeIfPresent(Bool) 对比确认为遗漏。
78+
```
79+
80+
### PR 说明
81+
82+
- PR 标题简洁,正文说明改了什么、为什么改
83+
- 一个 PR 只做一件事,避免混合不同类型的改动
84+
- 如果是较大的功能,建议先开 Issue 讨论方案
85+
86+
## 标记为 `good first issue` 的任务
87+
88+
如果你是第一次贡献,可以从标记为 [`good first issue`](https://github.com/iAmMccc/SmartCodable/labels/good%20first%20issue) 的任务开始。这些任务通常是:
89+
90+
- 补充单元测试
91+
- 改善文档和示例
92+
- 修复已确认的小 Bug
93+
94+
## 捐赠支持
95+
96+
如果你想通过捐赠支持项目,请查看 [捐赠页面](Explore%26Contribute/Contributing.md)
97+
98+
## 社区
99+
100+
- [GitHub Discussions](https://github.com/iAmMccc/SmartCodable/discussions) — 提问与讨论
101+
- QQ 交流群:865036731

Document/CodeOptimization.md

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# 代码质量优化记录
2+
3+
**分支:** `6.1.0`
4+
**日期:** 2026-04-20
5+
6+
---
7+
8+
## 一、Bug 修复与拼写修正
9+
10+
### 1.1 `decodeIfPresent(String)``currentIndex` 被递增两次
11+
12+
**文件:** `JSONDecoderImpl+UnkeyedContainer.swift`
13+
14+
**问题:** `currentIndex` 在方法开头被递增了一次,在成功路径末尾又递增了一次。而 fallback 路径 `optionalDecode()` 内部每条分支也会递增 `currentIndex`,导致实际跳过两个元素。
15+
16+
**修复前:**
17+
```swift
18+
mutating func decodeIfPresent(_ type: String.Type) throws -> String? {
19+
self.currentIndex += 1 // <- 第 1 次递增
20+
guard let value = try? self.getNextValue(ofType: String.self) else {
21+
return optionalDecode() // <- optionalDecode 内部第 2 次递增
22+
}
23+
guard case .string(let string) = value else {
24+
return optionalDecode() // <- optionalDecode 内部第 2 次递增
25+
}
26+
self.currentIndex += 1 // <- 第 2 次递增
27+
return string
28+
}
29+
```
30+
31+
**修复后:**
32+
```swift
33+
mutating func decodeIfPresent(_ type: String.Type) throws -> String? {
34+
guard let value = try? self.getNextValue(ofType: String.self) else {
35+
return optionalDecode()
36+
}
37+
guard case .string(let string) = value else {
38+
return optionalDecode()
39+
}
40+
self.currentIndex += 1
41+
return string
42+
}
43+
```
44+
45+
**验证方式:**`decodeIfPresent(Bool)` 等同类方法结构完全一致;`optionalDecode()` 内部三条路径都会自行递增 `currentIndex`
46+
47+
### 1.2 `decode(String)``getNextValue` 误传 `Bool.self`
48+
49+
**文件:** `JSONDecoderImpl+UnkeyedContainer.swift`
50+
51+
**问题:** `decode(_ type: String.Type)` 方法中调用 `getNextValue(ofType: Bool.self)`,应为 `String.self`
52+
53+
`getNextValue(ofType:)` 的泛型参数 `T` 仅用于错误信息(`valueNotFound` 的类型参数),不影响返回值(始终返回 `self.array[self.currentIndex]`)。因此功能不受影响,但如果数组越界,错误信息会误报为 "Expected Bool" 而非 "Expected String"。
54+
55+
**修复:** `Bool.self``String.self`
56+
57+
### 1.3 变量名拼写错误
58+
59+
**涉及文件:**
60+
- `JSONDecoderImpl+Unwrap.swift`: `tranformer``transformer`(4 处)
61+
- `JSONDecoderImpl+SingleValueContainer.swift`: `trnas``trans`(2 处)
62+
63+
均为局部变量名修正,不影响任何逻辑。
64+
65+
### 1.4 CodingUserInfoKey rawValue 拼写错误
66+
67+
**涉及文件:**
68+
- `SmartJSONEncoder.swift`: `"Stamrt.useMappedKeys"``"Smart.useMappedKeys"`
69+
- `SmartJSONDecoder.swift`: `"Stamrt.parsingMark"``"Smart.parsingMark"`
70+
- `SmartJSONDecoder.swift`: `"Stamrt.logContext.header"``"Smart.logContext.header"`
71+
- `SmartJSONDecoder.swift`: `"Stamrt.logContext.footer"``"Smart.logContext.footer"`
72+
73+
**安全性确认:** 这些 rawValue 仅在运行时通过 `CodingUserInfoKey` 静态属性在 encoder/decoder 内部传递,所有访问点均通过静态属性而非硬编码字符串。不存在外部持久化依赖。
74+
75+
---
76+
77+
## 二、线程安全修复
78+
79+
### 2.1 `SmartCodableOptions` 全局配置加锁
80+
81+
**文件:** `SmartCodableOptions.swift`
82+
83+
**问题:** `numberStrategy``ignoreNull``public static var`,无任何同步保护。如果一个线程在修改配置的同时,另一个线程正在解码并读取配置,会产生数据竞争。
84+
85+
**修复方案:** 引入 `NSLock`,将存储属性改为 private,通过 computed property 加锁访问。
86+
87+
**修复前:**
88+
```swift
89+
public struct SmartCodableOptions {
90+
public static var numberStrategy: NumberConversionStrategy = .strict
91+
public static var ignoreNull: Bool = true
92+
}
93+
```
94+
95+
**修复后:**
96+
```swift
97+
public struct SmartCodableOptions {
98+
private static let lock = NSLock()
99+
private static var _numberStrategy: NumberConversionStrategy = .strict
100+
private static var _ignoreNull: Bool = true
101+
102+
public static var numberStrategy: NumberConversionStrategy {
103+
get {
104+
lock.lock()
105+
defer { lock.unlock() }
106+
return _numberStrategy
107+
}
108+
set {
109+
lock.lock()
110+
defer { lock.unlock() }
111+
_numberStrategy = newValue
112+
}
113+
}
114+
// ignoreNull 同理
115+
}
116+
```
117+
118+
**API 兼容性:** 公共 API 完全不变。
119+
120+
### 2.2 `SmartSentinel.debugMode` 加锁
121+
122+
**文件:** `SmartSentinel.swift`
123+
124+
**问题:** `_mode``private static var``debugMode` 的 getter/setter 直接读写它,无同步保护。Swift 语言规范不保证 enum 赋值的原子性,存在 data race。
125+
126+
**修复方案:** 引入 `modeLock`(NSLock),保护 `_mode` 的读写。
127+
128+
**API 兼容性:** 公共 API 完全不变。
129+
130+
### 2.3 评估后决定不修复的问题
131+
132+
| 问题 | 结论 | 理由 |
133+
|------|------|------|
134+
| `@unchecked Sendable` 标记 | 不改 | Apple 的 `JSONEncoder`/`JSONDecoder` 自身也标记了 `@unchecked Sendable`,SmartCodable 跟随父类行为合理 |
135+
| `_iso8601Formatter` 全局共享 | 不改 | Apple 的 Foundation `JSONDecoder` 源码中采用了相同写法 |
136+
| `SmartSentinel.cache`(LogCache struct) | 不改 | 其唯一存储属性 `snapshotDict``SafeDictionary`(class 类型,自带 NSLock),struct copy 时拷贝的是同一个 class 引用,线程安全由 `SafeDictionary` 保证 |
137+
138+
---
139+
140+
## 三、代码重构评估
141+
142+
对工作计划中列出的 8 个重构点,逐一读取相关代码,基于事实逻辑判断是否值得修改。
143+
144+
### 3.1 实际修复
145+
146+
**SmartDate.swift 重复 `import Foundation`** — 删除重复的一行。
147+
148+
### 3.2 评估后决定不修复的问题
149+
150+
| 问题 | 结论 | 理由 |
151+
|------|------|------|
152+
| `didFinishMapping` 重复 | 不改 | 两个实现**不同**:UnkeyedContainer 版本多了 `guard T.self is SmartDecodable.Type` 性能优化 |
153+
| SmartAny 整数推断 10 种类型 | 不改 | 改循环需类型擦除 `[Any.Type]`,降低可读性和 debug 能力,收益有限 |
154+
| SmartCompact 未遵循 PropertyWrapperable | 不改 | 设计决策,SmartCompact 有独立的 Codable 实现,不需要该协议的回调机制 |
155+
| `preconditionFailure` / `fatalError` | 不改 |`Encoder` 协议签名约束(非 throws),无法改为 throw;Apple 的 JSONEncoder 也使用同样的 `preconditionFailure` |
156+
| 日志代码无条件编译保护 | 不改 | 开发者可能需要在 Release 环境临时开启日志排查问题,`#if DEBUG` 会阻止这一场景 |
157+
| LogCache 无容量上限 | 不改 | `debugMode` 默认 `.none`,每次解析后自动清理,实际不会长期积累 |
158+
| Encoder/Decoder snake_case 重复 | 不改 | 确认是**反向转换**(camelCase→snake_case vs snake_case→camelCase),算法不同 |
159+
160+
---
161+
162+
## 修改文件汇总
163+
164+
| 文件 | 改动 |
165+
|------|------|
166+
| `JSONDecoderImpl+UnkeyedContainer.swift` | 修复 currentIndex 双重递增;修复 Bool.self 误传 |
167+
| `JSONDecoderImpl+Unwrap.swift` | 修正 `tranformer``transformer`(4 处) |
168+
| `JSONDecoderImpl+SingleValueContainer.swift` | 修正 `trnas``trans`(2 处) |
169+
| `SmartJSONDecoder.swift` | 修正 `Stamrt``Smart`(3 处) |
170+
| `SmartJSONEncoder.swift` | 修正 `Stamrt``Smart`(1 处) |
171+
| `SmartCodableOptions.swift` | 添加 NSLock 保护 `numberStrategy``ignoreNull` |
172+
| `SmartSentinel.swift` | 添加 modeLock 保护 `debugMode` |
173+
| `SmartDate.swift` | 删除重复的 `import Foundation` |

0 commit comments

Comments
 (0)