How to reimplement property getter method in subclass? - ios

I'm new to objective-c and found some OO features I have learned from other language is different in objective-c. The most confusing question for me until now is how to reimplement a property in subclass, or should I do this?
For example, I have two view controllers GameViewController and CardGameViewController.
#interface GameViewController : UIViewController {
Game *_game;
}
#property (nonatomic) Game *game;
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons;
#property (weak, nonatomic) IBOutlet UILabel *scoreLabel;
#property (weak, nonatomic) IBOutlet UILabel *messageLabel;
#end
#interface CardGameViewController : GameViewController
#property (nonatomic) CardMatchingGame *game;
#end
CardMatchingGame is derived from Game. #property game in GameViewController is implemented like below:
- (Game *)game
{
if (!_game) {
_game = [[Game alloc] initWithCardCount:[self.cardButtons count]
usingDeck:[self createDeck]];
}
return _game;
}
I tried to reimplement #property game like below but got a warning in the return clause which said Incompatible pointer types returning 'Game *' from a function with result type 'CardMatchingGame *'.
- (CardMatchingGame *)game
{
if (!_game) {
_game = [[CardMatchingGame alloc] initWithCardCount:[self.cardButtons count]
usingDeck:[self createDeck]
matchThreshold:self.threshold.selectedSegmentIndex + 2];
}
return _game;
}
Is this the correct way of reimplementing a property in subclass? If not, what am I supposed to do?

You can't reimplement like that (well, you could do a ton of casting, but that isn't a good option).
One option is to create the property as #property (nonatomic) id game; and then it can hold any type of Game class (or subclass) that you have. And, because it is id type the compiler will assume that any method you try to call is valid. Obviously you could make mistakes which you won't find till later.
Another, better, option is to add a new property in the subclass as #property (nonatomic) CardMatchingGame *cardGame; and, in the subclass, when a new game is created the CardMatchingGame instance is stored in both game and cardGame properties. Now, the superclass has access to game and can use it, and the subclass has access to cardGame, which is typed as the correct class. So, you have compiler checks and usage of the same instance in both classes. But, you haven't reimplemented a property - you have multiple properties, each doing the appropriate task for their location + purpose.

If CardMatchingGame is a subclass of Game you can override - (Game *)game and return your CardMatchingGame object. Just make sure not to change the return type.

I think the problem is ivar in parent class Game *_game; it produce warning you see. Try to use different name or id type for it.
or just cast it before return (CardMatchingGame *)_game;

Related

Xcode requiring me to redeclare properties as instance variables

