เดิมทีฉันเป็นโปรแกรมเมอร์ Java ที่ตอนนี้ทำงานร่วมกับ Objective-C ฉันต้องการสร้างคลาสนามธรรม แต่ก็ไม่เป็นไปได้ใน Objective-C เป็นไปได้ไหม
ถ้าไม่ฉันจะเข้าใกล้คลาสนามธรรมได้อย่างไรใน Objective-C
เดิมทีฉันเป็นโปรแกรมเมอร์ Java ที่ตอนนี้ทำงานร่วมกับ Objective-C ฉันต้องการสร้างคลาสนามธรรม แต่ก็ไม่เป็นไปได้ใน Objective-C เป็นไปได้ไหม
ถ้าไม่ฉันจะเข้าใกล้คลาสนามธรรมได้อย่างไรใน Objective-C
คำตอบ:
โดยทั่วไปแล้วคลาส Objective-C เป็นนามธรรมโดยการประชุมเท่านั้น - ถ้าผู้เขียนเอกสารชั้นเป็นนามธรรมก็ไม่ได้ใช้มันโดยไม่ต้องคลาสย่อย อย่างไรก็ตามไม่มีการบังคับใช้เวลาคอมไพล์ที่ป้องกันการสร้างอินสแตนซ์ของคลาสนามธรรมอย่างไรก็ตาม ในความเป็นจริงไม่มีอะไรที่จะหยุดผู้ใช้จากการให้วิธีการเชิงนามธรรมผ่านหมวดหมู่ (เช่นที่รันไทม์) คุณสามารถบังคับให้ผู้ใช้อย่างน้อยแทนที่วิธีการบางอย่างโดยเพิ่มข้อยกเว้นในการใช้วิธีการเหล่านั้นในระดับนามธรรมของคุณ:
[NSException raise:NSInternalInconsistencyException
format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
หากวิธีการของคุณคืนค่ามันเป็นเรื่องง่ายกว่าที่จะใช้
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
userInfo:nil];
จากนั้นคุณไม่จำเป็นต้องเพิ่มคำสั่ง return จากเมธอด
หากคลาสนามธรรมเป็นอินเทอร์เฟซ (เช่นไม่มีวิธีการใช้งานที่เป็นรูปธรรม) การใช้โปรโตคอล Objective-C เป็นตัวเลือกที่เหมาะสมกว่า
@protocol
กำหนด แต่คุณไม่สามารถกำหนดวิธีการที่มี
+ (instancetype)exceptionForCallingAbstractMethod:(SEL)selector
นี้ใช้ได้ดีมาก
doesNotRecognizeSelector
ที่ว่านี้เป็นพฤติกรรมที่ต้องการมากไปกว่า
ไม่ไม่มีวิธีในการสร้างคลาสนามธรรมใน Objective-C
คุณสามารถเยาะเย้ยคลาสนามธรรม - โดยการเรียกเมธอด / selectors doesNotRecognizeSelector: และยกระดับข้อยกเว้นที่ทำให้คลาสใช้ไม่ได้
ตัวอย่างเช่น:
- (id)someMethod:(SomeObject*)blah
{
[self doesNotRecognizeSelector:_cmd];
return nil;
}
คุณสามารถทำสิ่งนี้เพื่อ init
NSObject
อ้างอิงแนะนำให้ใช้สิ่งนี้ซึ่งคุณไม่ต้องการที่จะสืบทอดวิธีการที่จะไม่บังคับใช้วิธีการเอาชนะ แม้ว่าสิ่งเหล่านี้อาจจะเหมือนกันบางที :)
เพียงแค่ riffing ตามคำตอบของ @Barry Wark ด้านบน (และอัปเดตสำหรับ iOS 4.3) และปล่อยให้สิ่งนี้สำหรับการอ้างอิงของฉันเอง:
#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
#define methodNotImplemented() mustOverride()
จากนั้นในวิธีการของคุณคุณสามารถใช้สิ่งนี้
- (void) someMethod {
mustOverride(); // or methodNotImplemented(), same thing
}
หมายเหตุ:ไม่แน่ใจว่าการทำให้แมโครดูเหมือนฟังก์ชั่น C เป็นความคิดที่ดีหรือไม่ แต่ฉันจะเก็บไว้จนกว่าจะเรียนจบ ฉันคิดว่ามันถูกต้องมากกว่าที่จะใช้NSInvalidArgumentException
(แทนที่จะNSInternalInconsistencyException
) เพราะนั่นคือสิ่งที่ระบบรันไทม์ส่งต่อเพื่อตอบสนองต่อการdoesNotRecognizeSelector
ถูกเรียก (ดูNSObject
เอกสาร)
universe.thing
[Universe universe].thing
สนุกใหญ่ประหยัดหลายพันตัวอักษรของรหัส ...
#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
.
__PRETTY_FUNCTION__
ทั่วทุกสถานที่ผ่านทาง DLog ( ... ) แมโครเป็นข้อเสนอแนะที่นี่: stackoverflow.com/a/969291/38557
#define setMustOverride() NSLog(@"%@ - method not implemented", NSStringFromClass([self class])); mustOverride()
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[BaseDynamicUIViewController localizeUI] must be overridden in a subclass/category'
ในกรณีที่ฉันเสนอให้คุณรับHomeViewController - method not implemented
HomeViewController สืบทอดมาจาก Base - จะให้ข้อมูลเพิ่มเติม
วิธีแก้ปัญหาที่ฉันพบคือ:
วิธีนี้คอมไพเลอร์จะแจ้งเตือนคุณสำหรับวิธีการใด ๆ ในโปรโตคอลที่ไม่ได้ใช้โดยคลาสลูกของคุณ
มันไม่รวบรัดเหมือนกับใน Java แต่คุณจะได้รับคำเตือนคอมไพเลอร์ที่ต้องการ
NSObject
) ถ้าคุณใส่โพรโทคอลและการประกาศคลาสพื้นฐานในไฟล์ส่วนหัวเดียวกันมันแทบจะแยกไม่ออกจากคลาสนามธรรม
จากรายชื่อผู้รับจดหมายกลุ่ม Omni :
Objective-C ไม่มีคอมไพเลอร์นามธรรมที่สร้างอย่าง Java ในขณะนี้
ดังนั้นสิ่งที่คุณทำคือการกำหนดคลาสนามธรรมเป็นคลาสปกติอื่น ๆ และใช้วิธีการไม่สมบูรณ์สำหรับวิธีนามธรรมที่ว่างเปล่าหรือรายงานที่ไม่สนับสนุนตัวเลือก ตัวอย่างเช่น...
- (id)someMethod:(SomeObject*)blah
{
[self doesNotRecognizeSelector:_cmd];
return nil;
}
ฉันยังทำต่อไปนี้เพื่อป้องกันการเริ่มต้นของระดับนามธรรมผ่านเริ่มต้นเริ่มต้น
- (id)init
{
[self doesNotRecognizeSelector:_cmd];
[self release];
return nil;
}
แทนที่จะพยายามสร้างคลาสพื้นฐานที่เป็นนามธรรมให้พิจารณาใช้โปรโตคอล (คล้ายกับอินเตอร์เฟส Java) สิ่งนี้ช่วยให้คุณสามารถกำหนดชุดของวิธีการจากนั้นยอมรับวัตถุทั้งหมดที่สอดคล้องกับโปรโตคอลและใช้วิธีการ ตัวอย่างเช่นฉันสามารถกำหนดโปรโตคอลการดำเนินงานแล้วมีฟังก์ชั่นเช่นนี้:
- (void)performOperation:(id<Operation>)op
{
// do something with operation
}
โดยที่ op สามารถเป็นวัตถุใด ๆ ที่ใช้โปรโตคอลการดำเนินการ
หากคุณต้องการคลาสฐานนามธรรมของคุณเพื่อทำมากกว่าเพียงแค่กำหนดวิธีการคุณสามารถสร้างคลาส Objective-C ปกติและป้องกันไม่ให้อินสแตนซ์ เพียงแทนที่ฟังก์ชั่น - (id) init และทำให้มันคืนค่าศูนย์หรือยืนยัน (เท็จ) มันไม่ได้เป็นโซลูชั่นที่สะอาดมาก แต่เนื่องจาก Objective-C เป็นแบบไดนามิกอย่างสมบูรณ์จึงไม่มีทางเทียบเท่าโดยตรงกับคลาสฐานนามธรรม
หัวข้อนี้เก่าและส่วนใหญ่ที่ฉันต้องการแบ่งปันอยู่ที่นี่แล้ว
อย่างไรก็ตามวิธีที่ฉันชอบไม่ได้กล่าวถึงและ AFAIK ไม่มีการสนับสนุนดั้งเดิมใน Clang ปัจจุบันดังนั้นที่นี่ฉันไป ...
อันดับแรกและสำคัญที่สุด (อย่างที่คนอื่น ๆ ได้ชี้ให้เห็นแล้ว) คลาสนามธรรมเป็นสิ่งที่แปลกมากใน Objective-C - เรามักจะใช้องค์ประกอบ (บางครั้งผ่านการมอบหมาย) แทน นี่อาจเป็นเหตุผลว่าทำไมคุณสมบัติดังกล่าวยังไม่มีอยู่ในภาษา / คอมไพเลอร์นอกเหนือจาก@dynamic
คุณสมบัติซึ่ง IIRC ถูกเพิ่มใน ObjC 2.0 พร้อมกับการแนะนำ CoreData แล้ว
แต่เนื่องจาก (หลังจากการประเมินสถานการณ์ของคุณอย่างระมัดระวัง!) คุณได้ข้อสรุปว่าการมอบหมาย (หรือองค์ประกอบโดยทั่วไป) ไม่เหมาะสมในการแก้ไขปัญหาของคุณนี่คือวิธีที่ฉันทำ:
[self doesNotRecognizeSelector:_cmd];
...__builtin_unreachable();
การปิดเสียงเตือนคุณจะได้รับวิธีการที่ไม่เป็นโมฆะโดยบอกให้คุณ“ ควบคุมการสิ้นสุดฟังก์ชั่นที่ไม่เป็นโมฆะโดยไม่มีการคืนค่า "-[NSObject doesNotRecognizeSelector:]
โดยใช้__attribute__((__noreturn__))
ในหมวดหมู่โดยไม่มีการใช้งานเพื่อที่จะไม่แทนที่การใช้งานดั้งเดิมของวิธีการนั้นและรวมส่วนหัวสำหรับหมวดหมู่นั้นใน PCH ของโครงการของคุณโดยส่วนตัวฉันชอบรุ่นมาโครที่ช่วยให้ฉันลดความร้อนได้มากที่สุด
นี่มันคือ:
// 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
อย่างที่คุณเห็นมาโครนำเสนอวิธีการเชิงนามธรรมอย่างเต็มรูปแบบซึ่งช่วยลดจำนวนแผ่นเหล็กที่จำเป็นให้เหลือน้อยที่สุด
ตัวเลือกที่ดียิ่งขึ้นคือการล็อบบี้ ทีมเสียงดังกราวเพื่อจัดทำแอตทริบิวต์คอมไพเลอร์สำหรับกรณีนี้ผ่านการร้องขอคุณสมบัติ (ดีกว่าเพราะจะทำให้สามารถวินิจฉัยเวลาคอมไพล์สำหรับสถานการณ์เหล่านั้นที่คุณคลาสย่อยเช่น NSIncrementalStore)
__builtin_unreachable()
อาจทำให้คนแปลกใจ แต่ก็ง่ายที่จะเข้าใจเช่นกัน)จุดสุดท้ายนั้นต้องการคำอธิบายบางอย่างฉันเดาว่า:
บางคน (ส่วนใหญ่?) ถอนคำยืนยันในการสร้างงาน (ผมไม่เห็นด้วยกับนิสัยนั้น แต่ที่เรื่องอื่น ... ) ล้มเหลวในการใช้วิธีการที่จำเป็น - แต่ - เป็นที่ไม่ดี , น่ากลัว , ผิดและพื้นสิ้นสุดของจักรวาลสำหรับโปรแกรมของคุณ โปรแกรมของคุณไม่สามารถทำงานได้อย่างถูกต้องในเรื่องนี้เพราะมันไม่ได้กำหนดและพฤติกรรมที่ไม่ได้กำหนดเป็นสิ่งที่เลวร้ายที่สุดที่เคย ดังนั้นความสามารถในการตัดการวินิจฉัยเหล่านั้นโดยไม่สร้างการวินิจฉัยใหม่จะไม่สามารถยอมรับได้อย่างสมบูรณ์
มันไม่ดีพอที่คุณจะไม่สามารถรวบรวมการวิเคราะห์เวลาที่เหมาะสมสำหรับข้อผิดพลาดของโปรแกรมเมอร์และต้องหันไปหาการค้นพบแบบรันไทม์สำหรับสิ่งเหล่านี้ แต่ถ้าคุณสามารถพล็อตมากกว่าในรุ่นที่วางจำหน่ายทำไมต้องลองชั้นนามธรรมใน ที่แรก?
__builtin_unreachable();
อัญมณีทำให้การทำงานนี้สมบูรณ์แบบ แมโครทำให้การจัดทำเอกสารด้วยตนเองและพฤติกรรมตรงกับสิ่งที่เกิดขึ้นหากคุณเรียกวิธีการที่ขาดหายไปบนวัตถุ
การใช้@property
และ@dynamic
สามารถใช้งานได้ หากคุณประกาศคุณสมบัติแบบไดนามิกและไม่ให้การใช้งานวิธีการจับคู่ทุกอย่างจะยังคงรวบรวมโดยไม่มีคำเตือนและคุณจะได้รับunrecognized selector
ข้อผิดพลาดในขณะทำงานหากคุณพยายามเข้าถึง นี่เป็นสิ่งเดียวกับการโทร[self doesNotRecognizeSelector:_cmd]
แต่พิมพ์น้อยกว่ามาก
ใน Xcode (โดยใช้เสียงดังกราว ฯลฯ ) ฉันชอบที่จะใช้__attribute__((unavailable(...)))
แท็กคลาสนามธรรมเพื่อให้คุณได้รับข้อผิดพลาด / คำเตือนถ้าคุณลองใช้
มันให้การป้องกันบางอย่างโดยไม่ตั้งใจโดยใช้วิธีการ
ใน@interface
แท็กชั้นฐานวิธีการ "นามธรรม":
- (void)myAbstractMethod:(id)param1 __attribute__((unavailable("You should always override this")));
ด้วยการเพิ่มหนึ่งขั้นตอนต่อไปนี้ฉันสร้างแมโคร:
#define UnavailableMacro(msg) __attribute__((unavailable(msg)))
นี่ช่วยให้คุณทำสิ่งนี้:
- (void)myAbstractMethod:(id)param1 UnavailableMacro(@"You should always override this");
อย่างที่ฉันพูดนี่ไม่ใช่การป้องกันคอมไพเลอร์จริง ๆ แต่มันก็ดีพอ ๆ กับที่คุณจะได้ใช้ภาษาที่ไม่สนับสนุนวิธีการเชิงนามธรรม
-init
วิธีการในคลาสย่อยของคุณ
คำตอบของคำถามกระจัดกระจายอยู่ในความคิดเห็นภายใต้คำตอบที่ได้รับแล้ว ดังนั้นฉันจึงสรุปและทำให้ง่ายขึ้นที่นี่
หากคุณต้องการสร้างคลาสนามธรรมโดยไม่มีการใช้งานให้ใช้ 'โปรโตคอล' คลาสที่สืบทอดโปรโตคอลจำเป็นต้องใช้เมธอดในโปรโตคอล
@protocol ProtocolName
// list of methods and properties
@end
หากคุณต้องการสร้างคลาสนามธรรมที่มีการนำไปใช้บางส่วนเช่น "รูปแบบวิธีการของแม่แบบ" นี่เป็นวิธีแก้ปัญหา Objective-C - รูปแบบวิธีการของเทมเพลต?
ทางเลือกอื่น
เพียงตรวจสอบชั้นเรียนในชั้นนามธรรมและยืนยันหรือยกเว้นสิ่งที่คุณจินตนาการ
@implementation Orange
- (instancetype)init
{
self = [super init];
NSAssert([self class] != [Orange class], @"This is an abstract class");
if (self) {
}
return self;
}
@end
สิ่งนี้จะลบความจำเป็นในการแทนที่ init
(ข้อเสนอแนะที่เกี่ยวข้องเพิ่มเติม)
ฉันต้องการมีวิธีให้โปรแกรมเมอร์รู้ว่า "อย่าโทรจากเด็ก" และแทนที่อย่างสมบูรณ์ (ในกรณีของฉันยังคงมีฟังก์ชั่นเริ่มต้นบางอย่างในนามของผู้ปกครองเมื่อไม่ขยาย):
typedef void override_void;
typedef id override_id;
@implementation myBaseClass
// some limited default behavior (undesired by subclasses)
- (override_void) doSomething;
- (override_id) makeSomeObject;
// some internally required default behavior
- (void) doesSomethingImportant;
@end
ข้อดีคือโปรแกรมเมอร์ที่จะเห็น "แทนที่" [super ..]
ในการประกาศและจะรู้ว่าพวกเขาไม่ควรจะเรียก
ได้รับมันน่าเกลียดที่จะต้องกำหนดประเภทผลตอบแทนเป็นรายบุคคลสำหรับสิ่งนี้ แต่มันทำหน้าที่เป็นคำใบ้ภาพที่ดีพอและคุณไม่สามารถใช้ส่วน "override_" ในนิยามคลาสย่อยได้อย่างง่ายดาย
แน่นอนว่าคลาสนั้นยังสามารถใช้งานเริ่มต้นได้เมื่อส่วนขยายนั้นเป็นตัวเลือก แต่เช่นเดียวกับคำตอบอื่น ๆ ให้ใช้ข้อยกเว้นรันไทม์เมื่อเหมาะสมเช่นคลาส abstract (เสมือน)
มันจะดีถ้าได้สร้างคำแนะนำในคอมไพเลอร์เช่นนี้แม้แต่คำแนะนำที่ดีที่สุดในการ pre / post call การใช้งานของ super แทนที่จะต้องขุดผ่านข้อคิดเห็น / เอกสารประกอบหรือ ... สมมติว่า
หากคุณคุ้นเคยกับการรวบรวมการละเมิด instantiation นามธรรมในภาษาอื่น ๆ แล้วพฤติกรรม Objective-C น่าผิดหวัง
ในฐานะที่เป็นภาษาผูกพันปลายเป็นที่ชัดเจนว่า Objective-C ไม่สามารถตัดสินใจแบบคงที่ว่าคลาสนั้นเป็นนามธรรมหรือไม่ (คุณอาจจะเพิ่มฟังก์ชั่นที่รันไทม์ ... ) แต่สำหรับกรณีการใช้งานทั่วไปสิ่งนี้ดูเหมือนจะเป็นข้อบกพร่อง ฉันต้องการคอมไพเลอร์การแบนออกป้องกัน instantiations คลาสนามธรรมแทนการโยนข้อผิดพลาดที่รันไทม์
นี่คือรูปแบบที่เราใช้เพื่อรับการตรวจสอบแบบคงที่นี้โดยใช้เทคนิคสองสามอย่างเพื่อซ่อน initializers:
//
// Base.h
#define UNAVAILABLE __attribute__((unavailable("Default initializer not available.")));
@protocol MyProtocol <NSObject>
-(void) dependentFunction;
@end
@interface Base : NSObject {
@protected
__weak id<MyProtocol> _protocolHelper; // Weak to prevent retain cycles!
}
- (instancetype) init UNAVAILABLE; // Prevent the user from calling this
- (void) doStuffUsingDependentFunction;
@end
//
// Base.m
#import "Base.h"
// We know that Base has a hidden initializer method.
// Declare it here for readability.
@interface Base (Private)
- (instancetype)initFromDerived;
@end
@implementation Base
- (instancetype)initFromDerived {
// It is unlikely that this becomes incorrect, but assert
// just in case.
NSAssert(![self isMemberOfClass:[Base class]],
@"To be called only from derived classes!");
self = [super init];
return self;
}
- (void) doStuffUsingDependentFunction {
[_protocolHelper dependentFunction]; // Use it
}
@end
//
// Derived.h
#import "Base.h"
@interface Derived : Base
-(instancetype) initDerived; // We cannot use init here :(
@end
//
// Derived.m
#import "Derived.h"
// We know that Base has a hidden initializer method.
// Declare it here.
@interface Base (Private)
- (instancetype) initFromDerived;
@end
// Privately inherit protocol
@interface Derived () <MyProtocol>
@end
@implementation Derived
-(instancetype) initDerived {
self= [super initFromDerived];
if (self) {
self->_protocolHelper= self;
}
return self;
}
// Implement the missing function
-(void)dependentFunction {
}
@end
อาจเป็นสถานการณ์ประเภทนี้ควรเกิดขึ้นในเวลาการพัฒนาเท่านั้นดังนั้นสิ่งนี้อาจใช้ได้:
- (id)myMethodWithVar:(id)var {
NSAssert(NO, @"You most override myMethodWithVar:");
return nil;
}
คุณสามารถใช้วิธีการที่เสนอโดย@Yar (ด้วยการดัดแปลงบางอย่าง):
#define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
#define setMustOverride() NSLog(@"%@ - method not implemented", NSStringFromClass([self class])); mustOverride()
ที่นี่คุณจะได้รับข้อความเช่น:
<Date> ProjectName[7921:1967092] <Class where method not implemented> - method not implemented
<Date> ProjectName[7921:1967092] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[<Base class (if inherited or same if not> <Method name>] must be overridden in a subclass/category'
หรือยืนยัน:
NSAssert(![self respondsToSelector:@selector(<MethodName>)], @"Not implemented");
ในกรณีนี้คุณจะได้รับ:
<Date> ProjectName[7926:1967491] *** Assertion failure in -[<Class Name> <Method name>], /Users/kirill/Documents/Projects/root/<ProjectName> Services/Classes/ViewControllers/YourClass:53
นอกจากนี้คุณสามารถใช้โปรโตคอลและโซลูชันอื่น ๆ - แต่นี่เป็นหนึ่งในวิธีที่ง่ายที่สุด
โกโก้ไม่ได้ให้อะไรที่เป็นนามธรรม เราสามารถสร้างบทคัดย่อระดับที่ได้รับการตรวจสอบเฉพาะที่รันไทม์และในเวลารวบรวมนี้ไม่ได้ตรวจสอบ
ฉันมักจะปิดการใช้งานวิธีการเริ่มต้นในชั้นเรียนที่ฉันต้องการนามธรรม:
- (instancetype)__unavailable init; // This is an abstract class.
สิ่งนี้จะสร้างข้อผิดพลาดในเวลารวบรวมเมื่อใดก็ตามที่คุณเรียกใช้ init บนคลาสนั้น ฉันใช้วิธีการเรียนเพื่อทุกอย่างอื่น
Objective-C ไม่มีวิธีการประกาศคลาสนามธรรมในตัว
การเปลี่ยนแปลงเล็ก ๆ น้อย ๆ สิ่งที่ @redfood ปัญหาโดยใช้ความคิดเห็น @ dotToString ของคุณจริงมีทางออกลูกบุญธรรมของ Instagram IGListKit
AbstractClass
ต้องเป็นอินพุตหรือเอาต์พุตด้วยวิธีการบางอย่างให้พิมพ์มันAbstractClass<Protocol>
แทนเนื่องจากAbstractClass
ไม่ได้ใช้Protocol
วิธีเดียวที่จะมีAbstractClass<Protocol>
อินสแตนซ์คือโดย subclassing เนื่องจากAbstractClass
ไม่สามารถใช้งานได้ทุกที่ในโครงการจึงกลายเป็นนามธรรม
แน่นอนว่าสิ่งนี้ไม่ได้ป้องกันนักพัฒนาที่ไม่ด้อยโอกาสจากการเพิ่มวิธีการใหม่ ๆ ที่อ้างถึงAbstractClass
ซึ่งจะทำให้การใช้อินสแตนซ์ของคลาสนามธรรม (ไม่ใช่อีกต่อไป)
ตัวอย่างโลกจริง: IGListKitมีชั้นฐานIGListSectionController
ที่ไม่ใช้โปรโตคอลแต่วิธีการที่ต้องใช้เช่นการเรียนว่าทุกจริงขอประเภทIGListSectionType
IGListSectionController<IGListSectionType>
ดังนั้นจึงไม่มีวิธีที่จะใช้ประเภทวัตถุIGListSectionController
สำหรับสิ่งที่มีประโยชน์ในกรอบของพวกเขา
อันที่จริง Objective-C ไม่มีคลาสนามธรรม แต่คุณสามารถใช้โปรโตคอลเพื่อให้ได้ผลเช่นเดียวกัน นี่คือตัวอย่าง:
#import <Foundation/Foundation.h>
@protocol CustomProtocol <NSObject>
@required
- (void)methodA;
@optional
- (void)methodB;
@end
#import <Foundation/Foundation.h>
#import "CustomProtocol.h"
@interface TestProtocol : NSObject <CustomProtocol>
@end
#import "TestProtocol.h"
@implementation TestProtocol
- (void)methodA
{
NSLog(@"methodA...");
}
- (void)methodB
{
NSLog(@"methodB...");
}
@end
ตัวอย่างง่ายๆของการสร้างคลาสนามธรรม
// Declare a protocol
@protocol AbcProtocol <NSObject>
-(void)fnOne;
-(void)fnTwo;
@optional
-(void)fnThree;
@end
// Abstract class
@interface AbstractAbc : NSObject<AbcProtocol>
@end
@implementation AbstractAbc
-(id)init{
self = [super init];
if (self) {
}
return self;
}
-(void)fnOne{
// Code
}
-(void)fnTwo{
// Code
}
@end
// Implementation class
@interface ImpAbc : AbstractAbc
@end
@implementation ImpAbc
-(id)init{
self = [super init];
if (self) {
}
return self;
}
// You may override it
-(void)fnOne{
// Code
}
// You may override it
-(void)fnTwo{
// Code
}
-(void)fnThree{
// Code
}
@end
คุณสร้างผู้รับมอบสิทธิ์ไม่ได้ใช่ไหม
ผู้รับมอบสิทธิ์เป็นเหมือนคลาสพื้นฐานที่เป็นนามธรรมในแง่ที่คุณพูดว่าฟังก์ชันใดที่จำเป็นต้องกำหนด แต่คุณไม่ได้นิยามฟังก์ชันเหล่านั้นจริงๆ
จากนั้นเมื่อใดก็ตามที่คุณใช้ตัวแทนของคุณ (เช่นคลาสนามธรรม) คุณจะได้รับคำเตือนจากคอมไพเลอร์เกี่ยวกับฟังก์ชั่นเสริมและฟังก์ชั่นบังคับที่คุณต้องกำหนดพฤติกรรม
ฟังดูเหมือนชั้นฐานที่เป็นนามธรรมสำหรับฉัน