I have a base class which has a custom init method and then uses the value passed in through the init method to then call custom init methods on it's subclasses. problem is When i try to access variables that have had values assigned to them in the base class through the subclass Via super, the values are null, it's like the base class is a completely different object. Is it because the base class has not yet returned from it's init method yet? or am i going about the wrong way with inheritance here? code to follow.
Interface
#interface WTFGameBoard : NSObject
{
#protected
UIView *_answerView;
UIView *_keyboardView;
NSMutableArray* _answerSeperatedByCharacter;
WTFAnswerBoard *_answerBoard;
WTFGameKeyboard *_gameKeyboard;
OpenGameViewController *_weakGameViewRef;
GameInfo *_gameinfo;
}
-(id) initWithGameVC:(OpenGameViewController*)gameVC;
#property (nonatomic,unsafe_unretained)OpenGameViewController *weakGameViewRef;
#property (nonatomic,strong)GameInfo *gameInfo;
#end
Implementation
#implementation WTFGameBoard
#synthesize weakGameViewRef = _weakGameViewRef;
#synthesize gameInfo = _gameinfo;
-(id) initWithGameVC:(OpenGameViewController*)gameVC
{
if (self = [super init])
{
//[weakGameViewRef ]
_answerView = [gameVC answerView];
_keyboardView = [gameVC keyboardView];
self.weakGameViewRef = gameVC;
self.gameInfo = [[CurrentGamesInfo sharedCurrentGamesInfo]_selectedGame];
_answerBoard = [[WTFAnswerBoard alloc] initWithAnswer:[gameVC answer] blankSpaceImageView:[gameVC answerBox]];
_gameKeyboard = [[WTFGameKeyboard alloc] initWithButtons:[gameVC letterSelectButtons]];
}
return self;
}
#end
Interface
#interface WTFAnswerBoard : WTFGameBoard
{
NSMutableArray *WTFAnswerSpaces;
NSMutableArray *_answerBlankBlocks;
NSMutableArray *_answerGiven;
NSMutableArray *_answerBlankOriginalPosition;
NSString *_answer;
}
-(id)initWithAnswer:(NSString*)answer blankSpaceImageView:(UIImageView*)answerBox;
Implementation
-(id)initWithAnswer:(NSString*)answer blankSpaceImageView:(UIImageView*)answerBox
{
if ( self = [super init] )
{
_weakGameViewRef = [super weakGameViewRef];//WHY U NO NOT BE NULL?
_gameinfo = [super gameInfo];//WHY U NO NOT BE NULL?
_answerBlankBlocks = [_weakGameViewRef answerBlankBlocks];
_answerGiven = [_weakGameViewRef answerGiven];
_answerBlankOriginalPosition = [_weakGameViewRef answerBlankOriginalPosition];
[self SetupBlankAnswerSpacesForAnswer:answer withTemplate:answerBox];
}
return self;
}
Problem is the fact that you are not invoking your custom constructor in your derived class:
if ( self = [super init] )
You are calling the default one, which is not overridden, and that doesn't initialize the ivars that you are trying to access.
You should call your custom constructor:
if ( self = [super initWithGameVC:gameVC] )
Of course this means that you need to pass along the parameter, or override the default constructor by initializing what you want to initialize without the need of any parameter.
Another thing that I don't understant is why you are setting ivars of the derived class in your custom class:
_weakGameViewRef = [super weakGameViewRef];
This basically does nothing because the ivar is the same, if you set the one of your base class then you can directly access it.
EDIT
Since you have a strange dependency issue here a quick solution would be to have something like
WTFAnswerBoard initWithWTFGameBoard:(WTFGameBoard*)board {
self.board = board;
}
so that you can access the board that instantiates the WTFAnswerBoard and keep inheritance but switching the usage to composition (by adding a property to WTFAnswerBoard so that your recursive initialization doesn't occur.
Related
u1Option is an Option (custom class) object which is
declared and called in my ViewController viewDidLoad as:
- (void)viewDidLoad {
[super viewDidLoad];
Option *u1Option = [[Option alloc]init];
[u1Option setName: #"test"];
NSLog(#"Test1 Result: %#", u1Option.name);
}
Option is a custom class inheriting from NSObject and has a property declared in Option.h:
#property NSString *name;
However, if try to use u1Option in a IBAction, nothing is passed and while I get the "test" string in the Test1 NSLOG, on the contrary I get NULL in the Test2 NSLOG.
- (IBAction)addFirstOption:(UIButton *)sender {
NSLog(#"Test2 Result: %#", u1Option.name);
}
The Option instance is destroyed as soon as viewDidLoad returns. You need to put it in a property or instance variable. If it's already a property or instance variable then you are re-defining it, so use:
u1Option = [[Option alloc] init];
And this issue can be avoided in future by using self.u1Option or _u1Option.
I have trouble implementing a Key-Value Observer at my attempt to follow the MVC pattern. I have a controller class, a model class and a view class. I update my model from the controller class and I want to put a key value observer in my view class to monitor when a NSMutableArray changes in model (like through addObject) and then redraw itself automatically. I used answer in this thread to guide me: How to add observer on NSMutableArray?
Code so far:
From my Scene (using sprite kit if it matters). Setting of letters will be done from Ctrl class, this is just to test.
BarCtrl *barCtrl = [[BarCtrl alloc] init];
BarModel *barModel = [[BarModel alloc] init];
BarView *barView = [[BarView alloc] init];
barCtrl.barModel = barModel;
barCtrl.barView = barView;
barView.barModel = barModel;
ScrabbleDeck *sd = [[ScrabbleDeck alloc] init];
if([barModel addLetter:[sd getLetter] onSide:BarModelSideRight])
NSLog(#"Added letter");
BarModel.h
#import <Foundation/Foundation.h>
#import "Letter.h"
typedef NS_ENUM(int, BarModelSide) {
BarModelSideLeft,
BarModelSideRight
};
#interface BarModel : NSObject
#property (nonatomic, strong) NSMutableArray *addedLetters;
- (instancetype)init;
- (BOOL) addLetter: (Letter*) letter onSide: (BarModelSide) side;
#end
BarModel.m
#import "BarModel.h"
#interface BarModel ()
#property (nonatomic) int capacity;
#end
#implementation BarModel
- (instancetype)init
{
self = [super init];
if (self) {
self.capacity = letterCapacity;
_addedLetters = [[NSMutableArray alloc] init];
}
return self;
}
// We'll use automatic notifications for this example
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
{
if ([key isEqualToString:#"arrayLetter"]) {
return YES;
}
return [super automaticallyNotifiesObserversForKey:key];
}
- (BOOL) addLetter: (Letter*) letter onSide: (BarModelSide) side{
if([_addedLetters count] > _capacity){
return FALSE;
}
switch (side) {
case BarModelSideLeft:
[_addedLetters insertObject:letter atIndex:0];
return TRUE;
break;
case BarModelSideRight:
[_addedLetters addObject:letter];
return TRUE;
break;
default:
return FALSE;
break;
}
}
// These methods enable KVC compliance
- (void)insertObject:(id)object inDataAtIndex:(NSUInteger)index
{
self.addedLetters[index] = object;
}
- (void)removeObjectFromDataAtIndex:(NSUInteger)index
{
[self.addedLetters removeObjectAtIndex:index];
}
- (id)objectInDataAtIndex:(NSUInteger)index
{
return self.addedLetters[index];
}
- (NSArray *)dataAtIndexes:(NSIndexSet *)indexes
{
return [self.addedLetters objectsAtIndexes:indexes];
}
- (NSUInteger)countOfData
{
return [self.addedLetters count];
}
#end
BarView.h
#import <SpriteKit/SpriteKit.h>
#import "BarModel.h"
#interface BarView : SKSpriteNode
#property (nonatomic, strong) BarModel *barModel;
#end
BarView.m
#import "BarView.h"
#implementation BarView
static char MyObservationContext;
- (instancetype)init
{
self = [super init];
if (self) {
//_barModel = [[BarModel alloc] init];
}
return self;
}
-(void)setBarModel:(BarModel *)barModel{
if(_barModel != barModel)
_barModel = barModel;
[_barModel addObserver:self
forKeyPath:#"arrayLetter"
options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew)
context:&MyObservationContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// Check if our class, rather than superclass or someone else, added as observer
if (context == &MyObservationContext) {
// Check that the key path is what we want
if ([keyPath isEqualToString:#"arrayLetter"]) {
// Verify we're observing the correct object
if (object == self.barModel) {
[self draw:change];
}
}
}
else {
// Otherwise, call up to superclass implementation
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void) draw: (NSDictionary*) change{
NSLog(#"KVO for our container property, change dictionary is %#", change);
}
#end
When I ru this I get this "error":
2014-08-31 00:23:02.828 Testing[329:60b] Added letter
2014-08-31 00:23:02.830 Testing[329:60b] An instance 0x17803d340 of class BarModel was deallocated while key value observers were still registered with it. Observation info was leaked, and may even become mistakenly attached to some other object. Set a breakpoint on NSKVODeallocateBreak to stop here in the debugger. Here's the current observation info:
<NSKeyValueObservationInfo 0x17804eb50> (
<NSKeyValueObservance 0x1780cf180: Observer: 0x178111670, Key path: arrayLetter, Options: <New: YES, Old: YES, Prior: NO> Context: 0x100101428, Property: 0x17804eb80>
I tried to follow the instructions in error but can not find where to set break point. Please help me figure this out!
The error is pretty descriptive. You add self as an observer of a BarModel object. At some point that object gets deallocated. But you never remove self as an observer by calling removeObserver:forKeyPath:context:. You need to do that.
First, in setBarModel, make sure to remove self as an observer of the previous value of _barModel.
Next, you probably need to add a dealloc method that does the same thing.
There are multiple problems with the code. In addition to what Tom Harrington said with respect to the specific error that was logged about failing to remove the observation:
You implemented the indexed collection accessors for a (non-existent) property named "data". That is, they have "Data" in their name where the property name should be.
The indexed collection property is addedLetters. So, the indexed collection mutating accessors should be:
- (void)insertObject:(id)object inAddedLettersAtIndex:(NSUInteger)index;
- (void)removeObjectFromAddedLettersAtIndex:(NSUInteger)index;
You don't really need the non-mutating accessors, since you have an array-type public property with a normal getter (i.e. -addedLetters).
By the way, that property is of type NSMutableArray which it should not be. The property should be of type NSArray, backed by an instance variable of type NSMutableArray. That is, the mutability of the type (as opposed to the property) should not be exposed through the public interface. When you do this, you have to manually declare the instance variable (since it should differ from the type of the property and auto-synthesis will get it wrong), make the property copy instead of strong, and implement the setter yourself to do a mutable copy of the passed-in immutable array:
- (void) setAddedLetters:(NSArray*)addedLetters
{
if (addedLetters != _addedLetters)
_addedLetters = [addedLetters mutableCopy];
}
Once you have implemented the indexed collection mutating accessors with the correct names, you must use only those methods to mutate the collection (after initialization). In particular, your -addLetter:onSide: method must not directly operate on the _addedLetters instance variable. This is the part that makes the class KVO-compliant for that property. The mere presence of the indexed collection mutating accessors does not help. They must be used for all actual mutations.
Your implementation of +automaticallyNotifiesObserversForKey: is redundant. Automatic notification is the default.
The BarView class is key-value observing a key path "arrayLetter" on its _barModel object, but that's not the name of the property on BarModel. I suppose you meant to use the key path "addedLetters".
Finally, for proper adherence to MVC design, your view should not have a reference to your model. It should have a reference to the controller. The controller can reflect the model to the view (or, in theory, adapt a model of a different internal design to what the view expects). Or, in a more traditional non-KVO approach, the controller would actually tell the view when something has changed and give it the updated data it should show.
So, your controller could expose its own addedLetters property:
#property (readonly, copy, nonatomic) NSArray* addedLetters;
It could be implemented as a derived property, forwarded through to the _barModel object:
+ (NSSet*)keyPathsForValuesAffectingAddedLetters
{
return [NSSet setWithObject:#"barModel.addedLetters"];
}
- (NSArray*)addedLetters
{
return self.barModel.addedLetters;
}
Then, the view would have a reference to the controller and not the model, and it would key-value observe the "addedLetters" key path on the controller.
In my XCode project, I want some default setup settings, which basically is a set of variables like GlobalTintColor, ServerUrl and so forth.
I then need to override some of these settings per client/target.
These settings are only for interval use, which means I'm not looking for settings bundle type solution.
I don't want to have duplicate settings, so some sort of inheritance seems to be the right way to go.
I was thinking I'd make a parent class carrying all the default settings, and then a subclass for each client, overriding settings as needed. I just can't figure out how I'm going to load these settings. I figured only the clients that needed to override settings had a subclass. Other clients just used the default settings as defined by the parent class.
But when I'm loading the settings at application start, I then need to check if the subclass is available, and if not, I only load the super class.
But then I get the problem of what kind of class the settings are: subclass or superclass?
I've been looking into categories as well as class clustering, but haven't found a solution so far.
Seems to me this is functionality a lot of app developers need. Does any of you know of a good pattern to solve this?
To illustrate:
- (id) getAppConfigurationSettings {
id settings;
if ([AppConfigurationSettings class]) {
settings = [AppConfigurationSettings class];
} else {
settings = [DefaultAppConfigurationSettings class];
}
return settings;
}
Do you want something like this ?
"Parent.h"
#interface Parent : NSObject
#property(nonatomic,strong)UIColor *color;
#end
"Parent.m"
#import "Parent.h"
#implementation Parent
-(void)setColor:(UIColor *)color{
self.color=color;
}
#end
Then you create another class which will inherit Parent say Child
Child.h
#import "Parent.h"
#interface Child : Parent
#end
Child.m
#import "Child.h"
#implementation Child
//Override the actual color
-(void)setColor:(UIColor *)color{
self.color=color;
}
#end
Then you can use it like below
Parent *parent=[[Parent alloc] init];
[parent setColor:[UIColor redColor]];
Child *child=[[Child alloc] init];
[Child setColor:[UIColor blueColor]];
I hope it will give you enough idea..
Updated
For custom initialization you can create some enum, and do your initializations accordingly like below
typedef enum {
kParent = 1,
kChild = 2
}kSettings;
-(void)updateColor:(kSettings)settingType{
id classObj;
switch (settingType) {
case kParent:
classObj=[[Parent alloc] init];
break;
case kChild:
classObj=[[Child alloc] init];
break;
default:
break;
}
[classObj setColor:[UIColor redColor]];
}
Note - The above code is not tested may not be completely correct, but can be like this.
When I hear about "base" and "override", I immediately think of a hierarchy of classes, so #iphonic answer does the job pretty well, although I would design it in a slightly different way:
"BaseSettings"
#interface BaseSettings : NSObject
... properties
#end
#implementation BaseSettings
- (instancetype) init {
self = [super init];
if (self) {
[self constantInit];
[self dynamicInit];
}
}
// Put here initialization that won't be overridden
// in inherited classes
- (void) constantInit {
}
// Put here initialization that will be overridden
// in inherited classes
- (void) dynamicInit {
}
#end
"SettingsInheritor"
#interface SettingsInheritor : BaseSettings
#end
#implementation SettingsInheritor
- (void) dynamicInit {
// Call base method so that not overriden settings
// are still initialized properly
[super dynamicInit];
// Override settings here
...
}
The constantInit method is for convenience only, to let you visually separate constant from overrideable settings - so you can get rid of it if you won't need or like it.
What can be improved in #iphonic's answer is how the actual settings class is instantiated, so I propose a different approach.
As described here, you can use obj_getClassList() to obtain the list of all registered class definitions - then you can loop through all of them, and check if its superclass is BaseSettings (or whatever you want to call the base settings class), using class_getSuperClass() or isSubclassOfClass:. Note: the latter method returns YES if subclass or identical, something to take into account when comparing.
Once you find a class inheriting from BaseSettings, you can break the loop and instantiate the found class (for instance using class_createInstance()). A (untested) skeleton is like this:
int numClasses = objc_getClassList(NULL, 0);
if (numClasses > 0) {
BOOL found = NO;
Class settingsClass;
Class *classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);
for (int index = 0; index < numClasses; ++index) {
Class curClass = classes[index];
Class superClass = class_getSuperclass(curClass);
const char *superClassName = class_getName(superClass);
if (strcmp(superClassName, "BaseSettings") == 0) {
settingsClass = curClass;
found = YES;
break;
}
}
if (found) {
// Create the class instance from `settingsClass`
}
free(classes);
}
Credits to Ole Begemann for (most of) the above code
I have class RestModel : NSObject that handles interaction with a Rest API, with the intention being to subclass for each model accessible via the API (e.g., PageModel : RestModel or PhotoModel : RestModel)
What I want to do is have a generic forgeFromDictionary static method which will return an instance of the subclass, but will be the same for each subclass unless there is some custom instantiation involved. So ideally the forgeFromDictionary method would be on my RestModel, but I could call PageModel* myPage = [PageModel forgeFromDictionary:previouslyDefinedDictionary]; and get an actual PageModel object.
Is this possible in objective-c?
Here's what I've tried, not sure if I'm on the right track:
I know I can set the return type of a static method to instancetype, which apparently references whichever class is actually calling the method, like so:
+ (instancetype) forgeFromDictionary: (NSDictionary*) dictionary
But using instancetype anywhere in the actual method gives a fatal error, so this doesn't work:
+ (instancetype) forgeFromDictionary: (NSDictionary*) dictionary {
instancetype *object = [[instancetype alloc] init]; # red alert!
# "use of undeclared identifier 'instancetype'
object.properties = dictionary;
object.original = dictionary;
return object;
}
Inside your method, replace:
instancetype *object = [[instancetype alloc] init];
with:
RestModel *object = [[self alloc] init];
If you want to return the instance class and not the super class maybe you can use something like this:
+(id)forgeFromDictionary:(NSMutableDictionary *)dict{
Class t = [self class];
id test = [t new];
// if you have a common property you can set it here by doing
if([test respondsToSelector:#selector(property)]){
//set value here
[test setProperty:val];
}
return test;
}
using it will just be like:
// you need to cast the object since creating it with id type
YourClass *instance = (YourClass *)[YourClass forgeFromDictionary:yourDictionary];
This will return the instance class and not the super class
originally, I have a default init function
-(id) init
{
if (self=[super init])
{
......
}
return self;
}
However, I like to override the init function to pass in custom objects or other objects
like
-(id) initWithScore:(NSString*) score
{
if (self=[super init])
Now there is an error saying [super init] function can only be called with -(id) init function.
So what do I do to fix it so I can pass in objects and also use self=[super init]?
Error:Cannot assign to self outside of a method in the init family.
I was trying to convert a projet to ARC and after creating a new one and including the files from the old one - one of the issues i got was
Cannot assign to 'self' outside of a method in the init family
The selector name MUST begin with init - not only that in my case the init selector was:
-(id)initwithPage:(unsigned)pageNum {...}
Notice the small 'w'.
I have changed it to:
-(id)initWithPage:(unsigned)pageNum {...}
Notice the capital 'W'!
My problem was solved.
I hope this helps someone.
You need to return an object of type id in your new method.
Suppose you have declare an NSString *myscore property, you will write something like this:
-(id) initWithScore:(NSString*) score
{
self=[super init];
if (self)
{
self.myscore = score;
}
return self;
}