Objective-CARC模式Skill Objective-CARCPatterns

Objective-C ARC 模式技能专注于自动引用计数在 Objective-C 编程中的应用,涵盖强引用、弱引用、循环引用避免、所有权限定符、Core Foundation 桥接技术等关键主题。适用于 iOS、macOS 等 Apple 平台开发,帮助开发者编写内存高效、无泄漏的代码,提升应用性能和稳定性。关键词:Objective-C, ARC, 自动引用计数, 内存管理, 强引用, 弱引用, 循环引用, Core Foundation, iOS 开发, 移动应用开发。

移动开发 0 次安装 0 次浏览 更新于 3/25/2026

name: Objective-C ARC 模式 user-invocable: false description: 在 Objective-C 中使用自动引用计数时使用,包括强引用/弱引用、循环引用、所有权限定符、与 Core Foundation 的桥接,以及无需手动 retain/release 的内存安全代码模式。 allowed-tools: []

Objective-C ARC 模式

引言

自动引用计数 (ARC) 是 Objective-C 的内存管理系统,它在编译时自动插入 retain 和 release 调用。ARC 消除了大多数手动内存管理,同时提供确定性的内存行为,并防止常见的内存错误,如使用后释放和双重释放。

与垃圾回收不同,当引用计数达到零时,ARC 提供立即释放,使其适用于资源受限的环境,如 iOS。理解 ARC 的所有权规则、限定符和模式对于编写内存安全的 Objective-C 代码和避免循环引用至关重要。

本技能涵盖强引用和弱引用、所有权限定符、循环引用、Core Foundation 桥接以及基于 ARC 的内存管理最佳实践。

强引用和弱引用

强引用维护对象的所有权并防止释放,而弱引用观察对象而不阻止释放。

// 强引用(默认)
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSArray *friends;
@property (nonatomic, strong) UIImage *photo;
@end

@implementation Person
@end

// 弱引用
@interface ViewController : UIViewController
@property (nonatomic, weak) id<ViewControllerDelegate> delegate;
@property (nonatomic, weak) IBOutlet UILabel *nameLabel;
@end

@implementation ViewController
@end

@protocol ViewControllerDelegate <NSObject>
- (void)viewControllerDidFinish:(ViewController *)controller;
@end

// 使用强引用和弱引用
void strongWeakExample(void) {
    Person *person = [[Person alloc] init];
    person.name = @"Alice"; // 对 NSString 的强引用

    __weak Person *weakPerson = person; // 弱引用
    NSLog(@"Weak person: %@", weakPerson.name);

    person = nil; // Person 被释放
    NSLog(@"After nil: %@", weakPerson); // weakPerson 现在为 nil
}

// 无主引用(unsafe_unretained)
@interface NodeOld : NSObject
@property (nonatomic, unsafe_unretained) NodeOld *parent;
@property (nonatomic, strong) NSArray<NodeOld *> *children;
@end

@implementation NodeOld
@end

// 弱引用 vs 无主引用
@interface CommentOld : NSObject
@property (nonatomic, strong) NSString *text;
@property (nonatomic, weak) PostOld *post; // 弱引用:可以为 nil
@end

@interface PostOld : NSObject
@property (nonatomic, strong) NSArray<CommentOld *> *comments;
@end

@implementation CommentOld
@end

@implementation PostOld
@end

// 块中的强捕获
void blockCaptureExample(void) {
    Person *person = [[Person alloc] init];
    person.name = @"Bob";

    // 强捕获
    void (^strongBlock)(void) = ^{
        NSLog(@"%@", person.name); // 强捕获 person
    };

    // 弱捕获以避免循环引用
    __weak Person *weakPerson = person;
    void (^weakBlock)(void) = ^{
        NSLog(@"%@", weakPerson.name); // 弱捕获
    };

    strongBlock();
    weakBlock();
}

强引用增加保留计数,而弱引用在对象释放时自动设置为 nil,防止悬空指针。

