Performance Optimization and Frame‑Rate Testing for iOS Mobile Client Dynamic Layout
The article outlines iQIYI’s iOS dynamic‑layout architecture, recommends CADisplayLink for frame‑rate checks and TimeProfiler for deep analysis, then details a two‑phase optimization workflow—eliminating main‑thread bottlenecks (e.g., string formatting, image loading, view hierarchy changes) and reducing off‑screen rendering and layer blending through layer tricks, caching, and asset adjustments—to achieve smoother UI on low‑end devices.
Before diving in, this article briefly introduces iQIYI's mobile client dynamic layout solution based on a Card‑driven rendering engine. The backend sends description data, and the client parses it to build and compose UI elements. Because UI attributes and sizes are unknown at compile time, UI elements must be created and measured at runtime, raising strict performance requirements.
Tool Selection
Frame‑rate measurement: CADisplayLink API.
Function‑execution timing: Xcode's built‑in TimeProfiler.
Rendering issues: Simulator Debug menu’s off‑screen and layer‑blending inspection tools.
Although TimeProfiler is powerful, it generates large data volumes (often >1 GB in minutes) and is not lightweight. For pure frame‑rate checks, CADisplayLink is recommended; use TimeProfiler only when deeper analysis is needed.
How to Optimize
The optimization process is divided into two phases: locating main‑thread bottlenecks and rendering‑level improvements.
Phase 1 – Main‑Thread Bottlenecks
High‑cost system APIs identified from profiling:
1. +(instancetype)stringWithFormat:(NSString *)format, …
Solution: replace with C functions such as asprintf or snprintf to build a char* , then create an NSString from it.
2. +(UIImage *)imageNamed:(NSString *)name
Solution: use smaller‑resolution images, and cache frequently used placeholders by storing the image pointer globally.
3. Text‑size calculations:
-(CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context;
- (CGSize)sizeThatFits:(CGSize)size;
Solution: fix a display area in UI design to avoid repeated calculations, or cache the results after the first computation (consider font, size, line spacing, etc.).
4. UIView hierarchy adjustments:
- (void)insertSubview:(UIView*)view belowSubview:(UIView *)siblingSubview;
- (void)insertSubview:(UIView*)view aboveSubview:(UIView *)siblingSubview;
- (void)removeFromSuperview;
-(void)bringSubviewToFront:(UIView *)view;
-(void)sendSubviewToBack:(UIView *)view;
Solution: fix view hierarchy at creation time, hide unused views, and reveal them when needed.
5. NSScanner for mixed alphanumeric strings:
Solution: for simple cases, use intValue or C function strtoul(const char *nptr, char **endptr, int base) with bit‑operations for color parsing.
Phase 2 – Rendering Optimizations
Key rendering factors affecting smoothness are off‑screen rendering and layer blending.
1. Off‑Screen Rendering
Off‑screen rendering creates a separate buffer, incurs two context switches, and can hurt frame rate. Common triggers include custom drawRect , views with cornerRadius + masksToBounds = YES , shadows, allowsGroupOpacity = YES , and shouldRasterize = YES .
Mitigation strategies for corner radius:
Use CALayer with masksToBounds = YES + cornerRadius (still off‑screen but reliable).
Set only cornerRadius without masks (no off‑screen, but may be overwritten).
Pre‑draw rounded images on a background thread and use them as textures.
Apply a mask image with transparent center.
Let the backend supply pre‑rounded images (best performance).
For shadows, prefer a static shadow image or use shadowPath when the layer size is fixed.
Disable allowsGroupOpacity if group opacity is unnecessary, and enable shouldRasterize only for complex, static view hierarchies.
2. Blending (layer pixel mixing)
Transparent layers cause the GPU to blend multiple pixels. Prefer opaque layers whenever possible.
3. Additional tips
Set clearsContextBeforeDrawing = NO when only a small region updates.
Enable drawsAsynchronously to move actual drawing to a background thread.
Align CGRect values to integer pixel boundaries to avoid sub‑pixel rendering and extra anti‑aliasing work.
Match image resolution to display size; avoid overly large assets.
When using SDWebImage, set SDWebImageAvoidAutoSetImage if you need to post‑process the image before display.
For reusable cells, compare the new model with the previous one before re‑layout.
Reduce overly complex view hierarchies; replace unnecessary UIViews with CALayer.
Tailor optimizations for low‑end devices (reduce animations, shadows, corner radius).
Avoid main‑thread lock contention.
Summary
As app code grows, smoothness becomes a multi‑factor problem involving memory, thread count, CPU frequency, and OS version. Many teams parallelize UI‑related calculations, introduce thread‑scheduling, and preload resources to maintain fluidity. This article provides a practical checklist for iOS developers to diagnose and improve performance in dynamic layout scenarios.
iQIYI Technical Product Team
The technical product team of iQIYI
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.