Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to have "implementation-only" class members?

this is kind of a beginners question so please bear with me.

I've got a class that makes use of a third-party library (oniguruma, if that matters). I want library methods to be completely decorated by my own, so that I can switch the underlying implementation of my class anytime. Something like:

// MyClass.h

@interface MyClass : NSObject ...

- (int) doSomething;


// MyClass.m

#import "some_library.h"

@implementation MyClass

- (int) doSomething 
{
   //call library's specific stuff
}

So far, so good, but now I'm needing to use an instance variable in MyClass that has some library-defined type (a structure declared in "some_library.h"). Of course I can import the library right in the interface section:

//MyClass.h

#import "some_library.h"

@interface MyClass : NSObject {
    some_library_t blah;
}
- (int) doSomething;
@end

but this is exactly what i'm trying to avoid - make users of MyClass aware of its implementation details.

Can I somehow "hide" library-specific types from my class' interface? What is the standard practice?

like image 920
user187291 Avatar asked Mar 01 '10 12:03

user187291


2 Answers

The standard practice is to use opaque pointers to either the library types or a custom implementation structure (thus its also called Pimpl - pointer to implementation).

To do that you have to know that you can define pointers to incomplete types, i.e. types that you only declare to exist. E.g.:

struct FooImpl;

@interface Foo {
    struct FooImpl* impl; // using pointer is ok for incomplete types
}        
@end

You can then define the type in the implementation file:

struct FooImpl {
    // ... member definition
};

and allocate/initialize it e.g. in your -(id)init method.

FooImpl could also be SomeLibraryType if the library type was a struct - you'd then forward declare it in the same way and include the library header in the source file, which gives you the structs definition.

like image 142
Georg Fritzsche Avatar answered Oct 18 '22 19:10

Georg Fritzsche


gf's answer is dead on, but there is another way, too. Use an opaque class.

Foo.h:

@interface Foo : NSObject
{
    id internalGunk;
}
@end

Foo.m:

#import "Foo.h"

@interface PrivateStuff:NSObject
... declare ivars and/or properties and/or methods here ...
@end

@implementation PrivateStuff
... any custom implementation and/or @synthesizes here ...
@end

#define SELF_PRIVVY ((PrivateStuff *)internalGunk)
@implementation Foo
... implementation here ...
@end

If you don't like SELF_PRIVVY, then you can do something like this:

// in Foo's @implementation
- (PrivateStuff *) privateStuff { return internalGunk; }

The above pattern (all of it) has a couple of advantages. First, it is fully compatible with GC in that everything is declared as object references. Secondly, it is much easier to refactor the private stuff into a separate class, if needed. Finally, having that class around to hold the privates makes it easier to separate out any logic or persistence related to that private stuff from everything else; it'll make future refactoring easier.

Whether or not it is a better solution for your needs depends on your specific requirements.

like image 42
bbum Avatar answered Oct 18 '22 18:10

bbum