Beike iOS Cold‑Start Optimization Practices and Framework
This article systematically presents the cold‑start performance problems of the Beike iOS app, defines testing standards, explains the essence of optimization, lists common pitfalls, and details a comprehensive set of practical solutions ranging from lifecycle‑aware task scheduling and minimal‑set launchers to dynamic‑library lazy loading, compile‑time I/O elimination, static‑initializer handling, dead‑code removal, and monitoring standards.
The article is based on a 2021 GMTC Global Front‑End Conference talk titled "Beike iOS Cold‑Start Optimization Practice" and aims to provide a systematic view of cold‑start performance issues in the Beike real‑estate mobile app.
Cold‑Start Testing Standard : Tests should be performed after a full device reboot (or after killing the process and waiting 10 minutes), with airplane mode or mock network enabled, iCloud disabled, and at least 50 runs averaged. The completion point is defined as the moment the home‑page view controller's viewDidAppear fires, reflecting the user’s real perception.
Essence of Cold‑Start Optimization : Remove useless work, execute only what is required now, trade space for time (caching, compile‑time data embedding), and exploit multi‑core CPU by balancing serial and parallel tasks.
Common Pitfalls : Assuming that moving expensive work to a background thread always helps, using fixed delays, delaying work until after the home page renders, and over‑optimizing CPU usage without considering overall launch time.
Optimization Thought Process : The article divides the iOS launch into six MetricKit phases (System Interface, Runtime Init, UIKit Init, Application Init, Initial Frame Render, Extended) and then maps them to four Beike‑specific phases T1‑T4, each with concrete measurement points.
Feasibility Analysis : Each phase is examined for potential gains; the results are illustrated in a series of diagrams (Figures 6‑8).
Main Optimization Scheme :
Overall idea (Figure 9) – keep the launch minimal and defer non‑essential work.
Current status (Figure 10) – many startup items exist; a "minimal set" of ~5 essential tasks is identified.
Framework optimization – minimal set, lifecycle delay, launcher, task scheduling, thread management.
Framework – Minimal Set : Only crash monitoring, HTTP parameter configuration, window creation, tab‑bar creation, and home‑page creation remain. All other tasks are delayed or lazily loaded.
Lifecycle‑Delay : Tasks are classified as head, tail, parallel, or low‑priority and scheduled accordingly. Example classes include LJAppLauncher, CrashInitialization, LJAppLaunchManager, and LJAppLaunchTaskService.
Launcher : Two stages – unified management of the minimal set, then dynamic registration of delayed tasks after the home page appears.
Task Scheduling : Tasks are turned into a directed acyclic graph (DAG) to avoid locks and guarantee deterministic execution order.
Thread Management : Three GCD‑based queues – main (UI), background (parallel), and idle (low‑priority) – are created to control execution and improve CPU utilization.
Home‑Page Rendering Optimizations : Avoid Flutter engine initialization, preload images (or embed them as base64), trigger viewDidLoad early, use fine‑grained partial refreshes, and instantiate views only when needed.
Dynamic‑Library Lazy Loading : Use NSBundle loadAndReturnError or dlopen to load libraries on demand, and ensure symbols are retained with -undefined dynamic_lookup and appropriate strip settings.
Compile‑Time I/O Elimination : Generate static data (e.g., Plist contents) at compile time so that runtime reads become simple method calls, reducing I/O from several milliseconds to sub‑millisecond.
Static Initializer Governance (Figures 23‑24): Identify and defer C/C++ static initializers, __attribute__((constructor)) functions, global objects, and static constants. Example code snippets:
int test() { return 1; }
int x = test(); CGRect rect = CGRectZero; static __attribute__((constructor)) void ljshl_MessageCellIdentifiers(void) {
kLJSHLMessageCellIdentifiers = @[
@"LJSHLMessageBotCell",
@"LJSHLMessageSystemCell",
@"LJSHLMessageUserCell",
@"LJSHLMessageSelfCell",
@"LJSHLMessageVIPCell",
@"LJSHLMessageSystemWelcomeCell"
];
} class testGlobalVar {
public:
testGlobalVar() { std::cout << "testGlobalVar" << std::endl; }
};
testGlobalVar var;Dead‑Code Removal : Combine static analysis, 0‑PV scans, and runtime coverage checks to safely delete unused Objective‑C classes and large native modules.
Other Optimizations : Include miscellaneous tweaks (Figure 25) and address difficult points such as balancing high‑CPU usage for short bursts versus overall efficiency (Figure 26).
Standards and Monitoring : Define launch‑item governance, pre‑main controls, and thresholds for added latency (>5 ms). Use CI to scan symbols and enforce white‑list policies. Monitoring dashboards (Figure 28) track each phase and flag regressions.
Conclusion and Outlook : Summarize low‑cost high‑gain measures versus deep‑level optimizations, propose automated attribution of latency growth, and suggest intelligent, user‑profile‑driven launch task orchestration.
Finally, a recruitment notice invites iOS/Android/Flutter engineers to join the Beike mobile team (contact: [email protected]).
Beike Product & Technology
As Beike's official product and technology account, we are committed to building a platform for sharing Beike's product and technology insights, targeting internet/O2O developers and product professionals. We share high-quality original articles, tech salon events, and recruitment information weekly. Welcome to follow us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
