SpriteKit: Use one instance of a Class in multiple scenes? - ios

Is it possible to make an instance of a class and then send/recive data from every scene in a game?
Ex: Let say you make a RPG game, and you want to create a "Party" with information like Party Leader, members etc... and then you want different battle scenes to use the data from the very same instance?
If possible, then how?
/Daniel

First option: use a singleton.
Second option: pass the instance as a parameter to custom init method of a scene:
#interface GameScene1()
#property Party *party;
#end
#implementation GameScene1
-(id)initWithSize:(CGSize)size party:(Party*)party {
if (self = [super initWithSize:size]) {
self.party = party;
.....
}
}
#end

Related

Is extending a base class to overwirte something an anti-pattern in Objective-C

In objective-c.
Let's say i wish to provide a custom contentview for a UICollectionView (for whatever reason it may be).
I realise the only way to achieve it is this
#interface UICollectionViewCell (Extension)
#property(nonatomic, readwrite)UIView *contentView;
#end
#interface BaseCollectionViewCell()
#property(nonatomic, readonly)UIView *hairuiView;
#end
#implementation BaseCollectionViewCell
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.contentView = [[UIView alloc] init];
}
return self;
}
#end
Basically, i extend the content view to be read write, such that now i can make it to be any custom view i want.
Is this considered an anti-pattern in Objective-C
The basic idea of adding a specialized method to an existing class (UICOllectionViewCell) via a category is just fine.
So defining -(UIView*)hairuiView is cool.
However, the other stuff you're trying to do isn't cool:
(1) You cannot override a method via a category.
It's possible (although not explicitly supported) to override a method implemented in the superclass, but you cannot override a method in the class of the category.
The compiler will often allow this, but at run time only one of the methods gets implemented (either the class' original method or the one from the category), not both. And to make matters worse, there's no guarantee of which one the runtime will pick (it all depends on the order the classes are loaded).
Furthermore, [super ...] in the category will refer to the class's superclass, not the class you're adding the category to.
(2) You should not use an extension on classes you didn't write.
An extension is simply an anonymous category with some additional perks. However, you should only create an extension for a class that you wrote (and compile in that project). You can't add extensions to classes you don't own or have already been compiled. (Extensions allow additional instance variables to be defined and this can only be done once during compilation; you can't add instance variables to a class that's already defined/compiled.)
So stick to regular categories to extend existing classes. Category restrictions built into the language will generally keep you out of trouble—except for the override thing in (1).
This much is OK:
#interface BaseCollectionViewCell (HairuiView)
#property (nonatomic,readonly) UIView *hairuiView;
#end
#implementation BaseCollectionViewCell (HairuiView)
- (UIView*)hairuiView
{
...
}
#end

ARC and Objective C Subclassing

I am quite new to Obj C and iOS development and I have come across an issue to which I have no understanding why it is happening.
So to set the scene, I have 2 model classes, Player and Computer Player and a Controller.
Player:
#interface Player : NSObject
-(void) playerMessage;
#end
ComputerPlayer:
#interface ComputerPlayer : Player
-(void) computerPlayerOnlyMessage;
#end
Controller:
#interface viewController : UIViewController{
Player *p1;
Player *p2;
Player *currentPlayer;
}
#implmentation ViewController
-(void)viewDidLoad
{
p1 = [[Player alloc]init];
p2 = [[ComputerPlayer alloc]init];
[p1 playerMessage];
currentPlayer = p2;
[currentPlayer computerPlayerOnlyMessage];
}
However the issue with the above is [currentPlayer computerPlayerOnlyMessage] gives a complier error when ARC is turned on. When ARC is turned off it gives a compiler warning but will run as I would expect it too.
Any help is appreciated to get help me figure why this behaviour is happening.
Isn't it better to define:
- (void)playerMessage;
method in ComputerPlayer class and:
-(void)playerMessage {
[super playerMessage];
[self computerOnlyPlayerMessage];
}
That's a point of inheritance, isn't it? You defined (expecting) your class variable as Player but NOT ComputerPlayer, and if it is ComputerPlayer it will execute specific work only for "computer".
Of course then you execute:
[Player playerMessage]; // Warning should gone
You can test, if it is a computer player
if ([currentPlayer isKindOfClass:[ComputerPlayer class]])
[(ComputerPlayer *)currentPlayer computerPlayerOnlyMessage];
It gives you an error because p2 is subclass of Player and you haven't for such a method as computerPlayerOnlyMessage in Player class. This method exists in ComputerPlayer class so you should declare p2 as a object of this type. Chenge line where you declare p2 to:
ComputerPlayer *p2;
First instead of declaring them as ivars like
#interface viewController : UIViewController{
Player *p1;
Player *p2;
Player *currentPlayer;
}
do it with #properties. The reason being is that ivars don't have any getters or setters whereas they are automatically generated if you use '#properties so change to
#interface viewController : UIViewController
// This will auto generate the ivars, getters and setters
#property (nonatomic, strong) Player *p1;
#property (nonatomic, strong) Player *p2;
#property (nonatomic, strong) Player *currentPlayer;
#end
then you can do
#implmentation ViewController
-(void)viewDidLoad
{
p1 = [[Player alloc]init];
p2 = [[ComputerPlayer alloc]init];
[p1 playerMessage];
currentPlayer = p2;
// Could also replace below with [currentPlayer isKindOfClass:[ComputerPlayer class]] use which ever fits best for you and what you want.
// If using below, if you decided to subclass ComputerPlayer class anything that subclassed
// from ComputerPlayer will also make it into this if statement. If iskindOfClass: is used
// Only objects that are kind of class will make it into this if statement.
if([[currentPlayer class] isSubclassOfClass:[ComputerPlayer class]]) {
[(ComputerPlayer *)currentPlayer computerPlayerOnlyMessage];
}
}
As #Greg said, computerPlayerOnlyMessage is a method exposed by the ComputerPlayer class, not the class it inherits from, so even if the compiler reports a warning when ARC is disabled, it would be a bad practice to use it.
Explicitly asking the class instance if it implements that method it's a workaround that works though. However in my opinion that solution lacks of good OO design, and I wouldn't use it unless I have a good reason (there are cases when it is handy) - in other OO languages that wouldn't event be possible.
Polymorphism allows an instance of a class to be used as if it were one of its super classes, but not the opposite. You can override and specialize the superclass methods, but you cannot expect a superclass to be aware of methods implemented by any of its subclasses.
I suggest 2 possible solutions:
declare computerPlayerOnlyMessage in the Player class as abstract (with empty body or throwing an exception, acting as a reminder that the method should be overriden in subclasses)
remove computerPlayerOnlyMessage from ComputerPlayer and instead override playerMessage. Thanks to polymorphism, the correct implementation will be called, regardless of whether you are accessing to the class instance as Player or ComputerPlayer
If computerPlayerOnlyMessage is meant to do what playerMessage does, just in a different way, I'd choose option no. 2
This seems like a good place to use protocols.
Here's how I might write your example, where I need to send "player" messages to all instances of Players, specialize on occasion, and send specific "npc" messages other times.
#protocol <NPC>
#property (nonatomic) NSString *someNPCString;
- (void)foo;
- (void)bar;
#end
#interface Player : NSObject
#end
#implementation Player
- (void)message
{
NSLog(#"message");
}
#end
#interface AI : Player <NPC>
#end
#implementation AI
#synthesize someNPCString;
- (void)foo
{
NSLog(#"foo");
}
- (void)bar
{
NSLog(#"bar");
}
#end
#interface viewController : UIViewController
#property (nonatomic) NSArray *humans;
#property (nonatomic) NSArray *npcs;
#end
#implmentation ViewController
-(void)viewDidLoad
{
id<Player> currentPlayer = nil;
humans = [NSArray arrayWithObject:[Player new]];
npcs = [NSArray arrayWithObjects:[AI new], [AI new], nil];
// message all player types, regardless of player or npc
for (Player *player in [humans arrayByAddingObjectsFromArray:npcs])
{
currentPlayer = player;
[player message];
if([player conformsToProtocl:#protocol(NPC)])
{
[(id<NPC>)player foo];
}
}
for (id<NPC>npc in npcs)
{
[npc bar];
npc.someNPCstring = #"str";
}
}
As you can see, this lets you treat npcs like human players, if you need to, let's you ask if the player conforms to the NPC protocol, so you may call the required protocol methods, and let's you reference objects specifically by their protocol.
Protocols begin to make the most sense, in my humble opinion, when you begin to need to "mix in" behavior to various objects.

How can I share properties across custom and Apple-provided classes through inheritance?

I have a few different viewControllers that need to inherit the same properties, but aren't the same type of viewController. For example, one VC is a regular UIViewController, whereas another one is a UISplitViewController. Is there any way for me to efficiently use inheritance to make sure they all have these certain properties? Or do I just need to give each one their own separate declarations?
You can achieve what you want using a category on UIViewController. You can implement the properties in the category using associated objects.
See Objective-C: Property / instance variable in category for more details.
You could add a category to UIViewController. Since UISplitViewController inherits from UIViewController, it will have all properties and methods as defined in the category as well. However, categories have two limitations:
You can't add backing instance variables. You can create properties, but they can't have instance variables backing them. That means that if you are overriding the getter (and setter, if readwrite), so that it reads (or writes) an already existing property in some way, you're good. If not, you can look at associated objects.
Overriding methods in a category is a no-no. While nothing stops you from doing it, you have undefined behavior if another category overrides that method too. You just don't know which method will get executed. If you need to override methods, subclassing UIViewController would be better. However, UISplitViewController will then not know about these properties, unless you subclass it as well and add those same properties (in which case you're maintaining these properties twice).
I'm not sure what exactly do you need. If you don't want to (or can't) use common superclass with public properties, you can always write protocol. Only difference is that, protocol don't give you common implementation, but force you to write one (so you can be sure it is there, as you asked for).
Why not set up inheritance using a shared base class and set those shared properties in the init?
//MyBaseVC.h
#interface MyBaseVC : UIViewController
#property (nonatomic, strong) NSString *myString;
#end
//VC1.h
#interface VC1 : MyBaseVC
#end
//VC2.h
#interface VC2 : MyBaseVC
#end
-----
//(MyBaseVC.m)
-(id) init {
self = [super init];
if(self){
self.myString = #"Hello world!";
}
return self;
}
// VC1.m
-(id) init {
self = [super init];
NSLog(#"%#", self.myString); // "Hello world!"
return self;
}
// VC2.m
-(id) init {
self = [super init];
NSLog(#"%#", self.myString); // "Hello world!"
return self;
}
At that point, you can directly refer to the property on the subclassed objects:
NSLog(#"%#",myVc1.myString); //"Hello world!"
Otherwise, when you reference the VCs in a more generic fashion, you can always refer to their super class (MyBaseVC) - for example, if you need to pass them as a method parameter.
//-(void)doSomethingWithVC:(MyBaseVC *)vc;
[someObj doSomethingWithVc: vc1];

Objective C Polymorphism for a Game Class

My situation is that I have a few different mini-games in my app. The games are listed in a table view, and when one of them is selected I create the corresponding game object. I need the game objects to have a common parent class, so that I can do common things such as pausing and resuming.
I have a generic GameScene class which has a generic GameLayer property:
#interface GameScene : CCScene {
}
#property (nonatomic, strong) GameLayer* gameLayer;
However each child class of GameScene has it's own type of GameLayer. So for example the SuperMarioBros_GameScene has a SuperMarioBros_GameLayer. The problems arise when I initialize the SuperMarioBros_GameScene:
-(id) init
{
if( (self = [super init])) {
self.gameLayer = (SuperMarioBros_GameLayer*)[SuperMarioBros_GameLayer node];
[self addChild: self.gameLayer];
SuperMarioBros_InputLayer *inputLayer = [SuperMarioBros_InputLayer node];
inputLayer.delegate = self.gameLayer; //ERROR (see below)
[self addChild:inputLayer z:1 tag:2];
}
return self;
}
In general each game will also have an input layer, which will send UI messages to the gameLayer. I get this error:
Assigning to 'id <SuperMarioBros_FireDelegate> from incompatible type GameLayer*
I understand this is happening because SuperMarioBros_GameLayer implements <SuperMarioBros_FireDelegate>, but GameLayer doesn't.
So my question is how do I make the GameScene class hold a polymorphic property that can be any subclass of GameLayer? Keeping in mind that each type of GameLayer may implement different protocols based on their corresponding input layers? Or maybe there's a better way to go about this?
Again, the end goal is to be able to make a call from the main game controller such as
[currentGameScene pause];
which will in turn do something like:
[self.gameLayer pauseGameLoop];
In the implementation file of the SuperMarioBros_GameScene class, you can override the property declaration of the gameLayer property in a class extension:
#interface SuperMarioBros_GameScene ()
#property (nonatomic, strong) SuperMarioBros_GameLayer *gameLayer;
#end
Then you should not get a compiler error anymore.
This will not create new getter or setter functions (these are still called in the superclass), and does not introduce a new instance variable as backing store. It only tells the compiler that self.gameLayer is an instance of the subclass in this implementation file.

How to reach the main class in a parent object?

I'm writing a simple card game for iPhone. The cards are in cardset containers which are subclasses of UIScrollView. The cardsets are in a desk container which is a UIViewController. In the cards i overrode the touchesEnded method but I cannot access the main class which contains the desk. How to do it? Should I give a pointer to all the cards to reach the main? Or can I get the parent container?
You can store a reference in the cards.
In the cards interface, define something like
#interface cards
{
DeskController *deskp;
}
#property (nonatomic, assign) DeskController *deskp;
When you create cards, assign the desk reference
- (void) setupCards
{
CardController *card = // allocate card
card.deskp = self;
}
In cards dealloc() or viewDidUnload, set it to nil
self.deskp = nil;

Resources