iOS Best Practices
Performance Optimization
1. Choose the Right Processing Mode
Video Mode (FBProcessModeVideo)
- Suitable for real-time video streams and live streaming scenarios
- Better performance and faster processing speed
- Recommended for camera preview, video calls, and similar scenarios
Image Mode (FBProcessModeImage)
- Suitable for single image processing
- Higher quality and better effects
- Recommended for photo editing and image beautification scenarios
objc
// Real-time video processing
FBImageFrame *output = [engine processImage:input processMode:FBProcessModeVideo];
// High-quality image processing
FBImageFrame *output = [engine processImage:input processMode:FBProcessModeImage];2. Parameter Adjustment Recommendations
Beauty Parameter Adjustment Principles
- Start with smaller values to avoid over-beautification
- Recommend reducing parameter values in real-time scenarios for smooth performance
- Static images can have appropriately higher parameter values
- Adjust parameter ranges based on user groups and scenarios
Recommended Parameter Ranges
objc
// Basic beauty parameters (real-time scenarios)
[self.beautyEngine setBasicParam:FBBasicParam_Smoothing floatValue:0.2f]; // Smoothing
[self.beautyEngine setBasicParam:FBBasicParam_Whitening floatValue:0.1f]; // Whitening
[self.beautyEngine setBasicParam:FBBasicParam_Rosiness floatValue:0.1f]; // Rosiness
// Face reshaping parameters (real-time scenarios)
[self.beautyEngine setReshapeParam:FBReshapeParam_FaceThin floatValue:0.1f]; // Face thinning
[self.beautyEngine setReshapeParam:FBReshapeParam_EyeSize floatValue:0.1f]; // Eye enlargement3. Memory Optimization
Use ARC for Memory Management
objc
// Recommended: Use ARC automatic management
@property (nonatomic, strong) FBImageFrame *reusableFrame;
// Avoid: Manual memory management (unless necessary)
// FBImageFrame *frame = [[FBImageFrame alloc] init];
// [frame release];Release Resources Timely
objc
@interface BeautyProcessor : NSObject
@property (nonatomic, strong) FBImageFrame *reusableFrame; // Reusable object
@end
@implementation BeautyProcessor
- (FBImageFrame *)processImage:(NSData *)imageData {
// Reuse FBImageFrame object
if (self.reusableFrame == nil) {
self.reusableFrame = [FBImageFrame createWithRGBA:data width:width height:height stride:stride];
}
// Process image
FBImageFrame *output = [self.beautyEngine processImage:self.reusableFrame processMode:FBProcessModeVideo];
return output;
}
- (void)release {
self.reusableFrame = nil; // ARC will automatically release
}
@endArchitecture Design
1. Singleton Pattern for Engine Management
objc
@interface BeautyEngineManager : NSObject
@property (nonatomic, strong, readonly) FBBeautyEffectEngine *engine;
+ (instancetype)sharedManager;
- (void)releaseEngine;
@end
@implementation BeautyEngineManager
+ (instancetype)sharedManager {
static BeautyEngineManager *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[BeautyEngineManager alloc] init];
});
return instance;
}
- (instancetype)init {
self = [super init];
if (self) {
[self initEngine];
}
return self;
}
- (void)initEngine {
FBEngineConfig *config = [[FBEngineConfig alloc] init];
config.appId = @"your_app_id";
config.appKey = @"your_app_key";
_engine = [FBBeautyEffectEngine createEngineWithConfig:config];
}
- (void)releaseEngine {
_engine = nil;
}
@end2. Asynchronous Image Processing
objc
@interface AsyncBeautyProcessor : NSObject
@property (nonatomic, strong) dispatch_queue_t processingQueue;
@property (nonatomic, strong) FBBeautyEffectEngine *engine;
@end
@implementation AsyncBeautyProcessor
- (instancetype)init {
self = [super init];
if (self) {
self.processingQueue = dispatch_queue_create("com.facebetter.processing", DISPATCH_QUEUE_SERIAL);
self.engine = [BeautyEngineManager sharedManager].engine;
}
return self;
}
- (void)processImageAsync:(FBImageFrame *)input completion:(void(^)(FBImageFrame *result, NSError *error))completion {
dispatch_async(self.processingQueue, ^{
@try {
FBImageFrame *output = [self.engine processImage:input processMode:FBProcessModeVideo];
// Switch to main thread for callback
dispatch_async(dispatch_get_main_queue(), ^{
completion(output, nil);
});
} @catch (NSException *exception) {
NSError *error = [NSError errorWithDomain:@"FacebetterError"
code:-1
userInfo:@{NSLocalizedDescriptionKey: exception.reason}];
dispatch_async(dispatch_get_main_queue(), ^{
completion(nil, error);
});
}
});
}
@endError Handling
1. Comprehensive Error Handling Mechanism
objc
@interface RobustBeautyProcessor : NSObject
@property (nonatomic, strong) FBBeautyEffectEngine *engine;
@end
@implementation RobustBeautyProcessor
- (BOOL)processImage:(FBImageFrame *)input output:(FBImageFrame *)output {
// Parameter validation
if (self.engine == nil) {
NSLog(@"Engine not initialized");
return NO;
}
if (input == nil) {
NSLog(@"Invalid input image");
return NO;
}
@try {
// Process image
FBImageFrame *result = [self.engine processImage:input processMode:FBProcessModeVideo];
if (result == nil) {
NSLog(@"Failed to process image");
return NO;
}
// Copy result to output
[self copyImageFrame:result to:output];
return YES;
} @catch (NSException *exception) {
NSLog(@"Exception during processing: %@", exception.reason);
return NO;
}
}
- (void)copyImageFrame:(FBImageFrame *)src to:(FBImageFrame *)dst {
// Implement image copying logic
}
@end2. Retry Mechanism
objc
@interface RetryBeautyProcessor : NSObject
@property (nonatomic, strong) FBBeautyEffectEngine *engine;
@end
@implementation RetryBeautyProcessor
static const int MAX_RETRY_COUNT = 3;
static const NSTimeInterval RETRY_DELAY = 0.1;
- (FBImageFrame *)processImageWithRetry:(FBImageFrame *)input {
for (int i = 0; i < MAX_RETRY_COUNT; i++) {
@try {
FBImageFrame *result = [self.engine processImage:input processMode:FBProcessModeVideo];
if (result != nil) {
return result;
}
} @catch (NSException *exception) {
NSLog(@"Attempt %d failed: %@", i + 1, exception.reason);
}
if (i < MAX_RETRY_COUNT - 1) {
[NSThread sleepForTimeInterval:RETRY_DELAY];
}
}
NSLog(@"All retry attempts failed");
return nil;
}
@endLifecycle Management
1. ViewController Lifecycle Handling
objc
@interface BeautyViewController : UIViewController
@property (nonatomic, strong) BeautyEngineManager *beautyManager;
@end
@implementation BeautyViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialize beauty engine
self.beautyManager = [BeautyEngineManager sharedManager];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Resume beauty processing
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Pause beauty processing
}
- (void)dealloc {
// Release beauty engine (if no longer needed)
// [self.beautyManager releaseEngine];
}
@end2. Application Lifecycle Handling
objc
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Pause beauty processing when app enters background
[[BeautyEngineManager sharedManager] pauseProcessing];
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Resume beauty processing when app returns to foreground
[[BeautyEngineManager sharedManager] resumeProcessing];
}Performance Monitoring
1. Performance Metrics Monitoring
objc
@interface BeautyPerformanceMonitor : NSObject
@property (nonatomic, assign) NSTimeInterval startTime;
@property (nonatomic, assign) NSInteger frameCount;
@property (nonatomic, assign) NSTimeInterval totalProcessTime;
@end
@implementation BeautyPerformanceMonitor
- (void)startFrame {
self.startTime = [[NSDate date] timeIntervalSince1970];
}
- (void)endFrame {
NSTimeInterval processTime = [[NSDate date] timeIntervalSince1970] - self.startTime;
self.totalProcessTime += processTime;
self.frameCount++;
// Calculate average processing time
if (self.frameCount % 30 == 0) { // Calculate every 30 frames
NSTimeInterval avgTime = self.totalProcessTime / self.frameCount;
NSLog(@"Average process time: %.2fms", avgTime * 1000);
// Reset counters
self.totalProcessTime = 0;
self.frameCount = 0;
}
}
- (BOOL)isPerformanceGood {
return (self.totalProcessTime / MAX(self.frameCount, 1)) < 0.033; // 30fps
}
@end2. Memory Usage Monitoring
objc
@interface MemoryMonitor : NSObject
@end
@implementation MemoryMonitor
- (void)logMemoryUsage:(NSString *)tag {
struct mach_task_basic_info info;
mach_msg_type_number_t size = MACH_TASK_BASIC_INFO_COUNT;
kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
if (kerr == KERN_SUCCESS) {
NSLog(@"%@ - Used: %.2fMB, Resident: %.2fMB", tag,
info.resident_size / 1024.0 / 1024.0,
info.resident_size / 1024.0 / 1024.0);
}
}
- (BOOL)isMemoryLow {
struct mach_task_basic_info info;
mach_msg_type_number_t size = MACH_TASK_BASIC_INFO_COUNT;
kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
if (kerr == KERN_SUCCESS) {
// Check if memory usage is too high (adjust threshold based on actual situation)
return info.resident_size > 100 * 1024 * 1024; // 100MB
}
return NO;
}
@endConfiguration Management
1. Beauty Configuration Management
objc
@interface BeautyConfigManager : NSObject
@property (nonatomic, strong) NSUserDefaults *userDefaults;
@end
@implementation BeautyConfigManager
- (instancetype)init {
self = [super init];
if (self) {
self.userDefaults = [NSUserDefaults standardUserDefaults];
}
return self;
}
- (void)saveBeautyConfig:(BeautyConfig *)config {
NSDictionary *configDict = @{
@"smoothing": @(config.smoothing),
@"whitening": @(config.whitening),
@"face_thin": @(config.faceThin),
@"basic_enabled": @(config.basicEnabled),
@"reshape_enabled": @(config.reshapeEnabled)
};
[self.userDefaults setObject:configDict forKey:@"beauty_config"];
[self.userDefaults synchronize];
}
- (BeautyConfig *)loadBeautyConfig {
BeautyConfig *config = [[BeautyConfig alloc] init];
NSDictionary *configDict = [self.userDefaults objectForKey:@"beauty_config"];
if (configDict) {
config.smoothing = [configDict[@"smoothing"] floatValue];
config.whitening = [configDict[@"whitening"] floatValue];
config.faceThin = [configDict[@"face_thin"] floatValue];
config.basicEnabled = [configDict[@"basic_enabled"] boolValue];
config.reshapeEnabled = [configDict[@"reshape_enabled"] boolValue];
}
return config;
}
@end
@interface BeautyConfig : NSObject
@property (nonatomic, assign) float smoothing;
@property (nonatomic, assign) float whitening;
@property (nonatomic, assign) float faceThin;
@property (nonatomic, assign) BOOL basicEnabled;
@property (nonatomic, assign) BOOL reshapeEnabled;
@end
@implementation BeautyConfig
- (instancetype)init {
self = [super init];
if (self) {
self.smoothing = 0.3f;
self.whitening = 0.2f;
self.faceThin = 0.1f;
self.basicEnabled = YES;
self.reshapeEnabled = NO;
}
return self;
}
@endTesting Recommendations
1. Unit Testing
objc
@interface BeautyEffectTests : XCTestCase
@property (nonatomic, strong) FBBeautyEffectEngine *beautyEngine;
@end
@implementation BeautyEffectTests
- (void)setUp {
[super setUp];
FBEngineConfig *config = [[FBEngineConfig alloc] init];
config.appId = @"test_app_id";
config.appKey = @"test_app_key";
self.beautyEngine = [FBBeautyEffectEngine createEngineWithConfig:config];
}
- (void)testBeautyEngineInitialization {
XCTAssertNotNil(self.beautyEngine, @"Engine should be created");
}
- (void)testImageProcessing {
// Create test image
uint8_t *data = malloc(640 * 480 * 4);
FBImageFrame *input = [FBImageFrame createWithRGBA:data width:640 height:480 stride:640 * 4];
// Process image
FBImageFrame *output = [self.beautyEngine processImage:input processMode:FBProcessModeVideo];
XCTAssertNotNil(output, @"Output should not be null");
free(data);
}
- (void)tearDown {
self.beautyEngine = nil;
[super tearDown];
}
@end2. Performance Testing
objc
- (void)testPerformance {
[self measureBlock:^{
NSTimeInterval startTime = [[NSDate date] timeIntervalSince1970];
for (int i = 0; i < 100; i++) {
// Process test image
[self processTestImage];
}
NSTimeInterval endTime = [[NSDate date] timeIntervalSince1970];
NSTimeInterval totalTime = endTime - startTime;
NSTimeInterval avgTime = totalTime / 100;
XCTAssertLessThan(avgTime, 0.05, @"Average process time should be less than 50ms");
}];
}