MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Objective-C多平台开发(macOS与iOS)差异

2021-03-132.6k 阅读

一、用户界面相关差异

1. 框架基础

在 macOS 开发中,主要使用 AppKit 框架来构建用户界面。AppKit 提供了丰富的类和接口,用于创建窗口、菜单、视图等用户界面元素。例如,要创建一个简单的窗口应用程序,可以这样写:

#import <AppKit/AppKit.h>

@interface MyAppDelegate : NSObject <NSApplicationDelegate>
@end

@implementation MyAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    NSWindow *window = [[NSWindow alloc] initWithContentRect:NSMakeRect(100, 100, 400, 300)
                                                  styleMask:NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask
                                                    backing:NSBackingStoreBuffered
                                                      defer:NO];
    [window setTitle:@"My macOS App"];
    [window makeKeyAndOrderFront:nil];
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSApplication *app = [NSApplication sharedApplication];
        MyAppDelegate *delegate = [[MyAppDelegate alloc] init];
        [app setDelegate:delegate];
        return NSApplicationMain(argc, argv);
    }
}

在上述代码中,通过 NSWindow 类创建了一个窗口,并设置了窗口的样式、标题等属性。NSApplication 管理整个应用程序的生命周期,MyAppDelegate 实现了 NSApplicationDelegate 协议中的方法来处理应用程序启动完成的事件。

而在 iOS 开发中,使用的是 UIKit 框架。UIKit 为 iPhone、iPad 等设备提供了构建用户界面的基础。下面是一个简单的 iOS 应用程序创建视图控制器和视图的示例:

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 50)];
    label.text = @"My iOS App";
    [self.view addSubview:label];
}
@end

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

在这段代码中,UIViewController 是 iOS 应用程序中管理视图的核心类。viewDidLoad 方法在视图加载时被调用,通过 self.view 获取视图,并设置背景颜色。UILabel 用于创建一个文本标签,并添加到视图中。UIApplicationMain 启动 iOS 应用程序,并指定应用程序代理类。

2. 布局方式

在 macOS 中,传统的布局方式主要依赖于 NSViewframe 属性手动设置视图的位置和大小。同时,也可以使用 Auto Layout 来实现更灵活的布局。例如,使用 Auto Layout 约束两个按钮水平居中且间距相等:

NSButton *button1 = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 30)];
[button1 setTitle:@"Button 1"];
NSButton *button2 = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 30)];
[button2 setTitle:@"Button 2"];
[self.view addSubview:button1];
[self.view addSubview:button2];

[NSLayoutConstraint activateConstraints:@[
    [button1.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
    [button1.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:100],
    [button2.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
    [button2.topAnchor constraintEqualToAnchor:button1.bottomAnchor constant:20],
    [button1.widthAnchor constraintEqualToConstant:100],
    [button2.widthAnchor constraintEqualToConstant:100]
]];

在上述代码中,通过 NSLayoutConstraintactivateConstraints: 方法激活一系列约束,实现了按钮在视图中的居中布局以及间距设置。

在 iOS 中,Auto Layout 是主流的布局方式。iOS 开发者通过 NSLayoutConstraint 或者更便捷的 Visual Format Language(VFL)来进行布局。例如,使用 VFL 布局三个按钮在同一行且间距相等:

UIButton *button1 = [UIButton buttonWithType:UIButtonTypeSystem];
[button1 setTitle:@"Button 1" forState:UIControlStateNormal];
UIButton *button2 = [UIButton buttonWithType:UIButtonTypeSystem];
[button2 setTitle:@"Button 2" forState:UIControlStateNormal];
UIButton *button3 = [UIButton buttonWithType:UIButtonTypeSystem];
[button3 setTitle:@"Button 3" forState:UIControlStateNormal];
[self.view addSubview:button1];
[self.view addSubview:button2];
[self.view addSubview:button3];

NSDictionary *views = NSDictionaryOfVariableBindings(button1, button2, button3);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[button1]-[button2]-[button3]-|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[button1(30)]" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[button2(30)]" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[button3(30)]" options:0 metrics:nil views:views]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:button1 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:button2 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:button3 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];

在这段代码中,首先将按钮添加到视图中,然后通过 NSDictionaryOfVariableBindings 创建包含按钮的字典。接着使用 constraintsWithVisualFormat: 方法创建水平和垂直方向的布局约束,实现按钮在同一行且垂直居中,以及宽度和间距的设置。

3. 交互方式

macOS 应用程序支持鼠标和键盘交互。例如,监听鼠标点击事件可以通过子类化 NSView 并重写 mouseDown: 方法:

@interface MyView : NSView
@end

@implementation MyView
- (void)mouseDown:(NSEvent *)theEvent {
    NSLog(@"Mouse clicked at %@", NSStringFromPoint([theEvent locationInWindow]));
}
@end

在上述代码中,当鼠标在 MyView 上点击时,会在控制台输出点击的位置。

iOS 应用程序主要基于触摸交互。监听触摸事件可以在 UIViewController 中重写触摸相关的方法,如 touchesBegan:withEvent:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView:self.view];
    NSLog(@"Touched at %@", NSStringFromCGPoint(location));
}