循环引用及其解决

循环引用发生在对象之间持有强引用时,阻止释放。解决循环引用需要使用弱引用或无主引用。

// 循环引用示例
@interface Parent : NSObject
@property (nonatomic, strong) NSArray<Child *> *children;
@end

@interface Child : NSObject
@property (nonatomic, weak) Parent *parent; // 弱引用以打破循环
@property (nonatomic, strong) NSString *name;
@end

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

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

void noCycleExample(void) {
    Parent *parent = [[Parent alloc] init];
    Child *child = [[Child alloc] init];
    child.name = @"Alice";
    child.parent = parent; // 弱引用

    parent.children = @[child]; // 强引用
    // 当 parent 超出作用域时,两者都被释放
}

// 无循环的委托模式
@protocol DataSourceDelegate <NSObject>
- (void)dataSourceDidUpdate:(id)source;
@end

@interface DataSource : NSObject
@property (nonatomic, weak) id<DataSourceDelegate> delegate;
- (void)fetchData;
@end

@implementation DataSource
- (void)fetchData {
    // 获取数据
    [self.delegate dataSourceDidUpdate:self];
}

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

// 块的循环引用
@interface NetworkManager : NSObject
@property (nonatomic, strong) NSString *baseURL;
- (void)fetchDataWithCompletion:(void (^)(NSData *data))completion;
@end

@implementation NetworkManager
- (void)fetchDataWithCompletion:(void (^)(NSData *))completion {
    // 模拟异步工作
    dispatch_async(dispatch_get_global_queue(
        DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData *data = [@"response" dataUsingEncoding:NSUTF8StringEncoding];
        dispatch_async(dispatch_get_main_queue(), ^{
            completion(data);
        });
    });
}

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

// 使用弱-强舞避免块循环
@interface ViewController2 : UIViewController
@property (nonatomic, strong) NetworkManager *networkManager;
@end

@implementation ViewController2
- (void)loadData {
    __weak typeof(self) weakSelf = self;
    [self.networkManager fetchDataWithCompletion:^(NSData *data) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) return;

        // 安全使用 strongSelf
        NSLog(@"Data loaded: %@", strongSelf.view);
    }];
}

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

// 观察者模式循环
@interface Observable : NSObject
@property (nonatomic, strong) NSMutableArray *observers;
- (void)addObserver:(id)observer;
- (void)removeObserver:(id)observer;
- (void)notifyObservers;
@end

@implementation Observable
- (instancetype)init {
    self = [super init];
    if (self) {
        _observers = [NSMutableArray array];
    }
    return self;
}

- (void)addObserver:(id)observer {
    // 使用 NSPointerArray 进行弱引用
    [self.observers addObject:[NSValue valueWithPointer:(__bridge void *)observer]];
}

- (void)removeObserver:(id)observer {
    [self.observers removeObject:[NSValue valueWithPointer:(__bridge void *)observer]];
}

- (void)notifyObservers {
    for (NSValue *value in self.observers) {
        id observer = (__bridge id)(void *)[value pointerValue];
        if (observer) {
            // 通知观察者
        }
    }
}
@end

始终对委托、父指针和观察者使用弱引用来打破循环引用。在块中使用弱-强舞以安全访问 self。

所有权限定符

ARC 提供所有权限定符,明确控制变量和属性的内存管理行为。

// 属性所有权限定符
@interface Container : NSObject

// Strong: 对象指针的默认值
@property (nonatomic, strong) id strongProperty;

// Weak: 不阻止释放
@property (nonatomic, weak) id weakProperty;

// Copy: 创建对象的副本
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSArray *items;

// Assign: 用于非对象类型
@property (nonatomic, assign) NSInteger count;
@property (nonatomic, assign) CGFloat value;

// Unsafe_unretained: 弱引用但不设为 nil
@property (nonatomic, unsafe_unretained) id unsafeProperty;

