第八章:实战 —— Hello, Object!

8.1 一个完整的 OC 程序

让我们把之前学到的东西拼装起来。如果不依赖 Xcode 的模板,一个纯粹的 OC 程序长什么样?

Greeter.h

#import <Foundation/Foundation.h>

// 定义协议
@protocol GreeterDelegate <NSObject>
- (void)greeterDidFinishGreeting:(NSString *)message;
@end

@interface Greeter : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, weak) id<GreeterDelegate> delegate;

- (instancetype)initWithName:(NSString *)name;
- (void)greet;
@end

Greeter.m

#import "Greeter.h"

@implementation Greeter

- (instancetype)initWithName:(NSString *)name {
    self = [super init]; // 先初始化父类
    if (self) {
        _name = [name copy]; // 记住要 Copy 字符串
    }
    return self;
}

- (void)greet {
    NSString *msg = [NSString stringWithFormat:@"Hello, %@!", self.name];
    NSLog(@"%@", msg);
    
    // 检查代理是否响应方法,然后发送消息
    if ([self.delegate respondsToSelector:@selector(greeterDidFinishGreeting:)]) {
        [self.delegate greeterDidFinishGreeting:msg];
    }
}
@end

main.m

#import <Foundation/Foundation.h>
#import "Greeter.h"

// 稍微作弊一下,直接在 main 里面实现协议
@interface MainRunner : NSObject <GreeterDelegate>
@end

@implementation MainRunner
- (void)greeterDidFinishGreeting:(NSString *)message {
    NSLog(@"[Delegate Callback] Done: %@", message);
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool { // 自动释放池,内存的保护伞
        MainRunner *runner = [MainRunner new];
        
        Greeter *g = [[Greeter alloc] initWithName:@"Objective-C"];
        g.delegate = runner;
        
        [g greet];
    }
    return 0;
}

8.2 解读

  1. @autoreleasepool: 在 main 函数里,所有的 OC 对象通常都需要在这个池子里。当出了这个括号,临时的对象(autoreleased objects)就会被统一清理。

  2. [super init]: OC 的初始化模式。先让爸爸初始化,爸爸同意了(返回非 nil),儿子再初始化。

  3. respondsToSelector:: 这就是动态性的体现。我在运行时问你:“你会不会这招?”你会我才调,不会我就不调,防止崩。

  4. Delegate 模式: id<GreeterDelegate>。这里用 id 表示“随便什么对象”,只要它遵守 GreeterDelegate 协议。 这是 iOS 开发中最著名的设计模式。

8.3 Categories (分类)

OC 还有一个大杀器:Category。 你可以不通过继承,直接给现有的类(甚至是系统类 NSString)添加方法。

@interface NSString (Reverse)
- (NSString *)reversedString;
@end

这在 Swift 里叫 Extension。但 OC 的 Category 更加野蛮,甚至可以通过 Runtime 给分类添加属性(虽然有点绕)。

8.4 总结

这一部分我们了解了 OC 的骨架。 你可能会觉得有点繁琐。没错,OC 就是繁琐。 但这种繁琐中包含着一种确定性。所有的逻辑都赤裸裸地摆在桌面上,没有 Swift 那么多语法糖藏着掖着。 下一章,我们将进入深水区:Advanced & Runtime。

使用 Hugo 构建
主题 StackJimmy 设计