macOS 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. NSViewController Lifecycle Handling
objc
@interface BeautyViewController : NSViewController
@property (nonatomic, strong) BeautyEngineManager *beautyManager;
@end
@implementation BeautyViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialize beauty engine
self.beautyManager = [BeautyEngineManager sharedManager];
}
- (void)viewWillAppear {
[super viewWillAppear];
// Resume beauty processing
}
- (void)viewWillDisappear {
[super viewWillDisappear];
// Pause beauty processing
}
- (void)dealloc {
// Release beauty engine (if no longer needed)
// [self.beautyManager releaseEngine];
}
@end2. Application Lifecycle Handling
objc
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
// Initialize beauty engine when app launches
[[BeautyEngineManager sharedManager] initEngine];
}
- (void)applicationWillTerminate:(NSNotification *)notification {
// Release beauty engine when app terminates
[[BeautyEngineManager sharedManager] releaseEngine];
}
- (void)applicationDidResignActive:(NSNotification *)notification {
// Pause beauty processing when app loses focus
[[BeautyEngineManager sharedManager] pauseProcessing];
}
- (void)applicationDidBecomeActive:(NSNotification *)notification {
// Resume beauty processing when app gains focus
[[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) {
// macOS has more memory, threshold can be set higher
return info.resident_size > 500 * 1024 * 1024; // 500MB
}
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;
}
@endmacOS-Specific Optimizations
1. Multi-Display Support
objc
@interface MultiDisplayManager : NSObject
@property (nonatomic, strong) NSArray<NSScreen *> *screens;
@end
@implementation MultiDisplayManager
- (void)setupMultiDisplaySupport {
self.screens = [NSScreen screens];
for (NSScreen *screen in self.screens) {
CGFloat scaleFactor = screen.backingScaleFactor;
if (scaleFactor > 1.0) {
// Retina display optimization
NSLog(@"Retina display detected with scale factor: %.1f", scaleFactor);
// Adjust processing parameters based on scale factor
}
}
}
- (void)handleDisplayChange:(NSNotification *)notification {
// Reconfigure when display configuration changes
[self setupMultiDisplaySupport];
}
@end2. Window Management
objc
@interface WindowManager : NSObject
@property (nonatomic, weak) NSWindow *mainWindow;
@end
@implementation WindowManager
- (void)handleWindowStateChange:(NSNotification *)notification {
NSWindow *window = notification.object;
if (window.isMiniaturized) {
// Pause processing when window is minimized
[[BeautyEngineManager sharedManager] pauseProcessing];
NSLog(@"Window minimized, pausing beauty processing");
} else if (window.isVisible) {
// Resume processing when window is visible
[[BeautyEngineManager sharedManager] resumeProcessing];
NSLog(@"Window visible, resuming beauty processing");
}
}
- (void)handleWindowDidResize:(NSNotification *)notification {
NSWindow *window = notification.object;
NSSize newSize = window.frame.size;
// Adjust processing parameters when window size changes
NSLog(@"Window resized to: %.0fx%.0f", newSize.width, newSize.height);
// Adjust beauty parameters based on new size
}
@end3. System Performance Monitoring
objc
@interface SystemPerformanceMonitor : NSObject
@end
@implementation SystemPerformanceMonitor
- (void)monitorSystemPerformance {
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
NSUInteger processorCount = processInfo.processorCount;
NSUInteger activeProcessorCount = processInfo.activeProcessorCount;
float systemLoad = (float)activeProcessorCount / processorCount;
if (systemLoad < 0.3) {
// Low system load, can use high quality mode
NSLog(@"System load low (%.1f%%), using high quality mode", systemLoad * 100);
} else if (systemLoad < 0.7) {
// Medium system load, use medium quality mode
NSLog(@"System load medium (%.1f%%), using medium quality mode", systemLoad * 100);
} else {
// High system load, use low quality mode
NSLog(@"System load high (%.1f%%), using low quality mode", systemLoad * 100);
}
}
@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;
// macOS has better performance, threshold can be set lower
XCTAssertLessThan(avgTime, 0.03, @"Average process time should be less than 30ms");
}];
}Summary
Following these best practices can help you:
- Improve Performance: Through memory management, multi-threading, and image optimization
- Improve User Experience: Through progressive loading and smooth transitions
- Improve Code Quality: Through modular design and error handling
- Ensure Stability: Through comprehensive testing strategies
- macOS Optimization: Through multi-display support and window management
Remember to adjust these practices based on specific needs and continuously monitor application performance. The macOS platform has more memory and stronger processing capabilities compared to iOS, allowing support for higher quality beauty effects.