在这段代码中,当手指触摸屏幕时,会获取触摸点在视图中的位置并输出到控制台。

二、系统功能调用差异

1. 设备传感器访问

在 macOS 上,虽然没有像 iOS 那样丰富的移动设备传感器,但也可以访问一些系统硬件相关信息。例如,获取电池状态:

#import <IOKit/pwr_mgt/IOPMLib.h>

NSDictionary *batteryInfo = [NSDictionary dictionaryWithDictionary:IOPSCopyPowerSourcesInfo()];
NSArray *powerSources = [NSArray arrayWithArray:IOPSCopyPowerSourcesList()];
for (id powerSource in powerSources) {
    NSDictionary *sourceInfo = [NSDictionary dictionaryWithDictionary:IOPSGetPowerSourceDescription(batteryInfo, (__bridge CFDictionaryRef)powerSource)];
    NSNumber *isCharging = sourceInfo[(id)kIOPSIsChargingKey];
    NSNumber *batteryLevel = sourceInfo[(id)kIOPSCurrentCapacityKey];
    NSLog(@"Is Charging: %@, Battery Level: %@", isCharging, batteryLevel);
}

在上述代码中,通过 IOKit 框架中的 IOPMLib 来获取电池的充电状态和电量信息。

在 iOS 中,访问设备传感器非常方便。例如,使用 Core Motion 框架获取设备的加速度信息:

#import <CoreMotion/CoreMotion.h>

CMMotionManager *motionManager = [[CMMotionManager alloc] init];
if (motionManager.isAccelerometerAvailable) {
    motionManager.accelerometerUpdateInterval = 0.2;
    [motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {
        if (!error) {
            CMAcceleration acceleration = accelerometerData.acceleration;
            NSLog(@"Acceleration: x = %.2f, y = %.2f, z = %.2f", acceleration.x, acceleration.y, acceleration.z);
        }
    }];
}

在这段代码中,首先检查设备的加速度计是否可用,然后设置更新间隔并开始获取加速度数据,在获取到数据后将加速度的 x、y、z 轴的值输出到控制台。

2. 通知机制

在 macOS 中,使用 NSUserNotificationCenter 来管理用户通知。例如,发送一个简单的通知:

#import <UserNotifications/UserNotifications.h>

UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
    if (granted) {
        UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
        content.title = @"My macOS Notification";
        content.body = @"This is a sample macOS notification.";
        content.sound = [UNNotificationSound defaultSound];

        UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5 repeats:NO];
        UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"myNotification" content:content trigger:trigger];

        [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
            if (!error) {
                NSLog(@"Notification scheduled successfully.");
            }
        }];
    }
}];

在上述代码中,首先请求用户授权通知,然后创建通知内容,包括标题、正文和声音。接着设置一个延迟 5 秒触发的通知触发器,并创建通知请求。最后将通知请求添加到通知中心。

在 iOS 中,通知机制更为复杂且功能丰富。同样使用 UserNotifications 框架来发送本地通知:

#import <UserNotifications/UserNotifications.h>

UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
    if (granted) {
        UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
        content.title = @"My iOS Notification";
        content.body = @"This is a sample iOS notification.";
        content.sound = [UNNotificationSound defaultSound];
        content.badge = @(1);

        UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5 repeats:NO];
        UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"myNotification" content:content trigger:trigger];

        [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
            if (!error) {
                NSLog(@"Notification scheduled successfully.");
            }
        }];
    }
}];

