Trying to override init method (to create instance with already initialized tag) in class and getting exception. Code sample:
#interface DiagnosticsReport : NSObject {
}
#property NSString *tag;
- (void) initWithTag:(NSString*) tag;
#end
#implementation DiagnosticsReport
- (id) initWithTag:(NSString*) tag {
if (self = [self init]) {
_tag = tag;
}
return self;
}
- (id) init {
if (self = [super init]) {
// default init here
}
return self;
}
Your declaration of method returns void, while definition returns id. Change both to return instancetype and you're good to go.
Related
I am learning iOS programming and am confused by the following code regarding the use of keyword self.
From my understanding, self is like Java's this. It refers to the current instance. When I want to call a class method, the usual way should be like [PlayingCard validSuits]; But it's also OK to invade a class method on an instance, right? Like [self validSuits]; (I am in the class so self refers to an instance of PlayingCard)
But in the following code, it gives error somewhere but looks ok elsewhere.(Pointed out by 3 comments, this is within Xcode 5.1)
Am I missing anything?
(P.S. I think I am having the similar problem as here, which no one answered yet. He got the same error even using [PlayingCard validSuits]. )
// PlayingCard.m
#import "PlayingCard.h"
#implementation PlayingCard
#synthesize suit = _suit;
+ (NSArray *)validSuits {
return #[#"♠︎", #"♣︎", #"♥︎", #"♦︎"];
}
+ (NSArray *)rankStrings {
return #[#"?", #"A", #"2", #"3", #"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K"];
}
+ (NSUInteger)maxRank {
return [[PlayingCard rankStrings] count] -1;
//1. [self rankStrings] works fine.**
}
//override super class's method
- (NSString *)contents {
NSArray *rankStrings = [PlayingCard rankStrings];
//2. if change rankStrings to self, then error:
//No visible #interface for 'PlayingCard' declares the selector 'rankStrings'
return [rankStrings[self.rank] stringByAppendingString:self.suit];
}
- (void) setSuit:(NSString *)suit {
if ( [[PlayingCard validSuits] containsObject:suit]) {
//3.error when changed to [self validsuits]
//No visible #interface for 'PlayingCard' declares the selector 'validsuits'**
_suit = suit;
}
}
- (NSString *) suit {
return _suit ? _suit : #"?";
}
#end
The header file:
// PlayingCard.h
#import "Card.h"
#interface PlayingCard : Card
#property (nonatomic, strong) NSString *suit;
#property (nonatomic) NSUInteger rank;
+ (NSArray *) validSuits;
+ (NSUInteger) maxRank;
#end
If you are calling another class method from inside a class method (of the same class) you can just use [self classMethod]. If however you are in an instance method and you need to call that classes class method you can use [[self class] classMethod]
As pointed out by #Martin R - if you subclass PlayingCard, calling self in a class method will then be that subclass and not PlayingCard.
EDIT:
For completeness you need to do:
// PlayingCard.m
#import "PlayingCard.h"
#implementation PlayingCard
#synthesize suit = _suit;
+ (NSArray *)validSuits {
return #[#"♠︎", #"♣︎", #"♥︎", #"♦︎"];
}
+ (NSArray *)rankStrings {
return #[#"?", #"A", #"2", #"3", #"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K"];
}
+ (NSUInteger)maxRank {
return [[self rankStrings] count] -1;
}
//override super class's method
- (NSString *)contents {
NSArray *rankStrings = [[self class] rankStrings];
return [rankStrings[self.rank] stringByAppendingString:self.suit];
}
- (void) setSuit:(NSString *)suit {
if ( [[[self class] validSuits] containsObject:suit]) {
_suit = suit;
}
}
- (NSString *) suit {
return _suit ? _suit : #"?";
}
#end
I want to override an NSString property declared in a superclass. When I try to do it using the default ivar, which uses the the same name as the property but with an underscore, it's not recognised as a variable name. It looks something like this...
The interface of the superclass(I don't implement the getter or setter in this class):
//Animal.h
#interface Animal : NSObject
#property (strong, nonatomic) NSString *species;
#end
The implementation in the subclass:
//Human.m
#implementation
- (NSString *)species
{
//This is what I want to work but it doesn't and I don't know why
if(!_species) _species = #"Homo sapiens";
return _species;
}
#end
Only the superclass has access to the ivar _species. Your subclass should look like this:
- (NSString *)species {
NSString *value = [super species];
if (!value) {
self.species = #"Homo sapiens";
}
return [super species];
}
That sets the value to a default if it isn't currently set at all. Another option would be:
- (NSString *)species {
NSString *result = [super species];
if (!result) {
result = #"Home sapiens";
}
return result;
}
This doesn't update the value if there is no value. It simply returns a default as needed.
to access the superclass variables, they must be marked as #protected, access to such variables will be only inside the class and its heirs
#interface ObjectA : NSObject
{
#protected NSObject *_myProperty;
}
#property (nonatomic, strong, readonly) NSObject *myProperty;
#end
#interface ObjectB : ObjectA
#end
#implementation ObjectA
#synthesize myProperty = _myProperty;
#end
#implementation ObjectB
- (id)init
{
self = [super init];
if (self){
_myProperty = [NSObject new];
}
return self;
}
#end
I define
#property (nonatomic, assign) int currentUserNum;
#property (nonatomic, assign) BOOL isAlive;
in #interface MyClass
and defined -init method in #implementation MyClass
#synthesize currentUserNum, isAlive;
-(id) init {
if (self = [super init]) {
self.currentUserNum = 0;
self.isAlive = YES;
}
return self;
}
self.currentUserNum = 0; is crashed , but self.isAlive = YES; can work ! They are both assign property.
I want to know why ? Thanks!
Your init method is missing a lot of important code.
- (id)init {
if ((self = [super init])) {
_currentUserNum = 0; // it's not wise to reference properties in the init method
}
return self;
}
Every init method should follow this basic pattern. You assign self the value of calling an appropriate super init or other self init. If that's not nil, you then perform appropriate initialization code, and finally you return self.
I have an ivar which is mentioned in my header
#interface MyClass : UIView{
int thistone;}
- (IBAction)toneButton:(UIButton *)sender;
#property int thistone;
#end
and I have synthesized it in the implementation:
#implementation MyClass
#synthesize thistone;
- (IBAction)toneButton:(UIButton *)sender {
if(thistone<4)
{thistone=1000;} // I hate this line.
else{thistone=thistone+1; }
}
I cannot find (or find in any manual) a way to set a nonzero initial value. I want it to start at 1000 and increase by 1 each time I press the button. The code does exactly what I intend, but I'm guessing there's a more proper way to do it that saves me the if/else statement above. Code fixes or pointers to specific lines in online documentation greatly appreciated.
Every object has a variant of the init method called at instantiation. Implement this method to do such setup. UIView in particular have initWithFrame: and initWithCoder. Best to override all and call a separate method to perform required setup.
For example:
- (void)commonSetup
{
thisTone = 1000;
}
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
[self commonSetup];
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
if (self = [super initWithCoder:coder])
{
[self commonSetup];
}
return self;
}
I have a class that has this in the initializer:
#implementation BaseFooClass
-(id) init
{
if (self = [super init])
{
// initialize instance variables that always need to start with this value
}
return self;
}
-(id) initWithSomeInt:(int) someInt
{
if (self = [self init]) // <-- I need to make sure that I am calling BaseFooClass's init here, not SubFooClass's, does that make sense?
{
self.someInt = someInt;
}
return self;
}
#end
That is all fine and dandy. My problem is that when I implement the subclass:
#implementation SubFooClass
-(id) init
{
return [self initWithSomeInt:0];
}
-(id) initWithSomeInt:(int) someInt
{
if (self = [super init]) // <--- Infinite loop (stack overflow :) )
{
// initialize other variables
}
}
#end
I basically need to specifically call the BaseFooClass's init rather than the SubFooClass's init.
I cannot change the way the objects are initialized, as I am converting a project from C# to use in my iPad application.
Thank you all in advance
EDIT:
Due to someone asking, here is my header:
#interface BaseFooClass : NSObject
// implicit from NSObject
// -(id) init;
-(id) initWithSomeInt:(int) someInt;
// more methods
#end
#interface SubFooClass : BaseFooClass
// implicit from NSObject
// -(id) init;
// implicit from BaseFooClass
//-(id) initWithSomeInt:(int) someInt;
#end
Objective-C doesn't work this way because of the way the runtime converts methods into function calls. Self is always an instance of the allocated class, even when invoking the super-class's methods. You need to create your designated initializer for your BaseClassFoo and always go there. So you should be doing something like this:
#implementation BaseFooClass
-(id) init
{
return [self initWithSomeInt:0]; // redirect super class's designated initializer
}
-(id) initWithSomeInt:(int) someInt
{
if ((self = [super init])) // Designated initializer always calls into super class's designated initializer (in this case, NSObject's designated initializer is init
{
self.someInt = someInt;
}
return self;
}
#end
#implementation SubFooClass
// Here we don't override init because our super class's designated initializer
// is initWithSomeInt:
// -(id) init
// {
// return [self initWithSomeInt:0];
// }
// we override this because it's our superclass's designated initializer, plus it
// is ours as well
-(id) initWithSomeInt:(int) someInt
{
if ((self = [super initWithSomeInt:someInt]))
{
// initialize other sub-class specific variables
}
}
#end
You have to call [super initWithSomeInt:someInt]; in the init method of your SubFooClass.
;)
ADDED:
I think is weird you try to call init within iniWithSomeInt . The usual thing would be to call [super initWithSomeInt:someInt] in initIthSomeInt method of SubFooClass and change what you need inside the if clause.