首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Objective-C 單例模式完美實現:線程安全、防複製、防釋放

     編輯:關於C語言

單例核心要求

  1. 全程序僅一個實例
  2. 提供全域存取點
  3. 線程安全初始化
  4. 防止 `alloc`/`init`、`copy`、`retain`/`release` 破壞單一性

標準實現 (ARC 相容)


// UserContext.h
#import 

@interface UserContext : NSObject 
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *email;

// 單例存取點
+ (instancetype)sharedInstance;
@end

// UserContext.m
#import "UserContext.h"

static UserContext *_sharedInstance = nil;
static dispatch_once_t _onceToken;

@implementation UserContext

+ (instancetype)sharedInstance {
    dispatch_once(&_onceToken, ^{
        _sharedInstance = [[self alloc] init];
    });
    return _sharedInstance;
}

// 防止 alloc/init 建立新實例
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedInstance = [super allocWithZone:zone];
    });
    return _sharedInstance;
}

// 防止複製產生新實例
- (id)copyWithZone:(struct _NSZone *)zone {
    return _sharedInstance;
}

// 非 ARC 環境下防止釋放 (ARC 下編譯器忽略)
- (instancetype)retain { return _sharedInstance; }
- (void)release {}
- (instancetype)autorelease { return _sharedInstance; }
- (NSUInteger)retainCount { return NSUIntegerMax; }

@end

關鍵技術解析

技術作用
`dispatch_once`GCD 保證初始化代碼僅執行一次,原子性、無鎖、高效能
`allocWithZone:`攔截 `[[UserContext alloc] init]` 直接返回共享實例
`copyWithZone:`實現 `NSCopying`,複製返回同一實例
`retain`/`release`/`autorelease`/`retainCount`非 ARC 時阻止參考計數變化,ARC 下編譯器優化移除
`instancetype`返回型別為接收者類別,支援子類別繼承單例模式

測試代碼


int main() {
    @autoreleasepool {
        UserContext *a = [UserContext sharedInstance];
        UserContext *b = [UserContext sharedInstance];
        UserContext *c = [[UserContext alloc] init];
        UserContext *d = [c copy];

        NSLog(@"a=%p b=%p c=%p d=%p", a, b, c, d); // 四個指標相同
        NSLog(@"name=%@", a.name); // 初始為 nil
    }
    return 0;
}

輸出結果

a=0x100100000 b=0x100100000 c=0x100100000 d=0x100100000
name=(null)

進階:支援子類別的單例基類


@interface SingletonBase : NSObject
+ (instancetype)sharedInstance;
@end

@implementation SingletonBase
+ (instancetype)sharedInstance {
    static id _instance = nil;
    static dispatch_once_t _token;
    dispatch_once(&_token, ^{
        _instance = [[self alloc] init]; // self 為子類別
    });
    return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t _token;
    dispatch_once(&_token, ^{
        // 僅首次 alloc 建立實例
    });
    return _instance ?: [super allocWithZone:zone];
}
@end

// 子類別自動獲得單例能力
@interface MyManager : SingletonBase
@property (nonatomic, strong) NSData *cache;
@end

常見誤區

  • 只在 `sharedInstance` 用 `dispatch_once`,未覆寫 `allocWithZone:` → `[[MyClass alloc] init]` 仍建立新物件
  • 非 ARC 專案未實作 `retain`/`release` 覆寫 → 釋放導致野指標
  • 單例持有大量資源未釋放 → 記憶體洩漏 (單例生命週期 = 進程生命週期,通常可接受)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved