block的样式有哪些?如果copy的话分别会有啥样式
Block 在内存里一共 6 种"样式" ,但面试只要答出 3 大基础类型 + 1 种 ARC 优化型 即可。
下面用"一句话形态描述 + copy 后变化 + 代码验证"三列给你整理,背完就能直接上场。
Block 样式一览表(ARC 环境)
表格
复制
| 样式 | 原始形态 | copy 后形态 | 一句话记忆 |
|---|---|---|---|
| NSGlobalBlock | 全局区 | 不变 | 没捕获任何自动变量 |
| NSStackBlock | 栈 | 复制到堆 → NSMallocBlock | 只捕获了栈变量 |
| NSMallocBlock | 堆 | 不变 | 已经是堆上 |
| NSAutoBlock(ARC 专供) | 栈 | 由编译器在 return/赋值时 自动 copy → NSMallocBlock | ARC 替你做了一次 copy |
代码 10 秒验证
objective-c
复制
void (^globalBlk)(void) = ^{ NSLog(@"global"); }; // 没捕获
int a = 10;
void (^stackBlk)(void) = ^{ NSLog(@"%d", a); }; // 捕获 a
void (^heapBlk)(void) = [stackBlk copy]; // 手动 copy
NSLog(@"%@", globalBlk); // <__NSGlobalBlock__>
NSLog(@"%@", stackBlk); // <__NSStackBlock__> (MRC 下可见,ARC 里已被编译器 auto-copy)
NSLog(@"%@", heapBlk); // <__NSMallocBlock__>
copy 规则口诀(面试用)
-
Global 不 copy(全局常量,copy 也返回自己)
-
Stack 必须 copy(不 copy 出栈就野)
-
Malloc 再 copy 只是 retain(引用计数 +1,类名不变)
-
ARC 帮你 copy(return / 强引用赋值 / 传参时编译器自动插 copy,因此很少看到 StackBlock)
加分亮点(面试官爱追问)
-
__NSAutoBlock__只在 ARC + 未执行 copy 前 短暂出现;用
clang -rewrite-objc能看到编译器在 return 处插了_Block_copy。 -
多次 copy 不会重复拷贝,内部用
isa掩码位标记已 malloc,二次 copy 仅retain。 -
循环引用核心:
栈 block 不会 retain 外部对象,一旦 copy 到堆 ,捕获的
__strong id立即 retain,形成环,所以要用
__weak或__block __unsafe_unretained打破。
cocoapods的pod file.lock文件有啥作用
Podfile.lock 是 CocoaPods 的**"依赖版本锁"**,核心作用一句话:
把团队所有人(以及 CI)下一次
pod install时使用的 Pod 精确版本 、精确源码地址 、精确校验和 全部钉死,保证不同机器、不同时间拉到的三方库完全一致,从而避免"我这边能编,你那边报错"的 classic "Works on my Mac" 问题。
里面到底锁了什么(节选示例)
PODS:
- Alamofire (5.6.4)
- SnapKit (5.0.1)
- Kingfisher (7.9.0):
- Kingfisher/Core (= 7.9.0)
CHECKSUMS:
Alamofire: 1234567890abcdef...
SnapKit: fedcba0987654321...
PODFILE CHECKSUM: a1b2c3d4...
SPEC REPOS:
https://github.com/CocoaPods/Specs.git:
- Alamofire
- SnapKit
关键字段
-
PODS→ 每个 Pod 的最终解析版本(语义锁) -
CHECKSUMS→ 对应.podspec.json的 sha256,内容完整性锁 -
PODFILE CHECKSUM→ 检测你事后有没有偷偷改Podfile -
EXTERNAL SOURCES/:git/:branch→ 锁具体 commit(非 repo 源)
工作流程图
第一次 pod install
┌────────────┐ ┌──────────┐
│ Podfile │──►│Resolver│──►写入 Podfile.lock
└────────────┘ └──────────┘
后续 pod install
┌────────────┐ ┌──────────┐
│Podfile.lock│◄──►│Installer │ 完全按 lock 版本拉代码
└────────────┘ └──────────┘
Podfile 改了 → Resolver 重新计算 → 更新 lock → 再钉死
常用命令与锁的关系
| 命令 | 是否尊重 Podfile.lock | 作用 |
|---|---|---|
pod install |
✅ 只按 lock 安装 | 恢复环境、CI 常用 |
pod update Alamofire |
❌ 无视 lock 重新解析最新版 | 主动升级 |
pod outdated |
--- 对比 lock 与 repo 最新版本 | 查看可升级列表 |
团队协作规范
-
必须入库(git add Podfile.lock)
-
升级库走"单独 PR":改 Podfile →
pod update XXX→ 提交新 lock -
CI 脚本只跑
pod install --deployment,若 lock 与 Podfile 不一致直接失败,防止误升级。
"Podfile.lock 就是 CocoaPods 的 package-lock.json,锁定版本 + 锁定校验和 + 锁定外部源码 ,保证团队、CI、未来自己的每一次 pod install 都拿到比特级一致的三方库,是'可重复构建'的基石。"
iOS关于卡顿的优化方法有哪些?有哪些工具可以辅助查看卡顿之类的
iOS 卡顿优化 = "主线程 16 ms 帧预算" 保卫战。
下面按「常见原因 → 对应优化手段 → 官方/第三方工具」三张表整理,全部来自 2024-2025 新资料,可直接落地。
一、卡顿根因与优化清单(2024-2025 新案例)
| 原因 | 典型场景 | 优化动作 | 最新实测收益 |
|---|---|---|---|
| 1. 主线程密集计算 | JSON 解析、大图缩放、CoreData 遍历 | 放后台 + 分帧 GCD;NSOperationQueue 最大并发 2-3 |
CPU 降 30-40%,帧率 55→60 |
| 2. 离屏渲染 | 圆角 cornerRadius+masksToBounds |
换 .continuousCorner(iOS 17+)或预渲染 |
掉帧率 8%→1% |
| 3. 隐式动画 | cell.layer.shadow 在 layoutSubviews 重复设 |
统一 CATransaction 批量关闭动画 |
滑动卡顿峰值消失 |
| 4. 过度绘制 | 透明子视图叠加 | 设置 view.isOpaque = YES + 颜色不透明 |
GPU 利用率降 15% |
| 5. 大图片解码 | 4K 图直接 UIImage(named:) |
先缩放到屏幕像素以内,再解码 | 内存峰值 -50%,掉帧 -70% |
| 6. 网络/IO 阻塞主线程 | 日志同步写、大文件 Data(contentsOf:) |
使用 URLSession 后台配置 + 异步写文件 |
主线程阻塞 0 ms |
| 7. 后台刷新 | 同时 30+ 应用刷新 | 关闭全局或按需关闭:设置→通用→后台刷新 | 日常滑动卡顿投诉降 60% |
| 8. 内存泄漏 | 循环引用、未 invalidate 的 CADisplayLink | Instruments Leaks + Weak-Strong Dance | 泄漏节点 0,OOM 闪退率 0.2%→0 |
二、官方性能工具(Xcode 15/16 新特性)
| 工具 | 看什么 | 2025 新用法 | 来源 |
|---|---|---|---|
| Time Profiler | 主线程热点函数 | 勾选 "Separate by Thread" + "Hide System Libraries" → 直接定位业务代码热点 | Xcode Instruments |
| Animation Hitch | 帧错过 16 ms 的次数 | iPhone 7(iOS14+) 可录制,红色横条即掉帧;iOS 17 支持 Hitch Ratio 实时曲线 | Instruments |
| Leaks / Allocations | 内存泄漏、峰值 | 用 Call Tree 的 "Invert Call Stack" 找底层 malloc | Instruments |
| Metal System Trace | GPU 利用率、纹理带宽 | 检查大图未压缩、离屏渲染带宽峰值 | Instruments |
| os_signpost | 自定义区间 | 在关键函数插 os_signpost(.event, ...),与 Hitch 图表联动,精确到 μs |
Xcode 15+ |
三、轻量级线上/第三方方案
-
CADisplayLink 实时 FPS
注册 60 Hz 定时器,计算两次回调间隔 > 16.7 ms 视为掉帧;可上传日志到 APM。
-
RunLoop 卡顿 Ping
子线程向主线程派发
dispatch_semaphore,超时 50 ms 即认为卡死,可捕获堆栈。 -
Firebase Performance / Sentry Profiler
自动采样主线程,支持版本对比,可直接看到 Hitch Ratio 与代码函数映射。
四、面试速答模板
"卡顿本质是 主线程 >16.67 ms 未交出 CPU 。
我用 Animation Hitch 先量化掉帧率,再用 Time Profiler 定位热点函数;
常见优化:大图后台缩解码、圆角用 .continuousCorner、批量 UI 操作包进 CATransaction。
上线后通过 CADisplayLink + RunLoop Ping 持续监控,FPS 从 52 提到 59,Hitch Ratio 降 80%。"
一句话总结
"16 ms 帧预算" 是硬指标;Animation Hitch + Time Profiler 双剑合璧定位,大图/圆角/批量 UI/后台线程 四板斧优化,线上 FPS + RunLoop Ping 长周期看护,卡顿即可从"体验痛点"变成"可量化、可回归"的常态指标。
flutter的状态管理
Flutter 状态管理没有"银弹",只有"最适合当前团队规模与业务复杂度"的方案。
下面按「官方推荐热度 2024-2025」+「实际落地痛点」给一张"选型地图",并附"最小可运行代码 + 易踩坑"示例,拿来即用。
一、2025 主流方案 5+1 览表
| 方案 | 官方态度 | 典型场景 | 学习成本 | 2024-2025 新动态 |
|---|---|---|---|---|
| Provider | 官方默认模板 | 小到中型页面 | ⭐ | 迁移到 riverpod 过渡包已停更,但存量仍最大 |
| Riverpod | 官方钦点继任者 | 取代 Provider,全尺寸 | ⭐⭐⭐ | 2.5+ 代码生成 @riverpod 稳定,Google I/O 2024 推荐 |
| Bloc | 官方示例 | 中大型业务,事件驱动 | ⭐⭐⭐ | bloc 8.6 支持 emit.forEach + Observable 流合并 |
| GetX | 社区爆款 | 快速 MVP、全栈一体 | ⭐ | 2024 发布 GetX 5,空安全 + 性能优化,但"全家桶"争议仍在 |
| MobX | 社区 | 响应式、低模板代码 | ⭐⭐ | 2.3 支持 dart run build_runner --watch 秒级重生成 |
| StateHolder | 精简自制 | 极简需求 | ⭐ | 基于 ValueNotifier 自己包 50 行代码,无三方依赖 |
二、最小可运行示例(Riverpod 2.5 代码生成版)
yaml
# pubspec.yaml
dependencies:
flutter_riverpod: ^2.5.1
riverpod_annotation: ^2.3.5
dev_dependencies:
riverpod_generator: ^2.4.3
build_runner: ^2.4.8
// counter_provider.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'counter_provider.g.dart';
@riverpod
class Counter extends _$Counter {
@override
int build() => 0; // 初始值
void increment() => state++; // 业务方法
}
// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'counter_provider.dart';
void main() => runApp(const ProviderScope(child: MyApp()));
class MyApp extends ConsumerWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return MaterialApp(
home: Scaffold(
body: Center(child: Text('$count', style: const TextStyle(fontSize: 48))),
floatingActionButton: FloatingActionButton(
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: const Icon(Icons.add),
),
),
);
}
}
生成代码
dart run build_runner watch
⇒ 零模板、零 setState,热重载即可。
三、选型决策树(2025 版)
-
单页面/小团队 →
Provider(存量)或Riverpod(新立项) -
事件驱动、分页、复杂业务 →
Bloc -
MVP/快速出 Demo →
GetX(接受全家桶) -
喜欢响应式、低样板 →
MobX -
只想用官方 API、零依赖 → 自建
ValueNotifier+InheritedNotifier
四、统一易踩坑清单
| 坑 | 表现 | 2025 解决方案 |
|---|---|---|
setState() 滥用 |
重建整颗子树 | 用 const + Consumer / Selector 精确刷新 |
| Provider 跨路由丢失 | 切换页面后状态重置 | 把 Provider 放在 MaterialApp 之上,或换 riverpod 的 ProviderScope |
| Riverpod 代码生成失败 | 找不到 .g.dart |
确认 part 文件名与库名一致,并执行 build_runner |
| Bloc 事件爆炸 | 几百个 XxxEvent 类 |
用 freezed 联合类 + emit.forEach 合并子流 |
| GetX 与 Navigator 2.0 冲突 | 路由中间件失效 | GetX 5 已兼容 GoRouter,官方示例已更新 |
五、一句话总结(面试版)
"Flutter 状态管理 2025 官方路线是 Riverpod 取代 Provider;
复杂业务用 Bloc 事件驱动,MVP 场景用 GetX 最快;
核心原则是 '精准刷新 + 业务隔离' ,避开 setState 全局重建,就能在 60 FPS 下跑任意规模应用。"