Creating an abstract class in Objective-C

前端 未结 21 2485
感情败类
感情败类 2020-11-22 15:44

I\'m originally a Java programmer who now works with Objective-C. I\'d like to create an abstract class, but that doesn\'t appear to be possible in Objective-C. Is this poss

相关标签:
21条回答
  • 2020-11-22 16:13

    You can use a method proposed by @Yar (with some modification):

    #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()
    

    Here you will get a message like:

    <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'
    

    Or assertion:

    NSAssert(![self respondsToSelector:@selector(<MethodName>)], @"Not implemented");
    

    In this case you will get:

    <Date> ProjectName[7926:1967491] *** Assertion failure in -[<Class Name> <Method name>], /Users/kirill/Documents/Projects/root/<ProjectName> Services/Classes/ViewControllers/YourClass:53
    

    Also you can use protocols and other solutions - but this is one of the simplest ones.

    0 讨论(0)
  • 2020-11-22 16:14

    If you are used to the compiler catching abstract instantiation violations in other languages, then the Objective-C behavior is disappointing.

    As a late binding language it is clear that Objective-C cannot make static decisions on whether a class truly is abstract or not (you might be adding functions at runtime...), but for typical use cases this seems like a shortcoming. I would prefer the compiler flat-out prevented instantiations of abstract classes instead of throwing an error at runtime.

    Here is a pattern we are using to get this type of static checking using a couple of techniques to hide 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
    
    0 讨论(0)
  • 2020-11-22 16:15

    Using @property and @dynamic could also work. If you declare a dynamic property and don't give a matching method implementation, everything will still compile without warnings, and you'll get an unrecognized selector error at runtime if you try to access it. This essentially the same thing as calling [self doesNotRecognizeSelector:_cmd], but with far less typing.

    0 讨论(0)
  • 2020-11-22 16:16

    The solution I came up with is:

    1. Create a protocol for everything you want in your "abstract" class
    2. Create a base class (or maybe call it abstract) that implements the protocol. For all the methods you want "abstract" implement them in the .m file, but not the .h file.
    3. Have your child class inherit from the base class AND implement the protocol.

    This way the compiler will give you a warning for any method in the protocol that isn't implemented by your child class.

    It's not as succinct as in Java, but you do get the desired compiler warning.

    0 讨论(0)
  • 2020-11-22 16:17

    Just riffing on @Barry Wark's answer above (and updating for iOS 4.3) and leaving this for my own reference:

    #define mustOverride() @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"%s must be overridden in a subclass/category", __PRETTY_FUNCTION__] userInfo:nil]
    #define methodNotImplemented() mustOverride()
    

    then in your methods you can use this

    - (void) someMethod {
         mustOverride(); // or methodNotImplemented(), same thing
    }
    



    Notes: Not sure if making a macro look like a C function is a good idea or not, but I'll keep it until schooled to the contrary. I think it's more correct to use NSInvalidArgumentException (rather than NSInternalInconsistencyException) since that's what the runtime system throws in response to doesNotRecognizeSelector being called (see NSObject docs).

    0 讨论(0)
  • 2020-11-22 16:17

    The answer to the question is scattered around in the comments under the already given answers. So, I am just summarising and simplifying here.

    Option1: Protocols

    If you want to create an abstract class with no implementation use 'Protocols'. The classes inheriting a protocol are obliged to implement the methods in the protocol.

    @protocol ProtocolName
    // list of methods and properties
    @end
    

    Option2: Template Method Pattern

    If you want to create an abstract class with partial implementation like "Template Method Pattern" then this is the solution. Objective-C - Template methods pattern?

    0 讨论(0)
提交回复
热议问题