Ok, so I'm using Objective-C. Now, say I have:
TopClass : NSObject
- (int) getVal {return 1;}
MidClass : TopClass
- (int) getVal {return 2;}
BotClass : MidClass
- (int) getVal {return 3;}
I then put objects of each type into an NSMutableArray and take one out. What I want to do is run the getVal func on the appropriate object type, but when I put
id a = [allObjects objectAtIndex:0];
if ([a isKindOfClass:[TopClass class]])
{
int i;
i = [a getVal];
}
I get firstly a warning about multiple methods called getVal (presumably because the compiler can't determine the actual object type until runtime). But more seriously I also get an error "void value not ignored as it should be" and it won't compile.
If I don't try and use the return from [a getVal] then it compiles fine e.g.
[a getval]; //obviously no good if I want to use the return value
It will also work if I use isMemberOfClass statements to cast the object to a class before running the function e.g.
if ([a isMemberOfClass:[BotClass]) i = [(BotClass*) a getVal];
But surely I shouldn't have to do this to get the functionality I require? Otherwise I'll have to put in a statement for every single subclass, and worse have to add a new line if I add a new sub class, which rather defeats the point of method overriding doesn't it?
Surely there is a better way?
Since a BotClass is a MidClass and a MidClass is a TopClass, you could just set the type of a to TopClass*.
TopClass* a = [allObjects objectAtIndex:0];
if ([a isKindOfClass:[TopClass class]]) {
int i;
i = [a getVal];
}
The better way is to add -getVal to the #interface and #import it. Then the compiler will know such this method is likely to return an int and won't complain even if a is an id. Make sure the method name won't coincide with others, though.
(BTW, in ObjC, getters won't be named as -getFoo. The convention is just call it -foo.)
Your code compiles without any warning:
#import <Foundation/Foundation.h>
#interface TopClass : NSObject
{
}
- (int) getVal;
#end
#interface MidClass : TopClass
{
}
- (int) getVal;
#end
#interface BotClass : MidClass
{
}
- (int) getVal;
#end
#implementation TopClass
- (int) getVal {
return 1;
}
#end
#implementation MidClass
- (int) getVal
{
return 2;
}
#end
#implementation BotClass
- (int) getVal
{
return 3;
}
#end
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *array = [NSMutableArray array];
[array addObject:[[[TopClass alloc]init]autorelease]];
[array addObject:[[[MidClass alloc]init]autorelease]];
[array addObject:[[[BotClass alloc]init]autorelease]];
for (int objectNumber = 0; objectNumber < [array count]; objectNumber++) {
id a = [array objectAtIndex:objectNumber];
if ([a isKindOfClass:[TopClass class]])
{
int i = [a getVal];
NSLog(#"%d",i);
}
}
[pool drain];
return 0;
}
The test "isKindOfClass" is always true in this case, as MidClass and BotClass inherit from TopClass.
Are you sure the compiler knows about the method signatures that you call?
Related
I have a superclass and a subclass. I can access the variable some_property (declared in the superclass) via self.some_property in the subclass.
However if I try to access the instance variable directly with _some_property, I'll get the error 'Use of undeclared identifier _some_property...'.
Using #synthesize some_property = _some_property silences this warning.
Whats going on when I re-synthesize the property?
You are creating another ivar named _some_property — and also overriding the getter method to return the value of this new ivar. The compiler gives you an error about this if the base class's #implementation (i.e. the implicit declaration of its _some_property ivar) is visible at the site of the #synthesize in the subclass.
(By the way, don't do this!)
You can demonstrate to yourself by inspecting the Obj-C runtime:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface Base : NSObject
#property id foo;
#end
#interface Derived : Base
#end
#implementation Derived
#synthesize foo=_foo; // the compiler doesn't know about Base's _foo yet, so this is OK...
- (instancetype)init {
if ((self = [super init])) {
_foo = #"I'm derived";
}
return self;
}
#end
#implementation Base // after Derived to avoid the error
- (instancetype)init {
if ((self = [super init])) {
_foo = #"I'm base";
}
return self;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
Derived *obj = [Derived new];
NSLog(#"getter returns %#", obj.foo);
unsigned int count = 0;
// Examine Base ivars
NSLog(#"Base ivars:");
Ivar *ivars = class_copyIvarList([Base class], &count);
for (unsigned int i = 0; i < count; i++) {
NSLog(#" %s = %#", ivar_getName(ivars[i]), object_getIvar(obj, ivars[i]));
}
// Examine Derived ivars
NSLog(#"Derived ivars:");
ivars = class_copyIvarList([Derived class], &count);
for (unsigned int i = 0; i < count; i++) {
NSLog(#" %s = %#", ivar_getName(ivars[i]), object_getIvar(obj, ivars[i]));
}
}
return 0;
}
Output:
getter returns I'm derived
Base ivars:
_foo = I'm base
Derived ivars:
_foo = I'm derived
I'm currently working on a project that's split in two classes, the class 'Array' and the class 'PPCalcVals'. Because other classes, that will be added, will also have to have access the array, I thought it would be the best to write the array class, containing an NSMutableArray and Subclass all the others (beginning with the PPCalcVals class.
So the 'PPCalcVals' class needs to access the elements of the array in the superclass 'Array'.
(Please correct me if this is the wrong approach).
As mentioned the whole program is written and well working in C but to create a GUI and eventually an OSX or IOS application I started to learn OOProgramming with Objecitve C.
Anyways, when I reference the objects in the array of the superclass the only value that gets printed is "null" which is not really what I want.
Here is the code:
main routine:
#import <Foundation/Foundation.h>
#import "Array.h"
#import "PPCalcVals.h"
int main(int argc, const char * argv[])
{
#autoreleasepool
{
Array *prices = [[Array alloc]initWithName:#0];
PPCalcVals *myVals = [[PPCalcVals alloc]init];
[prices addValue:#12];
[prices addValue:#13];
[prices addValue:#14];
[prices addValue:#15];
[prices addValue:#15];
[prices print];
[myVals print];
}
return 0;
}
array.h file:
#import <Foundation/Foundation.h>
#interface Array : NSObject
{
NSMutableArray *prices;
}
-(id) initWithName: (NSNumber *) values;
-(void) addValue: (NSNumber *) value;
-(void) print;
-(NSMutableArray *) prices;
#end
array.m
#import "Array.h"
#implementation Array
-(id) initWithName:(NSNumber *)values
{
self = [super init];
if(self)
{
prices = [NSMutableArray array];
}
return self;
}
-(void) addValue: (NSNumber *) value
{
[prices addObject:value];
}
-(void) print
{
NSLog(#"%#",prices);
}
-(NSMutableArray *)prices
{
return prices;
}
#end
PPCalcVals.h:
#import "Array.h"
#interface PPCalcVals : Array
#property id high,low,open,close;
-(void) setHigh:(NSMutableArray *)h setLow:(NSMutableArray *)l; //set high and low
-(void) setOpen:(NSMutableArray *)o setClose:(NSMutableArray *)c; //set open and close
-(void) sort; //sort array
-(void) print; //debugging tool
#end
PPCalcVals.m:
#import "PPCalcVals.h"
#implementation PPCalcVals
#synthesize high,low,open,close;
-(void) setOpen:(NSMutableArray *)o setClose:(NSMutableArray *)c
{
o = prices[0];
c = prices[2];
open = o;
close = c;
}
-(void) sort;
{
[prices sortedArrayUsingComparator:^(NSString *str1, NSString *str2) {
return [str1 compare:str2 options:NSNumericSearch];
}];
}
-(void) setHigh:(NSMutableArray *)h setLow:(NSMutableArray *)
{
h = prices[0];
l = prices[2];
high = h;
low = l;
}
-(void) print
{
NSLog(#"open: %#",open);
NSLog(#"close: %#",close);
NSLog(#"high: %#",high);
NSLog(#"low: %#",low);
}
#end
The running program outputs only:
2013-08-05 10:21:08.546 prog1[1314:303] (
12,
13,
14,
15,
15
)
open: (null)
close: (null)
high: (null)
low: (null)
I realize that this is probably a really basic question but I would appreciate your help a lot and I already thank you if you read until this point ;)
You have created two distinct, completely unrelated objects prices and myVals:
Array *prices = [[Array alloc]initWithName:#0];
PPCalcVals *myVals = [[PPCalcVals alloc]init];
What you probably meant is to create a PPCalcVals object (which inherits
all methods from the superclass Array):
PPCalcVals *prices = [[PPCalcVals alloc]init];
[prices addValue:#12]; // invokes `addValue` method from superclass "Array"
// ...
[prices print]; // invokes `print` method from "PPCalcVals"
You also have to implement init in the PPCalcVals class in such a way
that it calls the "designated initializer" initWithName in the superclass.
I have one question. Where did you make calls for the functions
-(void) setHigh:(NSMutableArray *)h setLow:(NSMutableArray *)l;
-(void) setOpen:(NSMutableArray *)o setClose:(NSMutableArray *)c;
Because all I can see is that these functions are not called. So the properties high, low, open & close are not yet assigned and thus they return Null.
Also may I know the reason why are you overriding the parameters inside above mentioned functions?
o = prices[0];
c = prices[2];
and
h = prices[0];
l = prices[2];
I have a class named person, with two values: age and weight.Why can't I access these two values in the main function like this:
int a=[chuck age];
int b=[chuck weight];
What is the best way to do that? Using properties is the correct way?
Header file:
#import <Foundation/Foundation.h>
#interface person : NSObject
{
int age;
int weight;
}
-(void) print;
-(void) setAge;
-(void) setWeight;
#end
Implementation file:
#import "person.h"
#implementation person
-(void) print
{
printf("Your age is %d and your weight is %d.", age, weight);
}
-(void) setAge
{
printf("Write age: ");
int v;
scanf("%d", &v);
age=v;
}
-(void) setWeight
{
printf("Write weight: ");
int g;
scanf("%d", &g);
weight=g;
}
#end
Are you working from some kind of a tutorial or book? That is an odd place to start for learning to write OS X or iOS apps.
In any case, the issue is that you've colluded getter/setter stuff with methods that implement other functionality.
I would suggest that your Person class be declared as:
#interface Person : NSObject
#property NSInteger age;
#property NSInteger weight;
#end
With Person.m:
#implementation Person
- (id) init {
self = [super init];
if (self) {
// preposterous initial values so we know if they weren't set.
_age = -1;
_weight = -1;
}
return self;
}
#end
That is, a Person only holds information about a single person. It does not do any kind of I/O, etc...
Then, your main.m would look something like:
#import <Foundation/Foundation.h>
#import "Person.h"
NSInteger ScanIntegerWithPrompt(NSString *prompt) {
printf("%s: ", [prompt UTF8String]);
int v;
scanf("%d", &v);
return (NSInteger) v;
}
int main(...) {
#autoreleasepool {
Person *p = [[Person alloc] init];
p.age = ScanIntegerWithPrompt(#"Enter age:");
p.weight = ScanIntegerWithPrompt(#"Enter weight:");
printf("Your age is %d and your weight is %d", p.age, p.weight);
}
return 0;
}
Structuring the code this way separates the Model -- the data container -- from the Control layer. There isn't much of a View layer here.
If you really wanted to keep the I/O / parse logic with the Person object, then add something like this to the Person object:
...
- (NSNumber)readIntegerWithPrompt:(NSString*)prompt
{
... same code as function above ...
}
- (void)readAgeFromStandardInput
{
self.age = [self readIntegerWithPrompt:#"Enter age: "];
}
- (void)readWeightFromStandardInput
{
self.weight = [self readIntegerWithPrompt:#"Enter weight: "];
}
...
Then you'd call those methods from your main.
Your problem is You're trying to access private age and weight ivars, which aren't accessible this way.
The good way to do this is to use ObjC properties, but this is not required for your example.
You need to create two methods to access the private ivars, call them age and weight, they should look like this in the class interface:
- (int) age;
- (int) weight;
and the implementation is:
- (int) age{
return age;
}
- (int) weight{
return weight;
}
Now in your main.m you can easily access the data needed like this:
#import <Foundation/Foundation.h>
#import "person.h"
int main(int argc, char *argV[]) {
#autoreleasepool {
person *andrew = [[person alloc]init];
[andrew setAge];
[andrew setWeight];
NSLog(#"Age:%d, Weight:%d",[andrew age], [andrew weight]);
}
return 0;
}
If you want to know how it's done with properties please let me know and I can update the answer :)
In your header file :
#property int age;
#property int weight;
Im getting the error incompatible integer to pointer conversion returning unsigned int from a function with result type NSUInteger *
I'm not sure what is that mean since they are kind of the same, no..? sorry im totally a newbie, here is my code for my PlayingCards deck class:
PlayingCards.h
#import "Card.h"
#interface PlayingCards : Card
#property (strong, nonatomic) NSString *suit;
#property (nonatomic) NSUInteger rank;
+(NSArray *) validSuit;
+(NSUInteger *) maxRank;
#end
PlayingCards.m
#import "PlayingCards.h"
#implementation PlayingCards
#synthesize suit = _suit;
//modifying the contents getter so it will return array with the ranks and rank+suit
-(NSString *) contents {
NSArray *cardsRank = [PlayingCards rankStrings];
return [cardsRank[self.rank] stringByAppendingString:self.suit];
}
//creating a method to make sure we get validated suits
+(NSArray *) validSuit {
return #[#"♠",#"♣",#"♥",#"♦"];
}
//creating calss method to validate the rank
+(NSArray *) rankStrings {
return #[#"?",#"A",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K"];
}
//creating a new setter for suit to make sure we get the valitated suits, uding the validateSuit method
-(void) setSuit:(NSString *)suit {
if ([[PlayingCards validSuit] containsObject:suit]) {
_suit = suit;
}
}
//creating new getter for suit to make sure its not empty
-(NSString *) suit {
return _suit? _suit: #"?";
}
//creating a class method to make sure when user set the rank he will will
+(NSUInteger *) maxRank {
return [self rankStrings].count - 1;
}
//creating a new setter to the renk to make sure the rank is validates
-(void) setRank:(NSUInteger)rank {
if (rank <= [PlayingCards maxRank]) {
_rank = rank;
}
}
#end
please help me to figure this out, its in the line (in the .m file):
+(NSUInteger *) maxRank {
return [self rankStrings].count - 1;
}
And effecting on the line:
-(void) setRank:(NSUInteger)rank {
if (rank <= [PlayingCards maxRank]) {
_rank = rank;
}
}
With another error
order comparison between pointer and integer
thanks!
This usually means you have tried to accidentally use a pointer where you should not. Or didn't use a pointer where you should have.
Your class method is returning a pointer to your NSUInteger. Remove the *
I have the following class in my iOS application (it is like an abstract class from the Java world).
#implementation WSObject
static NSDictionary* _dictionary = nil;
+(NSDictionary*) dictionary {
if (_dictionary == nil) {
_dictionary = [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
}
return _dictionary;
}
...
#end
I then have multiple classes which implement this above WSObject with the class method dictionary. The problem is, that each of these classes should have their own _dictionary, but they are all sharing the same object from the super class. I could, of course, copy to all the subclasses, but that would break the reusability. Besides this getter, there are other class methods in WSObject which mutate the dictionary. Because of this, there would be a several class methods which should be in every subclass.
How can I solve this in a smart way? Please tell me if my description is insufficient.
Associative references seem like they'll do the trick. You can essentially tack some storage on to the class object itself. (I'm using NSStrings here, in place of the dictionaries you want to use, just for demonstration.)
Superclass:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface Stuper : NSObject
// Accessor method for the "class variable"
+ (NSString *) str;
// Analog to your +localStorePath
+ (NSString *) quote;
#end
#import "Stuper.h"
// The doc suggests simply using the address of a static variable as the key.
// This works fine, even though every class is (as in your problem) using
// the same key, because we are associating to a different class each time.
static char key;
#implementation Stuper
+ (NSString *) str {
NSString * s = objc_getAssociatedObject(self, &key);
if( !s ){
s = [self quote];
// You'll probably want to use OBJC_ASSOCIATION_RETAIN for your dictionary.
// self inside a class method is the class object; use that as
// the associator. The string is now tied to the associator, i.e.,
// has the same lifetime.
objc_setAssociatedObject(self, &key, s, OBJC_ASSOCIATION_COPY);
}
return s;
}
+ (NSString *) quote {
return #"It was the best of times, it was the worst of times.";
}
#end
Subclass:
#import "Stuper.h"
#interface Stub : Stuper #end
#import "Stub.h"
#implementation Stub
+ (NSString *) quote {
return #"Call me Ishmael.";
}
#end
Trying this out:
#import <Foundation/Foundation.h>
#import "Stuper.h"
#import "Stub.h"
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog(#"%#", [Stuper str]);
NSLog(#"%#", [Stub str]);
[pool drain];
return 0;
}
Each class object now has its own string, associated with it.
2011-12-05 23:11:09.031 SubClassVariables[36254:903] It was the best of times, it was the worst of times.
2011-12-05 23:11:09.034 SubClassVariables[36254:903] Call me Ishmael.
The only downside here is that you'll have to call the accessor method every time you want the object; you don't have a pointer you can use directly. You can call objc_getAssociatedObject in the superclass as an accessor, too, of course, since it has access to key.
In order to give each subclass its own dictionary, store a second dictionary object in your primary dictionary using the class name as the key. For example:
static NSMutableDictionary *_dictionary = nil;
+ (NSDictionary*)dictionary
{
if (_dictionary == nil)
_dictionary = [[NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]] mutableCopy];
NSString *key = NSStringFromClass( [self class] );
if ( [_dictionary objectForKey:key] == nil )
[_dictionary setObject:[NSMutableDictionary dictionary] forKey:key];
return [_dictionary objectForKey:key];
}
Perhaps you can return a copy of the dictionary
#implementation WSObject
static NSDictionary* _dictionary = nil;
+(NSDictionary*) dictionary {
if (_dictionary == nil) {
_dictionary = [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
}
return [_dictionary copy];
}
...
#end
Keep in mind that if you modify _dictionary you will get a copy of that modified dictionary which may differ from what is on disk.
How often is this being called? is it really necessary to cache the file contents in this static _dictionary object?
Why not just fetch it every time form disk, assuming it isn't too often that performance comes into question.
#implementation WSObject
+(NSDictionary*) dictionary {
return [NSKeyedUnarchiver unarchiveObjectWithFile:[self localStorePath]];
}
...
#end