一、類別定義三部曲
1. 介面檔
// TestClass.h
#import
@interface TestClass : NSObject
// 公開屬性 (自動合成 _ivar + getter/setter)
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *tag; // copy 防御可變字串
// 公開方法
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
- (void)greet;
+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age; // 便利建構器
@end
2. 實作檔
// TestClass.m
#import "TestClass.h"
@implementation TestClass
// 指定初始化器
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
if (self = [super init]) {
_name = [name copy]; // 手動 copy (屬性已 copy 自動處理)
_age = age;
_tag = @"default";
}
return self;
}
// 便利建構器
+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age {
return [[self alloc] initWithName:name age:age]; // 用 self 支援子類別
}
- (void)greet {
NSLog(@"Hello, I'm %@, %ld years old. Tag: %@", _name, (long)_age, _tag);
}
@end
3. 使用範例
// main.m
#import
#import "TestClass.h"
int main(int argc, const char *argv[]) {
@autoreleasepool {
// 1. alloc/init
TestClass *p1 = [[TestClass alloc] initWithName:@"Alice" age:25];
[p1 greet];
// 2. 便利建構器
TestClass *p2 = [TestClass personWithName:@"Bob" age:30];
[p2 greet];
// 3. 屬性存取 (點語法 = 呼叫 getter/setter)
p1.age = 26;
p1.tag = @"VIP";
NSLog(@"Updated: %@, %ld, %@", p1.name, (long)p1.age, p1.tag);
// 4. 直接存取 ivar (不推薦,繞過 KVO/屬性觀察)
// p1->_age = 99; // 只在 init/dealloc/自定義 setter 內使用
}
return 0;
}
二、屬性修飾語完整對照表
| 屬性 | 關鍵字 | 效果 | 適用場景 |
| 原子性 | atomic (預設) | getter/setter 加鎖,執行緒安全 | 多執行緒共享、簡單型別 |
| nonatomic | 無鎖,高效能 | 絕大多數情況、UI 物件、單執行緒 |
| 記憶體語意 | strong (預設) | 保持強參考,ARC 管理 | 物件擁有關係 |
| weak | 不保持,物件釋放自動置 nil | 代理、父視圖、打破循環保留 |
| copy | setter 執行 copy,新物件 count=1 | NSString, NSArray, NSDictionary, Block |
| assign | 單純賦值,不影響計數 | 基本型別、CGRect、非 OC 物件、MRC delegate |
| unsafe_unretained | 像 weak 但不自動置 nil | 極少用,需確定生命週期 |
| 讀寫權限 | readwrite (預設) | 生成 getter + setter | 可讀可寫 |
| readonly | 僅生成 getter | 不可變屬性、計算屬性 |
| 自定義存取 | getter=xxx, setter=xxx | 改變方法名 | 布林屬性 getter=isEnabled |
| 空值重設 | null_resettable | set nil 時重設為預設值 | iOS 11+、配合 NS_DESIGNATED_INITIALIZER |
三、關鍵設計模式
指定初始化器模式
- 類別指定一個
initWith… 為指定初始化器,完整初始化所有 ivar。
- 其他
init 方法呼叫指定初始化器。
- 子類別覆寫指定初始化器時必須呼叫
[super designatedInit]。
- 標記
NS_DESIGNATED_INITIALIZER 讓編譯器檢查。
便利建構器模式
- 類別方法
+ typeWith…。
- 內部呼叫
[[self alloc] initDesignated…]。
- 回傳
instancetype。
- ARC 下無需
autorelease。
不可變/可變對應模式
// 不可變介面
@interface ImmutableData : NSObject
@property (readonly, copy) NSString *content;
- (instancetype)initWithContent:(NSString *)c;
@end
// 可變子類別
@interface MutableData : ImmutableData
@property (readwrite, copy) NSString *content; // 重新宣告為 readwrite
- (void)appendString:(NSString *)s;
@end
四、記憶體管理實戰檢查清單
- 所有物件屬性預設
strong (ARC) / retain (MRC)。
- 代理、Block 捕獲 self、父視圖 →
weak。
NSString/NSArray/NSDictionary/Block → copy。
- 基本型別、結構體、Core Foundation →
assign。
dealloc 僅釋放非 ARC 管理資源 (如 CFRelease、free、移除通知觀察者),不呼叫 [super dealloc] (ARC 禁止)。
- 單例:
static id _instance; + (id)shared { static dispatch_once_t once; dispatch_once(&once, ^{ _instance = [[self alloc] init]; }); return _instance; }
五、現代 Objective-C 最佳實踐 (LLVM/Clang)
- 實例變數自動合成
_propertyName,無需手動 @synthesize。
- 方法回傳型別用
instancetype 而非 id。
- 常量字串用
@"...",格式化 [NSString stringWithFormat:]。
- 區塊 (Block) 類型定義:
typedef void(^CompletionBlock)(BOOL success, NSError *error);。
- 泛型集合 (Xcode 7+):
NSArray *names; 編譯期型別檢查。
- 空值檢查:
if (obj) { ... } 而非 if (obj != nil)。