block的样式有哪些?如果copy的话分别会有啥样式
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]; // 手动 copyNSLog(@"%@", 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
→ 提交新 lockCI 脚本只跑
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.1riverpod_annotation: ^2.3.5
dev_dependencies:riverpod_generator: ^2.4.3build_runner: ^2.4.8
// counter_provider.dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'counter_provider.g.dart';@riverpod
class Counter extends _$Counter {@overrideint 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});@overrideWidget 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 下跑任意规模应用。”