I have an object called SCPFAd and it is declared in its header file as follows:
#interface SCPFAd : NSObject
#property (strong, nonatomic) NSArray *imageURLs;
#property (strong, nonatomic) NSString *title;
#property (strong, nonatomic) NSString *price;
#property (strong, nonatomic) NSString *longDescription;
#property (strong, nonatomic) SCPFLocation *location;
#property (strong, nonatomic) SCPFCategory *category;
#property (strong, nonatomic) NSArray *properties;
#property (readonly, strong, nonatomic) NSString *sellerID;
#property (readonly, strong, nonatomic) NSString *timePosted;
- (id)initWithRawData:(NSDictionary *)rawData;
- (BOOL)displaysPrice;
#end
In the implementation file, I have an SCPFAd extension declared this way:
#interface SCPFAd ()
{
NSMutableDictionary *_rawData;
NSMutableArray *_imageURLs;
NSString *_title;
NSString *_price;
NSString *_longDescription;
SCPFLocation *_location;
SCPFCategory *_category;
NSMutableArray *_properties;
}
#property (strong, nonatomic) NSDictionary *rawData;
#property (strong, nonatomic) NSString *sellerID;
#property (strong, nonatomic) NSString *timePosted;
#property (strong, nonatomic) NSString *adID;
#end
I deliberately redeclared the properties rawData, imageURLs, and properties as instance variables because I want external objects to access or assign them as immutable types, but I'll be changing them internally.
What I don't understand is why, when I override the setters, I get a compiler error that says it can't find the variables _title, _price, _longDescription, _location, and _category. The error goes away when I redeclare title, price, longDescription, location, and category as above, but I see it as unnecessary--nothing in the class extension changes their external declarations.
This is how I'm overriding setTitle, for example:
- (void)setTitle:(NSString *)title
{
_title = title;
_rawData[#"name"] = title;
}
- (NSString *)title
{
if (!_title) {
_title = _rawData[#"name"];
}
return _title;
}
If I comment out NSString *_title; in the extension, the compiler says it can't find _title in the first line of the setter, and wherever it occurs in the getter. The getter used to work just fine, though, even without the redeclaration.
If you declare a property and then override both the getter and setter, it won't auto-synthesize the property. But you can just add a line to synthesize it to your implementation:
#synthesize title = _title;
As for having a property be an immutable type, and its backing instance variable be mutable, you're going to have an issue when from outside your class the immutable type is assigned to it, and you treat it as the mutable version, because it won't respond to the methods to mutate it. For example, you assign an NSArray to a variable, then try to treat it as an NSMutableArray, it won't work.
If you implement a getter, the compiler doesn't automatically create an ivar.
This is for a good reason. The property may (and, in my experience, usually is) created on request and returned, so in that case no instance variable is needed to store it and it would add a significant memory overhead to classes with a large number of such properties if every getter had an associated ivar.
One other comment. This:
NSMutableDictionary *_rawData;
// ...
#property (strong, nonatomic) NSDictionary *rawData;
May cause you problems. If rawData is set with an immutable dictionary, it will raise an exception when you attempt to mutate it later. Make sure you copy it on assign using -mutableCopy. (I assume you aren't copying it because it's marked strong, not copy. If you are, it's fine)
When you override the setter and getter (not just the getter), Xcode assumes you want complete control and doesn't create the backing store (the _title). You have to do it yourself with
#synthesize title = _title
If you implement a getter and a setter for a read-write property, or a getter for a read-only property then Clang (Xcode) will not synthesise the backing instance variable - see Apple's Encapuslating Data, note in the section You Can Implement Custom Accessor Methods.
You are implementing both the setter and the getter so you must provide your own instance variable if needed.

How to access super class's variables in init method

Super class Resource
#interface Resource : CoderObject
#property (strong, nonatomic) NSString *resourceID;
#property (assign, nonatomic) ResourceType resourceType;
#property (assign, nonatomic) DataType dataType;
#end
Subclass ViewResource
#interface ViewResource : Resource
#property (strong, nonatomic) CustomView *view;
#property (strong, nonatomic) UIViewController *viewController;
#end
In subclass ViewResource's init method how to access Resource's variable dataType? Now I'm trying to just use super.dataType = ...
Is there other ways?
You just need to use self.dataType. Your subclass has full visibility of all of the superclass properties defined in the .h file. Using self.xxx also gives you the ability to override the accessor methods if required in the future without coming back to edit all of your usage code.
Looking at your link below, fair enough. Those are all valid points. Accessors shouldn't have side effects but you can't guarantee they won't. If the property is defined the superclass then you have a couple of options:
Use self.xxx to set the property and endeavour to ensure no side effects
Call an init method on super, passing the required parameters, and set them there
Like Wain stated in his answer you have direct access to your super's class members (if they are not private).
And there is no problem calling self.property in the init method as long as your init looks like this
-(id)initAndTheNameYoWantAndMaybeSomeParameters:(NSString *)paramExample {
self = [super initNameOfAnInitMethodFromSuperClass];
//check if the init was with success
if(self != nil) {
self.myStringProp = paramExample;
//or
self.propertyFromSuper = paramExample;
}
}
Yes, you can also do stupid stuff in the initMethods (I did it before :)) ) like calling the same initMethod from inside it which was generating a recursive calling that was crashing my app. (Easy to spot this issue)

Should I make my ivars properties in iOS?

Just when you think you understand something, you don't! :)
I understand that if I make a variable a property, I can access it anywhere in the Class and even set it from outside that class.
I thought if I didnt need it I could just make it an ivar. So I have a viewcontroller with about 5 UILabels. So in its viewDidLoad I say:
pharmacyName.text = self.receivedLocation.name;
pharmacyTel1.text = #"556-7843";
pharmacyTel2.text = #"991-2345";
pharmacyTel3.text = #"800-0001";
When I have declared them like so in the .h file:
#interface DetailViewController : UIViewController{
IBOutlet UILabel *pharmacyName;
IBOutlet UILabel *pharmacyTel1;
IBOutlet UILabel *pharmacyTel2;
IBOutlet UILabel *pharmacyTel3;
}
#property (nonatomic,strong) MyLocation *receivedLocation;
#end
No. Its not mandatory to create ivar as property. If you don't want to access it outside of class just use as it is. In ARC you can also declare your IBOutlet as below:
#interface DetailViewController : UIViewController{
__weak IBOutlet UILabel *pharmacyName;
__weak IBOutlet UILabel *pharmacyTel1;
__weak IBOutlet UILabel *pharmacyTel2;
__weak IBOutlet UILabel *pharmacyTel3;
}
This will keep a week reference of outlets. Here is detail of __weak and strong
There are always many ways you can approach programming tasks and standards. Our group has started using a few coding standards. We like to put our instance variables that are NOT accessed from outside the class (and protocol statements) in the private interface in the .m file like this:
#interface DetailViewController() {
NSString *value_;
}
#end
We also like to use #property for our instance ivars and declare those in the private interface as well like this:
#interface DetailViewController() {
}
#property (nonatomic, strong) IBOutlet UIlabel *pharmacyName;
#end
and then in your code, you would refer to this as self.pharmacyName. It seems to work pretty well with autocomplete, and with getting and setting. Also when you have thread safety issues, the nonatomic, strong behavior comes in handy.