与 macOS 类似,但 iOS 通知可以设置应用程序图标上的徽章数字(通过 content.badge),并且在请求授权时可以选择更多的选项,如显示徽章。

3. 文件系统访问

在 macOS 中,文件系统访问相对直接。可以使用 NSFileManager 来进行文件和目录的操作。例如,创建一个新目录并在其中创建一个文件:

NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *newDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"NewDirectory"];
NSError *error;
[fileManager createDirectoryAtPath:newDirPath withIntermediateDirectories:YES attributes:nil error:&error];
if (!error) {
    NSString *newFilePath = [newDirPath stringByAppendingPathComponent:@"newFile.txt"];
    [@"This is a new file." writeToFile:newFilePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
    if (!error) {
        NSLog(@"File created successfully.");
    } else {
        NSLog(@"Error creating file: %@", error);
    }
} else {
    NSLog(@"Error creating directory: %@", error);
}

在上述代码中,首先获取用户文档目录路径并拼接新目录路径,使用 createDirectoryAtPath:withIntermediateDirectories:attributes:error: 方法创建目录。如果目录创建成功,再在目录中创建一个新文件并写入内容。

在 iOS 中,由于应用程序的沙盒机制,文件系统访问受到限制。应用程序只能访问自己沙盒内的目录。同样使用 NSFileManager 来操作文件:

NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *newDirPath = [documentsDir stringByAppendingPathComponent:@"NewDirectory"];
NSError *error;
[fileManager createDirectoryAtPath:newDirPath withIntermediateDirectories:YES attributes:nil error:&error];
if (!error) {
    NSString *newFilePath = [newDirPath stringByAppendingPathComponent:@"newFile.txt"];
    [@"This is a new file." writeToFile:newFilePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
    if (!error) {
        NSLog(@"File created successfully.");
    } else {
        NSLog(@"Error creating file: %@", error);
    }
} else {
    NSLog(@"Error creating directory: %@", error);
}

与 macOS 代码类似,但 iOS 应用程序的沙盒机制确保每个应用程序只能在自己的特定目录(如 NSDocumentDirectory)下进行文件操作,增强了系统的安全性和稳定性。

三、内存管理与性能优化差异

1. 内存管理机制

在 macOS 和 iOS 开发中,虽然都使用 ARC(自动引用计数)来管理内存,但在一些细节上存在差异。在 macOS 中,对于一些基于 NSObject 的类,当对象不再被引用时,ARC 会自动释放内存。例如:

@interface MyObject : NSObject
@end

@implementation MyObject
- (void)dealloc {
    NSLog(@"MyObject deallocated");
}
@end

// 使用 MyObject
MyObject *obj = [[MyObject alloc] init];
// 当 obj 超出作用域或被设置为 nil 时,ARC 会自动释放内存,调用 dealloc 方法

在上述代码中,当 obj 不再被引用时,ARC 会自动调用 dealloc 方法释放内存。

在 iOS 中,ARC 同样有效,但由于移动设备资源相对有限,对内存管理的要求更为严格。例如,在 iOS 应用程序中,如果视图控制器被弹出栈或者被释放,ARC 会确保相关的视图和对象也被正确释放。同时,iOS 还提供了一些工具,如 Instruments 中的 Memory Graph Debugger,用于更直观地查看对象的内存引用关系,帮助开发者优化内存使用。

2. 性能优化策略

在 macOS 开发中,由于桌面设备通常拥有更强大的硬件资源,性能优化可能更侧重于多线程处理和资源的高效利用。例如,使用 NSOperationQueue 进行异步任务处理:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    // 执行耗时任务 1
    NSLog(@"Task 1 started");
    [NSThread sleepForTimeInterval:2];
    NSLog(@"Task 1 finished");
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    // 执行耗时任务 2
    NSLog(@"Task 2 started");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"Task 2 finished");
}];
[queue addOperation:operation1];
[queue addOperation:operation2];

在上述代码中,通过 NSOperationQueue 创建一个队列,并添加两个异步任务,这两个任务会在后台线程中并行执行,提高应用程序的响应性。

在 iOS 开发中,除了多线程处理外,还需要特别关注内存使用和渲染性能。例如,在处理大量图片时,可以使用 NSCache 来缓存图片,减少内存的频繁分配和释放。同时,在视图渲染方面,尽量避免在主线程进行复杂的视图布局和绘制操作。例如:

NSCache *imageCache = [[NSCache alloc] init];
// 获取图片方法
- (UIImage *)getImageWithURL:(NSURL *)url {
    UIImage *image = [imageCache objectForKey:url];
    if (!image) {
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        image = [UIImage imageWithData:imageData];
        if (image) {
            [imageCache setObject:image forKey:url];
        }
    }
    return image;
}

在这段代码中,NSCache 用于缓存从 URL 加载的图片。当需要获取图片时,先从缓存中查找,如果不存在则从 URL 加载并添加到缓存中,这样可以有效减少内存压力,提高应用程序的性能。

四、应用程序分发与部署差异

1. 应用商店

macOS 应用程序可以通过 Mac App Store 进行分发,也可以通过开发者自己的网站等渠道进行分发。在提交应用到 Mac App Store 时,需要遵循苹果的审核指南,包括应用的功能完整性、安全性、隐私政策等方面。例如,应用不能包含恶意代码,必须提供明确的隐私政策说明等。

iOS 应用程序则主要通过 App Store 进行分发,iOS 应用的审核更为严格。除了基本的功能和安全要求外,还需要遵循一系列与移动设备特性相关的规定。例如,应用的界面设计要符合 iOS 人机交互指南,应用不能干扰系统的正常功能,如不能随意修改系统设置等。

2. 应用配置与打包

在 macOS 开发中,应用程序的配置文件主要是 Info.plist,其中包含了应用的基本信息,如应用名称、版本号、支持的系统版本等。在打包应用时,通常会生成一个 .app 包,该包可以直接在 macOS 系统上运行。例如,在 Xcode 中,可以通过选择 “Product” -> “Archive” 来创建应用的归档文件,然后可以选择将其提交到 Mac App Store 或者导出为 .app 包进行本地分发。

在 iOS 开发中,同样需要 Info.plist 来配置应用信息。但 iOS 应用的打包更为复杂,需要生成 .ipa 文件。在打包过程中,需要进行代码签名,使用开发者证书和配置文件来确保应用的合法性和安全性。在 Xcode 中,同样通过 “Product” -> “Archive” 创建归档文件,然后可以选择将其提交到 App Store 或者导出为 .ipa 文件,用于企业内部分发或者测试。

3. 兼容性与更新

在 macOS 开发中,需要考虑不同版本 macOS 系统的兼容性。虽然 macOS 的更新频率相对较低,但不同版本之间可能存在 API 变化或者系统行为的差异。开发者需要在开发过程中进行充分的测试,确保应用在目标版本的 macOS 系统上正常运行。对于应用更新,开发者可以通过 Mac App Store 推送更新,也可以在应用内提供手动检查更新的功能。

在 iOS 开发中,兼容性问题更为突出,因为 iOS 设备型号众多,屏幕尺寸、性能等方面存在差异。开发者需要在不同型号的设备上进行测试,确保应用的界面布局和功能正常。同时,iOS 应用的更新主要通过 App Store 进行推送,用户可以选择自动更新或者手动更新应用。开发者在发布更新时,需要注意不破坏原有功能,并且要考虑与旧版本 iOS 系统的兼容性。

五、开发工具与调试差异

1. Xcode 功能差异

在 macOS 开发中,Xcode 提供了丰富的工具来支持 AppKit 开发。例如,Interface Builder 用于可视化设计 macOS 应用的用户界面,开发者可以直接拖放 NSViewNSButton 等控件到窗口中,并设置其属性和布局。同时,Xcode 还提供了强大的调试功能,如断点调试、性能分析等。在调试 macOS 应用时,可以使用 Instruments 中的工具,如 Time Profiler 来分析应用的性能瓶颈,找出耗时较长的函数。

在 iOS 开发中,Xcode 同样是主要的开发工具,但针对 iOS 开发有一些特定的功能。例如,Xcode 的模拟器可以模拟不同型号的 iOS 设备,包括不同的屏幕尺寸和分辨率,方便开发者在没有实际设备的情况下进行应用测试。此外,对于 iOS 应用的调试,除了常规的断点调试外,还可以使用视图调试工具来查看视图的层次结构和布局问题,这对于解决复杂的界面显示问题非常有帮助。

