Defining a class that uses different classes depending on the platform - objective-c

I want to be able to have two classes that are responsible to respond to selectors differently depending if the platform is iOS or OSX.
However, I want to have code that uses only one class, and I want to avoid repeating #ifdefs.
Ideally I would want to have 3 classes:
UniversalClass
iOSSpecificClass
OSXSpecificClass
iOSSpecificClass and OSXSpecificClass both extend UniversalClass.
All calls would be done to UniversalClass, and that class is responsible to call the respective methods of iOSSpecificClass and OSXSpecificClass.
There are two solutions that I came up with:
#interface UniversalClass : NSObject
+ (void) universalMethod;
#end
#implementation UniversalClass
+(id)forwardingTargetForSelector:(SEL)aSelector {
#if TARGET_OS_IPHONE
return [iOSSpecificClass class];
#else
return [OSXSpecificClass class];
#endif
}
#end
The problem with this approach is that UniversalClass promises something in the .h that can or cannot deliver. The warnings also tell us that. Grr. Warnings.
The second approach would be like this:
#implementation UniversalClass
+ (Class)correctClass {
Class aClass = Nil;
#if TARGET_OS_IPHONE
aClass = [iOSSpecificClass class];
#else
aClass = [OSXSpecificClass class];
#endif
return aClass;
}
+ (void)universalMethod {
Class masterClass = [UniversalClass correctClass];
[masterClass universalMethod];
}
#end
The problem with this approach is that I have to perform changes for every method I add and I feel that I am kinda repeating myself without needing.
What are the edge cases I have to pay attention to in both solutions? Is there any better solution than those?

One option is to have a common header file and two different implementations for two targets (one for OSX and another for iOS) that both import and implement the header methods.
Something like this:

