Chủ đề này là loại cũ, và hầu hết những gì tôi muốn chia sẻ đã ở đây.
Tuy nhiên, phương pháp yêu thích của tôi không được đề cập và AFAIK không có hỗ trợ riêng trong Clang hiện tại, vì vậy tôi đến đây
Đầu tiên và trên hết (như những người khác đã chỉ ra) các lớp trừu tượng là một thứ rất không phổ biến trong Objective-C - chúng ta thường sử dụng bố cục (đôi khi thông qua ủy quyền) thay vào đó. Đây có lẽ là lý do tại sao một tính năng như vậy không tồn tại trong ngôn ngữ / trình biên dịch - ngoài @dynamic
các thuộc tính, IIRC đã được thêm vào ObjC 2.0 kèm theo sự ra mắt của CoreData.
Nhưng cho rằng (sau khi đánh giá cẩn thận về tình huống của bạn!) Bạn đã đi đến kết luận rằng ủy nhiệm (hoặc thành phần nói chung) không phù hợp để giải quyết vấn đề của bạn, đây là cách tôi thực hiện:
- Thực hiện mọi phương thức trừu tượng trong lớp cơ sở.
- Hãy thực hiện điều đó
[self doesNotRecognizeSelector:_cmd];
...
- Sau đó, ông dùng
__builtin_unreachable();
để tắt tiếng cảnh báo bạn sẽ nhận được các phương thức không có giá trị, cho bạn biết quyền kiểm soát của bạn đã đạt đến cuối chức năng không có khoảng trống mà không cần trả lại.
- Kết hợp các bước 2. và 3. trong một macro hoặc chú thích
-[NSObject doesNotRecognizeSelector:]
bằng cách sử dụng __attribute__((__noreturn__))
trong một danh mục mà không thực hiện để không thay thế triển khai ban đầu của phương thức đó và bao gồm tiêu đề cho danh mục đó trong PCH của dự án của bạn.
Cá nhân tôi thích phiên bản macro hơn vì điều đó cho phép tôi giảm bản tóm tắt càng nhiều càng tốt.
Đây là:
// Definition:
#define D12_ABSTRACT_METHOD {\
[self doesNotRecognizeSelector:_cmd]; \
__builtin_unreachable(); \
}
// Usage (assuming we were Apple, implementing the abstract base class NSString):
@implementation NSString
#pragma mark - Abstract Primitives
- (unichar)characterAtIndex:(NSUInteger)index D12_ABSTRACT_METHOD
- (NSUInteger)length D12_ABSTRACT_METHOD
- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange D12_ABSTRACT_METHOD
#pragma mark - Concrete Methods
- (NSString *)substringWithRange:(NSRange)aRange
{
if (aRange.location + aRange.length >= [self length])
[NSException raise:NSInvalidArgumentException format:@"Range %@ exceeds the length of %@ (%lu)", NSStringFromRange(aRange), [super description], (unsigned long)[self length]];
unichar *buffer = (unichar *)malloc(aRange.length * sizeof(unichar));
[self getCharacters:buffer range:aRange];
return [[[NSString alloc] initWithCharactersNoCopy:buffer length:aRange.length freeWhenDone:YES] autorelease];
}
// and so forth…
@end
Như bạn có thể thấy, macro cung cấp việc thực hiện đầy đủ các phương thức trừu tượng, giảm số lượng nồi hơi cần thiết đến mức tối thiểu.
Một lựa chọn thậm chí tốt hơn sẽ là vận động nhóm Clang cung cấp thuộc tính trình biên dịch cho trường hợp này, thông qua các yêu cầu tính năng. (Tốt hơn, bởi vì điều này cũng sẽ cho phép chẩn đoán thời gian biên dịch cho các tình huống trong đó bạn phân lớp, ví dụ NSIncrementalStore.)
Tại sao tôi chọn phương pháp này
- Nó hoàn thành công việc một cách hiệu quả và có phần thuận tiện.
- Nó khá dễ hiểu. (Được rồi, điều đó
__builtin_unreachable()
có thể làm mọi người ngạc nhiên, nhưng nó cũng đủ dễ hiểu.)
- Nó không thể bị tước trong các bản dựng phát hành mà không tạo ra các cảnh báo hoặc lỗi biên dịch khác - không giống như cách tiếp cận dựa trên một trong các macro xác nhận.
Điểm cuối cùng cần một số lời giải thích, tôi đoán:
Một số (hầu hết?) Mọi người xác nhận dải trong các bản dựng phát hành. (Tôi không đồng ý với thói quen đó, nhưng đó là một câu chuyện khác ...) Không thực hiện một phương pháp cần thiết - tuy nhiên - là xấu , khủng khiếp , sai , và về cơ bản kết thúc của vũ trụ cho chương trình của bạn. Chương trình của bạn không thể hoạt động chính xác trong vấn đề này vì nó không được xác định và hành vi không xác định là điều tồi tệ nhất. Do đó, việc có thể loại bỏ các chẩn đoán đó mà không tạo ra các chẩn đoán mới sẽ là hoàn toàn không thể chấp nhận được.
Nó đủ tệ đến mức bạn không thể có được chẩn đoán thời gian biên dịch thích hợp cho các lỗi lập trình viên đó và phải dùng đến khám phá tại thời điểm đó, nhưng nếu bạn có thể khắc phục nó trong các bản dựng phát hành, tại sao lại thử có một lớp trừu tượng trong địa điểm đầu tiên?