2. 调试技巧与工具

在 macOS 开发中,除了 Xcode 自带的调试工具外,还可以使用一些第三方工具,如 Charles 进行网络调试。Charles 可以拦截和分析应用的网络请求,帮助开发者检查网络数据的正确性和性能。同时,在 macOS 应用中,可以通过日志输出(如 NSLog)来跟踪程序的执行流程和调试问题。

在 iOS 开发中,除了类似的日志输出和 Xcode 调试功能外,还可以利用一些特定的工具来优化应用性能。例如,Instruments 中的 Core Animation 工具可以分析视图的渲染性能,检测是否存在卡顿等问题。此外,对于 iOS 应用的崩溃调试,可以通过 Crashlytics 等第三方服务来收集和分析崩溃日志,找出应用崩溃的原因。

六、语言特性使用差异

1. 类别(Categories)

在 macOS 和 iOS 开发中都广泛使用类别来为现有类添加方法。例如,在 macOS 开发中,可以为 NSString 添加一个类别来获取字符串的长度(以字节为单位):

@interface NSString (ByteLength)
- (NSUInteger)byteLength;
@end

@implementation NSString (ByteLength)
- (NSUInteger)byteLength {
    return [self lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
}
@end

// 使用类别方法
NSString *str = @"Hello, macOS";
NSUInteger byteLength = [str byteLength];
NSLog(@"Byte length: %lu", (unsigned long)byteLength);

在 iOS 开发中,同样可以为 NSString 添加类似的类别方法:

@interface NSString (ByteLength)
- (NSUInteger)byteLength;
@end

@implementation NSString (ByteLength)
- (NSUInteger)byteLength {
    return [self lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
}
@end

// 使用类别方法
NSString *str = @"Hello, iOS";
NSUInteger byteLength = [str byteLength];
NSLog(@"Byte length: %lu", (unsigned long)byteLength);

虽然类别在两个平台上的使用方式基本相同,但在实际应用中,可能会因为平台相关的需求而添加不同功能的类别方法。

2. 协议(Protocols)

在 macOS 开发中,协议常用于定义对象之间的交互规范。例如,NSApplicationDelegate 协议定义了应用程序代理需要实现的方法,如 applicationDidFinishLaunching:。通过遵循该协议,开发者可以在应用程序启动完成时执行特定的逻辑。

@interface MyAppDelegate : NSObject <NSApplicationDelegate>
@end

@implementation MyAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    NSLog(@"App launched on macOS");
}
@end

在 iOS 开发中,协议同样广泛应用。例如,UITableViewDataSource 协议定义了 UITableView 数据源需要实现的方法,如 tableView:numberOfRowsInSection:tableView:cellForRowAtIndexPath:。通过遵循该协议,开发者可以为 UITableView 提供数据并创建单元格。

@interface ViewController : UIViewController <UITableViewDataSource>
@end

@implementation ViewController
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 5;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
    }
    cell.textLabel.text = [NSString stringWithFormat:@"Row %ld", (long)indexPath.row];
    return cell;
}
@end

虽然协议的概念在两个平台上一致,但具体的协议内容和应用场景因平台而异,与各自平台的框架和用户界面需求紧密相关。

3. 块(Blocks)

在 macOS 开发中,块常用于异步任务和回调处理。例如,使用 NSURLSession 进行网络请求时,可以使用块来处理请求的响应:

NSURL *url = [NSURL URLWithString:@"https://example.com"];
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (!error && data) {
        NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"Response: %@", responseString);
    } else {
        NSLog(@"Error: %@", error);
    }
}];
[task resume];

在 iOS 开发中,块同样是处理异步操作和回调的重要方式。例如,在使用 UIView 的动画方法时,可以使用块来定义动画的执行和完成回调:

[UIView animateWithDuration:0.3 animations:^{
    self.view.backgroundColor = [UIColor redColor];
} completion:^(BOOL finished) {
    if (finished) {
        NSLog(@"Animation completed on iOS");
    }
}];

虽然块在两个平台上的基本用法相同,但具体的应用场景会根据平台的特点有所不同。在 macOS 中更多用于系统服务调用的回调,而在 iOS 中除了网络请求等异步操作外,还大量应用于动画和用户界面交互的回调处理。