在 iOS/macOS 網絡開發中,AsyncSocket(GCDAsyncSocket 的前身)廣泛用於異步 TCP 通訊。其讀寫方法均帶有一個 tag 參數,許多開發者對其用途存疑。本文從源碼角度徹底解析。
tag 參數僅用於在回調方法中匹配發起調用的請求,不會隨數據發送到網絡,也不會從網絡讀取。
// 發起讀請求 - (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag; // 回調 - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;
readDataWithTimeout:tag: 時,內部創建 AsyncReadPacket 對象,封裝 tag、超時時間等信息;theReadQueue 隊列;AsyncReadPacket,提取其 tag;onSocket:didReadData:withTag: 時將該 tag 透傳給委託。// 發起寫請求 - (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag; // 回調 - (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
寫入時同樣封裝 AsyncWritePacket 入隊 theWriteQueue,完成後回調攜帶原 tag。
#define TAG_WELCOME 10#define TAG_CAPABILITIES 11#define TAG_MSG 12switch(tag) 分發業務邏輯,避免在數據中解析協議頭,極大簡化代碼。In addition to this you've probably noticed the tag parameter. The tag you pass during the read/write operation is passed back to you via the delegate method once the read/write operation completes. It does not get sent over the socket or read from the socket. It is designed to help simplify the code in your delegate method.
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
switch (tag) {
case TAG_WELCOME:
// 忽略歡迎消息
break;
case TAG_CAPABILITIES:
[self processCapabilities:data];
break;
case TAG_MSG:
[self processMessage:data];
break;
default:
break;
}
}
// 讀請求入隊
- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag {
AsyncReadPacket *packet = [[AsyncReadPacket alloc] init];
packet.tag = tag;
packet.timeout = timeout;
[theReadQueue addObject:packet];
// ... 設置 CFStream 回調 ...
}
// CFStream 回調中取出包並觸發委託
- (void)_onReadStreamEvent:(CFStreamEventType)event {
AsyncReadPacket *packet = [theReadQueue firstObject];
long tag = packet.tag;
[delegate onSocket:self didReadData:data withTag:tag];
}
#define 常量管理 tag,避免魔術數字;理解 tag 的本質是「用戶態請求標識」,而非「協議字段」,能幫助你設計更清晰、可維護的網絡層架構。