@end

@implementation Container
@end

// 变量限定符
void qualifierExamples(void) {
    // __strong: 默认限定符
    __strong NSString *strongString = @"Hello";

    // __weak: 弱引用
    __weak NSString *weakString = strongString;

    // __unsafe_unretained: 非管理的弱引用
    __unsafe_unretained NSString *unsafeString = strongString;

    // __autoreleasing: 用于输出参数
    NSError * __autoreleasing error;
}

// Copy 属性行为
@interface Message : NSObject
@property (nonatomic, copy) NSString *text;
@property (nonatomic, copy) NSArray *recipients;
@end

@implementation Message
@end

void copyPropertyExample(void) {
    Message *message = [[Message alloc] init];

    NSMutableString *mutableText = [NSMutableString stringWithString:@"Hello"];
    message.text = mutableText; // 创建副本

    [mutableText appendString:@" World"];
    NSLog(@"Message text: %@", message.text); // 仍然是 "Hello"
    NSLog(@"Mutable text: %@", mutableText); // "Hello World"
}

// Autoreleasing 参数
BOOL loadData(NSData **outData, NSError **outError) {
    if (outData) {
        *outData = [@"data" dataUsingEncoding:NSUTF8StringEncoding];
    }
    return YES;
}

void autoreleaseExample(void) {
    NSData *data;
    NSError *error;

    if (loadData(&data, &error)) {
        NSLog(@"Loaded: %@", data);
    } else {
        NSLog(@"Error: %@", error);
    }
}

// 属性属性组合
@interface ConfiguredObject : NSObject

// 只读强引用
@property (nonatomic, strong, readonly) NSString *identifier;

// 读写复制
@property (nonatomic, copy, readwrite) NSString *name;

// 弱可空
@property (nonatomic, weak, nullable) id<NSObject> delegate;

// 强非空
@property (nonatomic, strong, nonnull) NSArray *items;

@end

@implementation ConfiguredObject
@end

对于接受可变类型(如 NSMutableString 或 NSMutableArray)的属性,选择 copy 以防止意外突变。

Core Foundation 桥接

Core Foundation 对象需要显式内存管理,并与 ARC 管理的 Objective-C 对象桥接以正确工作。

// 使用 __bridge 进行免费桥接
void bridgingExample(void) {
    NSString *nsString = @"Hello";

    // 桥接到 CF 而不转移所有权
    CFStringRef cfString = (__bridge CFStringRef)nsString;
    // nsString 保留所有权

    // 桥接回 NS
    NSString *nsString2 = (__bridge NSString *)cfString;
}

// 将所有权转移到 CF
void bridgeRetainExample(void) {
    NSString *nsString = @"Hello";

    // 将所有权转移到 CF
    CFStringRef cfString = (__bridge_retained CFStringRef)nsString;
    // 必须手动 CFRelease

    // 使用 cfString
    CFIndex length = CFStringGetLength(cfString);
    NSLog(@"Length: %ld", (long)length);

    // 手动释放
    CFRelease(cfString);
}

// 从 CF 转移所有权
void bridgeTransferExample(void) {
    // 创建 CF 对象(拥有所有权)
    CFMutableStringRef cfString = CFStringCreateMutable(NULL, 0);
    CFStringAppend(cfString, CFSTR("Hello"));

    // 转移到 ARC
    NSMutableString *nsString = (__bridge_transfer NSMutableString *)cfString;
    // ARC 现在拥有,无需 CFRelease

    [nsString appendString:@" World"];
    NSLog(@"%@", nsString);
}

// 处理 CF 集合
void cfCollectionExample(void) {
    // 创建 CF 数组
    CFArrayRef cfArray = CFArrayCreate(
        NULL,
        (const void *[]){@"A", @"B", @"C"},
        3,
        &kCFTypeArrayCallBacks
    );

    // 桥接到 NSArray
    NSArray *nsArray = (__bridge_transfer NSArray *)cfArray;
    NSLog(@"Array: %@", nsArray);
}

