Tip

Apple 用户隐私授权弹窗文案大全

iOS 14.5之后,所有 App 必须使用 AppTrackingTransparency 框架来征得用户许可,才能对其进行跟踪或访问其设备的广告标识符。除非您征得用户对启用跟踪权限的许可,否则设备的广告标识符值将全部为零,您也将不允许跟踪用户。

因为权限弹窗只在 .notDetermined 时展示一下,为了吸引用户选择授权,弹窗内的文案就显得格外重要。
https://www.attprompts.com/ 收集了很多的 App 在用户隐私授权弹窗的文案,供大家参考。

文案提示

And More…

除了在弹窗文案上绞尽脑汁,也可以尝试在展示授权弹窗之前展示自定义的信息。参考 Apple Human Interface Guidelines 的 Displaying Custom Messaging Before the Alert 小节。

And More…

Getting Ready for App Tracking Transparency 作者介绍了如何将自己的 App 与 AppTrackingTransparency 兼容。

Review

极致首帧播放方案 - 零首帧解决方案

这篇文章针对短视频播放环节的各个阶段,介绍了对应的优化方案。

视频点击播放流程

视频点击播放流程

阶段及优化方法

  • 获取播放地址:
    • 播放地址随feed下发。
  • 网络建连:
    • DNS 预解析,预连接、连接复用,https TLS 1.3 false start, session 复用 0RTT。
  • 音视频首包
    • 减少 probe、moov 位置。
  • 音视频解码
    • 解码器异步初始化、解码器复用
  • 起播水位
    • 并不是首帧解码完成直接播放,而是起播之前缓存一定的数据来减少卡顿。
  • 预加载
    • 需要通过建立模型来确定是否开启预加载。需要实际考虑预加载的时机,以及其对带宽抢占的影响。
  • 预渲染
    • 提前将视频的首帧渲染好而不播放。
  • 长视频场景优化
    • mp4 的 moov 在不同网络条件下对播放体验的影响会有很大差别。
    • 对于弱网用户,建议使用 fmp4 的视频格式,fmp4 将视频拆成若干个片段,索引则存在各自的 side box,减小了起播所需要的数据。
  • 带历史进度的起播
    • 通常做法 seek 到历史进度前面最近一个关键帧,然后把视频帧塞进解码器,做丢帧处理,直到 pts 到指定的历史进度。这种方案在最坏场景下需要额外下载 20 Mb 的数据。文章建议直接在关键帧位置起播,避免额外数据下载,缩短首帧耗时。

Algorithm

N 皇后

描述:

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q''.' 分别代表了皇后和空位。

解法

  1. 回溯算法 2. 通过valid函数优化选择数目
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
53
54
55
56
57
58
/*
* @lc app=leetcode.cn id=51 lang=cpp
*
* [51] N 皇后
*/

// @lc code=start
class Solution {
public:
vector<vector<string>> results;

vector<vector<string>> solveNQueens(int n) {
vector<string> board(n, string(n, '.'));
backTracking(board, 0);
return results;
}

void backTracking(vector<string> &board, int row) {
// 终止条件
if (row == board.size()) {
results.push_back(board);
return;
}
int currentCol = board[row].size();

for (int col = 0; col < currentCol; col++)
{
// 排除不合法选项
if (!isValidChoice(board, row, col)) continue;
// in
board[row][col] = 'Q';
backTracking(board, row + 1);
// out
board[row][col] = '.';
}
}

bool isValidChoice(vector<string> &board, int row, int col) {
int rows = board.size();
// check conflict in same row.
for (int i = 0; i < row; i++)
{
if (board[i][col] == 'Q') return false;
}
// check conflict in +45 angle.
for (int i = row - 1, j = col + 1; i >= 0 && j < rows; i--, j++)
{
if (board[i][j] == 'Q') return false;
}
// check conflict in -45 angle.
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--)
{
if (board[i][j] == 'Q') return false;
}
return true;
}
};
// @lc code=end