Another alternative is to examine if you really need two classes. One #interface and two #implementations (potentially in separate files) is a pattern that I've seen.
Something like (this from CodeRunner where I did my test):
#import <Foundation/Foundation.h>
// #define iPHONE 1
#interface MyClass : NSObject
- (NSString*) someString;
- (BOOL) aMethod: (NSString*) inString;
#end
// common implementations here
#interface MyClass (common)
- (NSString*) commonString;
#end
#implementation MyClass (common)
- (NSString*) commonString
{
return #"same";
}
#end
#ifdef iPHONE
// iPhone specific implementations
#implementation MyClass
- (BOOL) aMethod: (NSString*) inString
{
return [inString isEqualToString: #"iPhone Impl"];
}
- (NSString*) someString
{
return #"iPhone Impl";
}
#end
#else
#implementation MyClass
- (BOOL) aMethod: (NSString*) inString
{
return [inString isEqualToString: #"iPhone Impl"];
}
- (NSString*) someString
{
return #"OS X Impl";
}
#end
#endif
// test
int main(int argc, char *argv[]) {
#autoreleasepool {
MyClass * obj = [[MyClass alloc] init];
NSLog(#"is iPhone? %#", [obj aMethod: [obj someString]] ? #"YES" : #"NO");
NSLog( #"string: %#", [obj someString] );
}
}
You could obviously do this more elegantly by having two .m files and putting one implementation in each (iPhone in one, OS X in the other); or three if you are going to have common routines that are shared by both.
Anyway, just an alternative way to get the same / similar effect - single interface to differing functionality.

You could go with something like this:
#implementation UniversalClass
static Class class;
+ (void)load
{
class = [UniversalClass correctClass];
}
+ (Class)correctClass {
Class aClass = Nil;
#if TARGET_OS_IPHONE
aClass = [iOSSpecificClass class];
#else
aClass = [OSXSpecificClass class];
#endif
return aClass;
}
+ (void)universalMethod {
[class universalMethod];
}
This will keep the promise you made on the .h by implementing the corresponding method (no warnings) and get the right class only once.

How about just ignoring the warning for the specific case of your forwardingTargetForSelector: version? It's like saying “hey, I know what I'm doing!” :-)
Add something like these #pragma calls around your #implementation line:
...
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wincomplete-implementation"
#implementation UniversalClass
#pragma clang diagnostic pop
...
See this answer here on Stack Overflow.

The solution that you are proposing is the Class Cluster pattern, which is quite common in Cocoa (e.g. it is used in NSArray, NSValue, etc). Class clusters are classes that return a private subclass from their constructor instead of an instance of the class that was requested. Here is how you might implement that in this case:
MyClass.h
#interface MyClass : NSObject
- (void)someMethod;
#end
MyClass.m
#implementation MyClass
+ (id)alloc
{
if (self == [MyClass class])
{
#if TARGET_OS_IPHONE
return [MyClass_iOS alloc];
#else
return [MyClass_Mac alloc];
#endif
}
else
{
return [super alloc];
}
}
- (void)someMethod
{
//abstract, will be overridden
}
#end
MyClass_iOS and MyClass_Mac would be declared in separate files and privately imported in the McClass.m file.
This seems like a pretty elegant solution at first, but it's not really appropriate for this situation. Class clusters are great for swapping class implementation at runtime when you don't know which implementation you want at compile time (good examples would be supporting different iOS versions, or universal apps that behave differently on iPad and iPhone) but for Mac/iOS we know at compile time which code we need, so introducing a cluster of 3 separate classes is redundant.
This solution doesn't really offer any benefit over the ones suggested by https://stackoverflow.com/users/145108/dad or https://stackoverflow.com/users/3365314/miguel-ferreira because we still have to branch the import code:
#if TARGET_OS_IPHONE
#import "MyClass_iOS.h"
#else
#import "MyClass_Mac.h"
#endif
We could solve that by having a single header for both MyClass_iOS and MyClass_Mac (which was Miguel's solution) or by having both implementations in the same file (which was Dad's solution) but then we've just built a layer on top of one of the solutions you already rejected.
Personally, I would just use a single .m file with three clearly delineated sections:
#interface MyClass
#pragma mark -
#pragma mark Common code
- (void)someMethod1
{
}
#pragma mark -
#pragma mark iOS code
#if TARGET_OS_IPHONE
- (void)someMethod2
{
}
#pragma mark -
#pragma mark Mac code
#else
- (void)someMethod2
{
}
#endif
#end
This avoids creating unnecessary classes and gives you freedom to easily have shared methods or separate implementations for each platform without exposing any of that in the interface.
If the classes for the two platforms definitely won't have any code in common, I'd probably opt for Miguel's solution, which is very clean.
I don't accept the "user confusion" explanation. You'd basically have these three files:
MyClass.h
MyClass_iOS.m
MyClass_Mac.m
I think if someone is confused by what that means, they shouldn't be working on your code base ;-)
You could also combine this with the class cluster approach if you did want to inherit shared code between the two platforms, in which case your MyClass.m file would contain both the shared implementation and the private interface:
#interface MyClass_Private : MyClass
- (void)somePlatformSpecificMethod;
#end
#implementation MyClass
+ (id)alloc
{
if (self == [MyClass class])
{
return [MyClass_Private alloc];
}
else
{
return [super alloc];
}
}
- (void)someSharedMethod
{
//concrete implementation
}
#end
And your project structure would look more like this:
MyClass.h
MyClass.m
MyClass_Private_iOS.m
MyClass_Private_Mac.m
Hope that helps!

Related

Prevent class from being subclassed in Objective-c

How do I prevent a particular class from being subclassed?
I am not aware of such functionality (say final keyword for example) in the language. However Apple says it has done so for all classes in AddressBookUI.framework (in iOS)
For educational purposes, how can I achieve the same functionality, or how would they have done such thing?
From iOS7 Release Notes(Requires login) :
Here's one way: override allocWithZone: from within your "final" class (substituting MyFinalClassName for your actual class name) like this:
+ (id)allocWithZone:(struct _NSZone *)zone
{
if (self != [MyFinalClassName class]) {
NSAssert(nil, #"Subclassing MyFinalClassName not allowed.");
return nil;
}
return [super allocWithZone:zone];
}
This will prevent a subclass that is not a member of MyFinalClassName from being alloc'ed (and therefore init'ed as well), since NSObject's allocWithZone: must be called eventually, and by refusing to call super from your "final" class, you will prevent this.
There's a simpler way to prevent subclassing in Xcode 6 as a result of Swift interop. To prevent Swift classes from being subclassed in Objective-C the objc_subclassing_restricted is added to all class definitions in the {ProjectName}-Swift.h file.
You can use this in your projects:
#if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted)
# define FOO_FINAL __attribute__((objc_subclassing_restricted))
#else
# define FOO_FINAL
#endif
FOO_FINAL
#interface Foo : NSObject
#end
#interface Bar : Foo
#end
The compiler will halt on the definition of Bar with Cannot subclass a class with objc_subclassing_restricted attribute
Here is possible solution:
#interface FinalClass : NSObject
#end
#implementation FinalClass
- (id)init
{
if (self.class != [FinalClass class]) {
return nil;
}
self = [super init];
if (self) {
// instance initialization
}
return self;
}
#end
#interface InvalidSubclass : FinalClass
#end
#implementation InvalidSubclass
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
#end
I'm not sure this is 100% guaranteed because it's runtime-checking anyway, but it should be enough to block and warn people that they should not subclass this. Subclass might skip superclass's init, but then the instance will not be usable because it's not fully initialised by superclass.
Something like the following will ensure that every time an "impossible subclass" calls +alloc, an object will be allocated that is an instance of FinalClass, and not the subclass. This is essentially what NSObject's +alloc method does, but here we specify an explicit class to create. This is how NSObject allocates instances (in Obj-C 2), but there is no guarantee this will always be the case, so you may want to add an appropriate -dealloc which calls object_dispose. This method also means you don't get a nil object back if you try to instantiate a subclass - you do get an instance of FinalClass.
#interface FinalClass: NSObject
//...
+ (id)alloc; // Optional
#end
// ...
#import <objc/runtime.h>
#implementation FinalClass
+ (id)alloc {
if (![self isMemberOfClass:[FinalClass class]]) {
// Emit warning about invalid subclass being ignored.
}
self = class_createInstance([FinalClass class], 0);
if (self == nil) {
// Error handling
}
return self;
}
#end
#interface InvalidSubclass : FinalClass
// Anything not in FinalClass will not work as +alloc will
// create a FinalClass instance.
#end
Note: I'm not sure I'd use this myself - specifying that a class shouldn't be subclassed is more in the nature of a design-contract with the programmer rather than an enforced rule at compile- or runtime.

is there an equivalent of method declaration/definition separation for Objective-C messages?

Let's say I have an objective-c .m file with the following methods defined:
- (void) doOneThing {
[self doAnotherThing];
}
- (void) doAnotherThing {
[self stillOtherThings];
}
if I compile this, xcode will throw me a warning that the class may not respond to -doAnotherThings, because doAnotherThing is defined below -doOneThing and the compiler doesn't know about -doAnotherThing yet when it's compiling -doOneThing. Of course, the code compiles properly and does in fact work, but I'd like to get rid of that warning message.
The trivial way to solve this problem would be to just define -doAnotherThing before -doOneThing, but sometimes I like to group related methods in the source code in ways that make it hard to re-order. If this were C, I could do something like:
void doAnotherThing();
void doOneThing() {
doAnotherThing();
}
void doAnotherThing() {
...still other things...
}
separating the definition from the declaration. Is there a way to do something like this in objective-c, or otherwise solve my problem?
The typical way to deal with this is as follows:
//in DoThings.h
#interface DoThings : NSObject {
//instance variables go here
}
//public methods go here
- (void) doAPublicThing;
//properties go here
#end
//in DoThings.m
#interface DoThings (Private)
- (void)doOneThing;
- (void)doAnotherThing;
- (void)stillOtherThings;
#end
#implementation DoThings
- (void) doAPublicThing {
[self doOneThing];
}
- (void) doOneThing {
[self doAnotherThing];
}
- (void) doAnotherThing {
[self stillOtherThings];
}
#end
You need to define these method declarations in your header file for the class:
#interface MyCustomClass : NSObject
- (void) doOneThing;
- (void) doAnotherThing;
#end
Then everything will work as intended.

What is the "template method pattern" in cocoa with Object C ? ( Language comparison thinking )

Here is template method pattern , Java and C++ can implement it easily with virtual function. How about Object C to implement this pattern ? Any example in cocoa touch (iOS) ?
As jer has already pointed out, all Objective-C methods are essentially virtual. It is a feature of the language which does not quite mesh with other C-like languages. That being said, the basics of the template method pattern can still be achieved in Objective-C, by "manually" forcing subclasses to implement certain functions. For example (using the convention in your linked Wikipedia article):
#interface Game
{
int playersCount;
}
- (void)playOneGame:(int)numPlayers;
// "virtual" methods:
- (void)initializeGame;
- (void)makePlay:(int)player;
- (BOOL)endOfGame;
- (void)printWinner;
#end
#implementation Game
- (void)initializeGame { NSAssert(FALSE); }
- (void)makePlay:(int player) { NSAssert(FALSE); }
- (BOOL)endOfGame { NSAssert(FALSE); return 0; }
- (void)printWinner { NSAssert(FALSE); }
- (void)playOneGame:(int)numPlayers
{
//..
}
#end
The above code forces subclasses of Game to override the "virtual" methods by throwing an exception the moment one of the base class implementations is called. In effect, this moves the test from the compiler stage (as it would be in C++ or Java) and into the runtime stage (where similar things are often done in Objective-C).
If you really want to enforce the rule that subclasses are not allowed to override the playOneGame: method, you can attempt(*) to verify the correct implementation from within the init method:
#implementation Game
...
- (void)init
{
if ((self = [super init]) == nil) { return nil; }
IMP my_imp = [Game instanceMethodForSelector:#selector(playOneGame:)];
IMP imp = [[self class] instanceMethodForSelector:#selector(playOneGame:)];
NSAssert(imp == my_imp);
return self;
}
...
#end
(*) Note that this code does not result in a 100% rock-solid defense against subclasses which re-implement playOneGame:, since the very nature of Objective-C would allow the subclass to override instanceMethodForSelector: in order to produce the correct result.
In Objective-C all methods are akin to C++ virtual methods.
In Objective-C Template Method Pattern is Used When you have a Skeleton of an Algorithm but it can be Implemented in different ways. Template method defines the steps to execute an algorithm and it can provide default implementation that might be common for all or some of the subclasses.
Let's take an example but First Look at the Picture
#interface Worker : NSObject
- (void) doDailyRoutine;
- (void) doWork; // Abstract
- (void) comeBackHome;
- (void) getsomeSleep;
#end
#implementation Worker
- (void) doDailyRoutine {
[self doWork];
[self comeBackHome];
[self getsomeSleep];
}
- (void) doWork { [self doesNotRecognizeSelector:_cmd]; }
- (void) comeBackHome { [self doesNotRecognizeSelector:_cmd]; }
- (void) getsomeSleep { [self doesNotRecognizeSelector:_cmd]; }
// [self doesNotRecognizeSelector:_cmd] it will force to call the subclass Implementation
#end
#interface Plumber : Worker
#end
#implementation Plumber
- (void) doWork { NSLog(#“Plumber Work"); }
#end
#interface Electrician : Worker
#end
#implementation Electrician
- (void) doWork { NSLog(#“Electrician Work"); }
#end
#interface Cleaner : Worker
#end
#implementation Cleaner
- (void) doWork { NSLog(#“Cleaner Work"); }
#end
In this example dowork() is an Abstract function which should be implemented by all the subclasses and this pattern is largely used in Cocoa Frameworks.
Hope it will help you to Understand the "Template Method Pattern".

Question about creating classes in Objective-C

I am really new to objective C, and I want to make a class that is an NSArray of NSDictionary, and then have a method that grabs a random entries. I know how to make that but I don't understand how to make it in the class. What I mean is I thought that you could put the code that declared (or whatever the correct terminology is) the array just sort of in the middle of the implementation file and then I would write a method under that. The only instance variable I had was the NSArray and that was in the interface file, along with the method prototype (or whatever) and these were the only things that were in the interface file.
I couldn't figure out the problem so I made a test class that was the same but with just an array of simple text strings. I used the same logic here and I'm pretty certain it is totally backward, I don't know in which way though.
This is the interface for the test class:
#import <Foundation/Foundation.h>
#interface TestClass : NSObject {
NSArray *TestArray;
}
#end
And this is the implementation file
#import "TestClass.h"
#implementation TestClass{
NSArray *TestArray;
}
TestArray = [[NSArray alloc] arrayWithObjects:#"stuff",#"things",#"example",#"stuffThings",nil];
#end
You should really read Apple's introduction to Objective-C. It explains the syntax and structure of the language. You must also read the Objective-C memory management guide so that your programs don't leak memory and crash.
Having said that, here's probably what you're trying to create (I took the liberty of changing some of your variable names):
TestClass.h
#import <Foundation/Foundation.h>
#interface TestClass : NSObject {
NSArray* strings_;
}
// Method declarations would go here
// example:
- (NSString*)randomElement;
#end
TestClass.m
#import "TestClass.h"
#import <stdlib.h>
// Notice how the implementation does NOT redefine the instance variables.
#implementation TestClass
// All code must be in a method definition.
// init is analogous to the default constructor in other languages
- (id)init {
if (self = [super init]) {
strings_ = [[NSArray alloc] initWithObjects:#"stuff", #"things", nil];
}
return self;
}
// dealloc is the destructor (note the call to super).
- (void)dealloc {
[strings_ release];
[super dealloc];
}
- (NSString*)randomElement {
NSUInteger index = arc4random() % [strings_ count];
return [strings_ objectAtIndex:index];
}
#end
For random number generation, it's easy to use arc4random() because it doesn't require setting the seed value.

Objective-c class extensions not effectives in static library

while creating a library that will be used on several projects, I encountered an error that I was not able to resolve by myself.
The library is composed of several "modules" that each declares its set of classes. The modules declares a header file that references the classes. Each module header is included in the library header, and all of them are copied to the library target.
The "GMData" module defines the ORM layer of the library, it declares a "GMInitializerBase" class, its purpose is to initialize the module. It must be called once in the UIApplicationDelegate.
The "GMModel" module contains the base model for the application (Categories, Articles, ...), It must register itself to "GMData" in order to function properly.
Structure:
<Library Root>
Library.h
<GMData>
GMData.h
GMInitializerBase.{h,m}
<GMModel>
GMModel.h
GMInitializerBase+GMModel.{h,m}
Contents of Library.h
#import "GMData.h"
#import "GMModel.h"
Contents of GMData.h
#import "... ORM related headers ..."
#import "GMInitializerBase.h"
Contents of GMInitializerBase.{h,m}
#import "... ORM Classes ..."
#interface GMInitializerBase : NSObject {
}
+ (void) bootstrap;
+ (GMInitializerBase*) initializer; // autoreleased instance creator
- (void) setup;
- (void) setupStore:(GMManagerFactory*)factory; // Setup database connection
- (void) setupHelpers:(GMHelperFactory*)factory; // Register helpers (abstract)
- (void) setupManagers:(GMManagerFactory*)factory; // Register managers (abstract)
#end
#implementation GMInitializerBase
+ (void) bootstrap {
GMInitializerBase* initializer = [self initializer];
[initializer setup];
}
- (void) setup {
/* Breakpoint 01 */
GMHelperFactory* helperFactory = [GMHelperFactory sharedInstance];
GMManagerFactory* managerFactory = [GMManagerFactory sharedInstance];
[self setupStore:managerFactory];
[self setupHelpers:helperFactory];
[self setupManagers:managerFactory];
}
#end
Contents of GMModel.h
#import "... Base Models files ..."
#import "GMInitializerBase+GMModel.h"
Contents of GMInitializerBase+GMModel.{h,m}
#interface GMInitializerBase (GMModel_Additions)
- (void) setup;
- (void) setupGMModelHelpers:(GMHelperFactory*)factory;
- (void) setupGMModelManagers:(GMManagerFactory*)factory;
#end
#implementation GMInitializerBase (GMModel_Additions)
- (void) setup {
/* Breakpoint 02 */
GMHelperFactory* helperFactory = [GMHelperFactory sharedInstance];
GMManagerFactory* managerFactory = [GMManagerFactory sharedInstance];
// parent implementation
[self setupStore:managerFactory];
// current implementation
[self setupGMModelHelpers:helperFactory];
[self setupGMModelManagers:managerFactory];
// parent implementation
[self setupHelpers:helperFactory];
[self setupManagers:managerFactory];
}
- (void) setupGMModelHelpers:(GMHelperFactory*)factory { /* ... */ }
- (void) setupGMModelManagers:(GMManagerFactory*)factory { /* ... */ }
#end
Contents of ProjectAppDelegate.m (located in another project, it includes the library.a and search the "Headers" directory)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[GMInitializerBase initializer] setup];
}
Stops at the first breakpoint (Breakpoint 01)
It crashed when in the library:
I declare an addition without overloading a method;
I declare an addition to a Cocoa class ([NSString toto]) without overloading;
In works when in the test project:
I declare an addition to a Cocoa class ([NSString toto]) without overloading;
I didn't try to overload a library class but I assume it will work too.
My problem is the following: I can't get the addition workingm and I need it.
Thanks for reading, thanks for answering.
Make sure you have the -all_load and -ObjC flags set in the "Other Linker Flags" in the project settings. Categories in a library won't work without them.
In Objective-C, you shouldn't override the method in a category of a class. Say you have
#implementation MyClass
-(void)foo
{
NSLog(#"%#",#"original!");
}
#end
and later you have
#implementation MyClass (MyCategoryA)
-(void)foo
{
NSLog(#"%#",#"categoryA!");
}
#end
#implementation MyClass (MyCategoryB)
-(void)foo
{
NSLog(#"%#",#"categoryB!");
}
#end
Then the result of
MyClass* myInstance=...;
[myInstance foo];
is not reliable, see the discussion in the official documentation. The documentation says it works if you have only one category, but the documentation says at the same time you shouldn't use that feature. So, don't do this.
The sole exception is +load. If a category defines this method, the runtime calls it for each category you define. So, if you want to perform some initialization task per category, +load is the way. Read the official documentation and this blog post by Mike Ash.