Singleton changing integer

I have a singleton I implement in this way:
PhotoViewController* sharedSingleton = [PhotoViewController sharedManager];
I know that to launch a method I have to do [sharedSingleton method];
but what if I want to change an integer declared in the PhotoViewController.h file as NSInteger* tagNumber, hoe can I do that? I tried this:
[sharedSingleton.tagNumber = 1];
but it doesn't work!
EDIT:
error: property tagNUmber not found on object of type photoViewController
#interface PhotoViewController : UIViewController{
BOOL newMedia;
UIPopoverController *popoverController;
DBRestClient *restClient;
NSInteger* tagNumber;
}
+ (PhotoViewController *) sharedManager;
#end
Singletons are regular objects. The only difference is that only one instance will be created from the class.
If you aren't able to set the tagNumber it is likely that some other type of coding error is happening... perhaps the tagNumber property was declared in a class extension, making the accessor/mutator methods private?
If you edit your question with how the tagNumber is declared, and also include the error message you are getting, I'll be able to edit this answer and give you more specific advice.
EDIT: ...and yes, definitely double check to make sure you didn't declare the NSInteger to be a pointer... an NSInteger is a scalar type (so it takes a direct value, and doesn't use the dereference '*' operator).
I suggest using properties instead of accessing the instance variables directly:
#interface PhotoViewController : UIViewController
#property (nonatomic, assign) BOOL newMedia;
#property (nonatomic, strong) UIPopoverController *popoverController;
#property (nonatomic, strong) DBRestClient *restClient;
#property (nonatomic, assign) NSInteger tagNumber;
+ (PhotoViewController *) sharedManager;
#end
Then set the variable without the brackets as:
sharedSingleton.tagNumber = 1;

Xcode4 - Declaring an object in header file

For example one, I declare an object inside the interface brace {} like:
#interface testViewController : UIViewController {
IBOutlet UILabel * myLabel;
}
#property (retain, nonatomic) UILabel *myLabel;
#end
and example two, I declare an object outside the inferface brace {} like:
#interface testViewController : UIViewController {
}
#property (retain, nonatomic) IBOutlet UILabel *myLabel;
#end
I run the code and the result is the same, so I want to ask what is the different for decalare an object inside or outside the interface brace {}?
Thanks
The modern Objective-C runtimes (64-bit Mac OS X and iOS) will generate the backing store for your declared properties when you #synthesize them. So you don't need to declare them within the braces.
If you are declaring an iVar that is not a property and will only be used by the class, then they need to be declared. It's a good idea to mark these #private e.g
#interface MyClass : NSObject {
#private
NSString *privateString;
}
#property (nonatomic, copy) NSString *publicString; // be sure to #synthesize this
#end
In the second example you only declare a property. Xcode will declare object automatically.

Resources