// CF 属性列表
void cfPropertyListExample(void) {
    NSDictionary *dict = @{@"key": @"value"};

    // 转换为 CF
    CFPropertyListRef plist = (__bridge_retained CFPropertyListRef)dict;

    // 写入文件
    CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:@"/tmp/test.plist"];
    CFPropertyListWrite(plist, url, kCFPropertyListXMLFormat_v1_0, 0, NULL);

    CFRelease(plist);
}

// 处理 CF 回调
void myCFArrayApplierFunction(const void *value, void *context) {
    NSString *string = (__bridge NSString *)value;
    NSLog(@"Value: %@", string);
}

void cfCallbackExample(void) {
    CFArrayRef cfArray = (__bridge CFArrayRef)@[@"A", @"B", @"C"];

    CFArrayApplyFunction(
        cfArray,
        CFRangeMake(0, CFArrayGetCount(cfArray)),
        myCFArrayApplierFunction,
        NULL
    );
}

使用 __bridge 进行临时桥接,__bridge_retained 当转移到 CF 时,__bridge_transfer 当从 CF 转移到 ARC 时。

ARC 和 C 结构

当将 ARC 对象与 C 结构混合时,需要显式管理结构中的对象指针。

// 带有对象指针的结构体
typedef struct {
    __unsafe_unretained NSString *name;
    NSInteger age;
} PersonStruct;

void structExample(void) {
    PersonStruct person;
    person.name = @"Alice"; // 必须使用 __unsafe_unretained
    person.age = 30;

    NSLog(@"Person: %@, %ld", person.name, (long)person.age);
}

// 更好:使用 Objective-C 类替代
@interface PersonData : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end

@implementation PersonData
@end

// 带有手动内存管理的结构体
typedef struct PersonStructManual {
    CFStringRef name; // 使用 CF 类型进行保留所有权
    NSInteger age;
} PersonStructManual;

PersonStructManual createPerson(NSString *name, NSInteger age) {
    PersonStructManual person;
    person.name = CFBridgingRetain(name); // 手动保留
    person.age = age;
    return person;
}

void releasePerson(PersonStructManual person) {
    CFRelease(person.name); // 手动释放
}

void manualStructExample(void) {
    PersonStructManual person = createPerson(@"Bob", 25);
    // 使用 person
    releasePerson(person);
}

// 结构体中的对象数组
typedef struct {
    __unsafe_unretained NSArray *items;
    NSInteger count;
} ContainerStruct;

// 替代:使用指向对象的指针
typedef struct {
    void *items; // 需要时桥接到 NSArray
    NSInteger count;
} ContainerStructPointer;

void pointerStructExample(void) {
    NSArray *array = @[@1, @2, @3];

    ContainerStructPointer container;
    container.items = (__bridge void *)array;
    container.count = array.count;

    // 检索
    NSArray *retrieved = (__bridge NSArray *)container.items;
    NSLog(@"Items: %@", retrieved);
}

避免将 ARC 管理的对象存储在 C 结构体中。使用 Objective-C 类或带有手动管理的 CF 类型替代。

自动释放池

自动释放池管理便捷方法创建的临时对象,并防止循环中内存积累。

// 主函数中的隐式自动释放池
int main(int argc, char *argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil,
            NSStringFromClass([AppDelegate class]));
    }
}

// 循环中的显式自动释放池
void processLargeDataset(void) {
    NSArray *items = /* 大数组 */;

    for (id item in items) {
        @autoreleasepool {
            // 每次迭代释放临时对象
            NSString *processed = [item description];
            NSData *data = [processed dataUsingEncoding:NSUTF8StringEncoding];
            // data 和 processed 在迭代结束时释放
        }
    }
}

