I've tried searching google and this site regarding my question but found no answer.
I'm a beginner with Obj-C and would like this question answered.
What is the benefit of using parameters in my methods.
for example..
-(id)initWithName:(NSString *)newName atFrequency:(double)newFreq {
self = [super init];
if (self != nil) {
name = newName;
frequency = newFrequency;
}
return self;
}
versus
-(void)myMethod {
self = [super init];
if (self != nil) {
name = newName;
frequency = newFrequency;
}
return self;
}
I understand that the -(void) means the method has no return type, and the -(id) means that the first method has 'id' as a return type, and 'id' is generic....
can anyone help explain? I hope my question makes sense, thank you all for your help.
Parameters are inputs to a method, just like function/method parameters in any language. In your second example, on the line frequency = newFrequency;, where is newFrequency supposed to come from?
In other languages, where you might have something like
void initWithName(string newName, double newFreq);
In Obj-C the equivalent is
- (void)initWithName:(NSString *)newName atFrequency:(double)newFreq;
The difference is that in Obj-C, there is an extra piece of the method name for each parameter (like the atFrequency) — in this case, the method name is initWithName:atFrequency:, not just initWithName:.
(This is actually optional, you only have to have a : for each parameter. Technically initWithName:: is still a valid method name, but that's not considered good practice in Obj-C.)
See also:
How do I pass multiple parameters in Objective-C?
Is there a language out there in which parameters are placed inside method name?
Related
I'm currently trying to learn Objective C and by this way, Oriented Object languages.
I'm declaring variables from a class I've written, but, my functions are way too long, and I'd like to cut that code off.
I do not know how the return works with classes and that's my problem.
Grapejuice *juice;
juice = [[Grapejuice alloc] init];
[juice setName:#"Grape juice"];
[juice setOrderNumber:1000];
[juice setPrice:1.79];
This, is part of a main in which I'm doing this to several objects, how can I do that in a separated function, and still got these informations out of this new function to be re-used later (to be printed for example) ?
Not sure if I'm clear but I've just started learning it yesterday, still hesitating on the basics.
Thanks homies.
If I understand you correctly, I believe what you want is a custom "init" method for your Grapejuice class.
In Grapejuice.h, add:
- (instancetype)initWithName:(NSString *)name orderNumber:(NSInteger)number price:(double)price;
In Grapejuice.m, add:
- (instancetype)initWithName:(NSString *)name orderNumber:(NSInteger)number price:(double)price {
self = [super init];
if (self) {
_name = name;
_orderNumber = number;
_price = price;
}
return self;
}
Then to use that code you do:
Grapejuice *juice = [[Grapejuice alloc] initWithName:#"Grape Juice" orderNumber:1000 price:1.79];
Please note that you may need to adjust the data types for the orderNumber and price parameters. I'm just guessing. Adjust them appropriately based on whatever type you specified for the corresponding properties you have on your Grapejuice class.
Suppose I have a class BasicDate, and a subclass of BasicDate called EuroDate. The difference between the classes is month-day-year versus day-month-year. I know it'd probably be better to just have methods on the same class to output them differently... but that's not the point of this question.
BasicDate has the following init method:
-(id)initWithMonth:(int)m andDay:(int)d andYear:(int)y {
if(self = [super init]) { /*initialize*/ } return self;
}
And the matching factory method then looks like this:
+(BasicDate)dateWithMonth:(int)m andDay:(int)d andYear:(int)y {
return [[BasicDate alloc] initWithMonth: m andDay: d andYear: y];
}
But if my subclass, EuroDate which would use a factory method more like this:
+(EuroDate)dateWithDay:(int)d andMonth:(int)m andYear:(int)y {
return [[EuroDate alloc] initWithDay: d andMonth: m andYear: y];
} //we can assume that EuroDate includes this init method...
This is all fine. Now, we assume that both classes have their own description method, which will print MMDDYYYY for BasicDate, but DDMMYYYY with EuroDate. This is still all fine.
But if I do this:
EuroDate today = [EuroDate dateWithMonth:10 andDay:18 andYear:2013];
This will call the BasicDate factory method that EuroDate has inherited. The problem is, remember how BasicDate's factory method looks? return [[BasicDate alloc] ...]
So today polymorphs into a BasicDate despite me wanting to store it as a EuroDate, so if I call the description method, it will print 10182013 rather than 18102013.
There are two solutions to this problem I have found.
Solution 1: Change BasicDate's factory method. Rather than return [[BasicDate alloc] ..., I can instead do return [[[self class] alloc] ...] This works and will allow me to use this method for BasicDate or any of BasicDate's subclasses and it will return the right object type.
Solution 2: Override the factory method. Whether I override it to throw an exception or override it to do return [[EuroDate alloc] ...]. The problem with overriding it is that I have to override every factory method for every subclass.
Which is better? What are some downsides to the two possible solutions that I may be missing? What is considered the standard way of handling this issue in Objective C?
You should generally use [[[self class] alloc] init...] in factory methods to ensure that they create instances of the correct class. Note that class isn't a property (and in fact, there's no such thing as a 'class property') so the use of dot syntax there is inappropriate.
Edit
And as pointed out by #ArkadiuszHolko (and Rob, thanks), you should now use instancetype rather than id for the return value, to get the benefits of strong typing while maintaining type flexibility for subclasses. And by the way, Apple's naming conventions suggest avoiding using the word 'and' in method names. So consider rewriting your convenience method like so:
+ (instancetype)dateWithMonth:(int)month day:(int)day year:(int)year
{
return [[self alloc] initWithMonth:month day:day year:year];
}
I have general question about designated initializer. I have a some class and from there i want to call a initializer, but before i started to fill my #properties with passing data i want to make data default. For example:
-(id)initWithDefault:(NSDictionary*)defaultTemplate
{
self = [super init];
_fontColor = [defaultTemplate objectForKey:#"color"];
_fontSize = [[defaultTemplate objectForKey:#"size"] intValue];
return self;
}
-(id)initWithTemplate:(NSDictionary*)template
{
self = [self initWithDefault:myDefaultTemplate];
//now i doing something with my template
return self;
}
Is this is a way to prevent null #properties? It this a correct use of designated initializer? Of course you can assume that myDefaultTemplates is not null, and has not null object in keys.
This seems fine with me. I would use the safe way (presented below), but otherwise your code is fine.
-(id)initWithTemplate:(NSDictionary*)template
{
if(self = [self initWithDefault:myDefaultTemplate]) {
//now i doing something with my template
}
return self;
}
Your implementation is perfectly fine, given the fact that _fontColor and _fontSize variables are your local variables of properties.
adig's suggestion is just an enhancement on what you already have implemented. This check takes care of the situation, when your object does not get allocated due to any reason.
Why does the Clang Static Analyzer (CSA) output the following message:
Although the value stored to 'self' is
used in the enclosing expression, the
value is never actually read from
'self'
for the following method:
- (id)init
{
return (self = [super initWithStyle:UITableViewStyleGrouped]);
}
The code works as expected, so I'm wondering whether the code is incorrect from a technical point-of-view, this is a bug within CSA or I'm simply missing something very obvious.
FYI, I'm using this pattern because I don't want the class creating an instance of this class to be able to specify the table style.
A more "proper" way to do this would be as follows:
- (id)init
{
self = [super initWithStyle:UITableViewStyleGrouped];
return self;
}
And it should satisfy the static analyzer
edit:
My best guess as to why Clang doesn't like that line:
When you write (self = [super initWithStyle:UITableViewStyleGrouped]), the result of the init call is stored into a temporary variable, which is then copied into self, and then it is that temporary variable that is actually returned from the method.
Although this is perfectly legal and normal behaviour (and will not break your app), the static analyzer (correctly) notices that the value stored in self is never actually read.
To illustrate, the following code:
- (id)init
{
id temp = [super initWithStyle:UITableViewStyleGrouped];
self = temp;
return temp;
}
Throws the same static analyzer error.
It's telling you that the self = part is unnecessary. It's not incorrect in sense of "broken or dangerous," but in that it's pointless. The variable self is just never used, so there's no point in assigning to it. It could be simply written as return [super initWithStyle:UITableViewStyleGrouped]; without any problem.
I've read in many places that you should always initialize Objective-C objects like so:
- (id) init {
if (self = [super init]) {
....
}
return self;
}
Because the super's init method may return a separate object from the current self.
Now I'm trying to do something like this, and I'm not sure if I have it right, vis-a-vis how retaining and releasing should work:
- (id) init:(int)idx {
id obj = [Cache findSelf:idx];
if (obj) {
[self release];
self = [obj retain];
} else {
self = [self doLoad];
}
return self;
}
I'm mostly curious if this is the correct way to do the retaining and releasing of self and obj. Is there a better way?
You're correct about the self = [super init] part, since some Cocoa classes actually do return a different object than the one that was allocated. However, this is the exception rather than the rule, and doing so in your own code should be exceedingly rare or not done at all. Although it may be tempting to intercept -init calls, you'd be going against the grain of established convention and what Objective-C programmers expect the code to do.
This type of -init method is generally a bad approach, since -init methods should be as straightforward as possible, and should really be concerned with initializing the object. I'd probably write a convenience method like this:
+ (id) instanceForIndex:(NSUInteger)index {
id obj = [Cache findSelf:index];
if (obj == nil) {
obj = [[self alloc] init];
// Add to cache
}
return [[object retain] autorelease];
}
Then call this method instead of -init. This will make the -init logic much cleaner.
Also, I'm not sure what your Cache class does, but it could be worth rethinking that implementation, and using a hidden static variable to store instances (for example, an NSMutableDictionary, where the key is an NSNumber created from the index). This SO question may be of use.
I agree with Quinn that you should use a convenience class method. Still, I think that your init method is mostly correct, except in your else clause you need to call the parent initializer, i.e. self = [super init].