在 iOS / macOS 開發中,Delegate(代理)模式 是實現物件間「一對一」通訊的基石。它將「發生事件的物件(委託者)」與「處理事件的物件(代理者)」解耦:委託者只負責發送訊息,具體處理邏輯由遵循特定協議的代理者實現。幾乎所有 UIKit 元件(UITableView、UITextField、UIScrollView 等)都大量採用此模式。
@required 必須實作與 @optional 可選實作)。@property (nonatomic, weak) id<ProtocolName> delegate; 避免循環保留。<ProtocolName>,並實作對應方法。場景:Dog 每秒吠叫一次,透過代理通知 Person 目前吠叫次數。
// DogBarkProtocol.h
#import
@class Dog; // 前向宣告
@protocol DogBarkProtocol
@optional
- (void)dog:(Dog *)dog didBarkWithCount:(NSInteger)count;
@end
// Dog.h
#import
@protocol DogBarkProtocol;
@interface Dog : NSObject
@property (nonatomic, assign) NSInteger dogID;
@property (nonatomic, weak) id delegate;
- (instancetype)initWithID:(NSInteger)id;
@end
// Dog.m
#import "Dog.h"
#import "DogBarkProtocol.h"
@implementation Dog {
NSTimer *_timer;
NSInteger _barkCount;
}
- (instancetype)initWithID:(NSInteger)id {
self = [super init];
if (self) {
_dogID = id;
_barkCount = 0;
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(timerFired:)
userInfo:nil
repeats:YES];
}
return self;
}
- (void)timerFired:(NSTimer *)timer {
_barkCount++;
if ([self.delegate respondsToSelector:@selector(dog:didBarkWithCount:)]) {
[self.delegate dog:self didBarkWithCount:_barkCount];
}
}
- (void)dealloc {
[_timer invalidate];
_timer = nil;
}
@end
// Person.h
#import
@class Dog;
#import "DogBarkProtocol.h"
@interface Person : NSObject
@property (nonatomic, strong) Dog *dog;
@end
// Person.m
#import "Person.h"
#import "Dog.h"
@implementation Person
- (void)dog:(Dog *)dog didBarkWithCount:(NSInteger)count {
NSLog(@"主人收到通知:狗 %ld 第 %ld 次吠叫", (long)dog.dogID, (long)count);
}
@end
// main.m
#import
#import "Person.h"
#import "Dog.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *owner = [[Person alloc] init];
Dog *dog = [[Dog alloc] initWithID:1024];
dog.delegate = owner; // 關鍵:設定代理
owner.dog = dog; // 可選:主人持有狗的強引用
// 保持 RunLoop 運作以接收 Timer 事件
[[NSRunLoop currentRunLoop] run];
}
return 0;
}
weak;MRC 時代用 assign,並在 dealloc 中置 nil。@class / @protocol:解決循環引用導致的編譯錯誤。@optional 方法前務必檢查,防止未實作導致崩潰。[[NSRunLoop currentRunLoop] run],否則 Timer 不會觸發。若需「一對多」通知,請改用 NSNotificationCenter 或 Combine 框架;簡單單次回調場景可考慮 Block(見下一篇「Blocks 應用」),但 Delegate 仍是建立長期、結構化協作關係的最佳選擇。