// 没有自动释放池(内存积累)
void inefficientLoop(void) {
    for (NSInteger i = 0; i < 1000000; i++) {
        NSString *string = [NSString stringWithFormat:@"Number %ld", (long)i];
        // string 直到外层池结束才释放
    }
}

// 有自动释放池(每次迭代释放内存)
void efficientLoop(void) {
    for (NSInteger i = 0; i < 1000000; i++) {
        @autoreleasepool {
            NSString *string = [NSString stringWithFormat:@"Number %ld", (long)i];
            // string 在每次迭代结束时释放
        }
    }
}

// 嵌套自动释放池
void nestedPools(void) {
    @autoreleasepool {
        NSString *outer = @"outer";

        @autoreleasepool {
            NSString *inner = [NSString stringWithFormat:@"%@ inner", outer];
            // inner 在内层池排空时释放
        }

        // outer 在外层池排空时释放
    }
}

// 后台线程池
void backgroundWork(void) {
    dispatch_async(dispatch_get_global_queue(
        DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        @autoreleasepool {
            // 后台工作
            NSString *result = [NSString stringWithFormat:@"Result"];
            NSLog(@"%@", result);
        }
    });
}

在创建许多临时对象的紧密循环中使用显式自动释放池,以防止自动释放池排空之间的内存增长。

最佳实践

  1. 对委托和父引用使用弱引用 以打破常见委托和层次模式中的循环引用

  2. 在块中应用弱-强舞 当捕获 self 时,以防止循环引用,同时确保执行期间的安全访问

  3. 对可变类型属性选择复制 如 NSString 和 NSArray,以防止调用者意外突变

  4. 显式注释可空性 使用 nullable/nonnull 以提高 API 清晰度和 Swift 互操作性

  5. 在创建临时对象的循环中使用自动释放池 以防止长运行迭代中内存积累

  6. 显式桥接 CF 类型 使用适当的限定符来管理 ARC 和手动引用计数之间的所有权转移

  7. 避免将对象存储在 C 结构体中 因为 ARC 无法管理它们;使用 Objective-C 类或 CF 类型替代

  8. 在弱引用后检查 nil 因为它们在被引用对象释放时可能随时变为 nil

  9. 使用 NSPointerArray 进行弱集合 当维护观察者或委托集合时,以防止循环引用

  10. 使用 Instruments 分析 以检测循环引用、内存泄漏和过多的自动释放对象创建

常见陷阱

  1. 使用强委托创建循环引用 导致内存泄漏;始终对委托属性使用弱引用

  2. 在块中强捕获 self 当块存储在属性中时创建循环引用

  3. 在弱-强舞中忘记强引用 允许 self 在块执行期间释放

  4. 对父指针使用强引用 创建双向强引用并阻止释放

  5. 对 NSString 属性不使用复制 允许调用者传递可变字符串并在之后修改它们

  6. 错误桥接 CF 类型 根据所有权转移方向导致过度释放或泄漏

  7. 将 ARC 对象存储在 C 结构体中 导致过早释放或崩溃,因为 ARC 无法跟踪它们

  8. 创建 NSTimer 而不失效 强保留目标并阻止释放直到失效

  9. 在紧密循环中缺少自动释放池 导致内存增长和潜在的内存压力崩溃

  10. 使用 unsafe_unretained 替代 weak 创建悬空指针,在释放后访问时崩溃

何时使用此技能

在编写 iOS、macOS、watchOS 或 tvOS 的 Objective-C 代码时使用 ARC 模式,以确保内存安全的应用程序,无需手动 retain/release 调用。

在实现委托、观察者或可能创建循环引用的回调时应用弱引用和弱-强舞。

在与使用手动引用计数的 Core Foundation、Core Graphics 或其他 C 基框架接口时,采用适当的桥接。

在处理大型数据集、导入数据或在循环中执行其他创建许多临时对象的操作时,利用自动释放池。

在设计公共 API 时,使用适当的所有权限定符,以清晰地向客户端传达内存管理期望。

资源