Why does the object in this Objective C code not set? - objective-c

I have the below code which simply read a bunch of cards and inputs their names into a Mutable Array if they are not present beforehand.
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSMutableArray *allCards = [[NSMutableArray alloc] init];
char cardAvailable = '\0';
do {
Card *newCard = [[Card alloc] init];
NSLog(#"What is the Card Name ?\n");
char cName[20];
scanf("%s",cName);
NSString *nName = [NSString stringWithCString:cName encoding:1];
[newCard setName:nName];
if([newCard wasMatch:allCards]) {
NSLog(#"Card Already Present");
} else {
NSLog(#" NEW CARD NAME %# %# %s",[newCard getName],newCard.name, cName);
[newCard addGlobal:newCard toArray:allCards];
}
NSLog(#"Is there another Card to Input?");
scanf("%c",&cardAvailable);
} while(cardAvailable != 'N' || cardAvailable != 'n');
NSLog(#":::: Card Names Are ::::");
for(Card *card in allCards)
{
NSLog(#"%#",card.name);
}
}
return 0;
}
However, first - The name is simply NOT SET. I don't know why this is happening. Today was my first day ever with Objective C, so please pardon me if this is too simple.
The Card class files are -
#import <Foundation/Foundation.h>
#interface Card : NSObject
#property(nonatomic) BOOL chosen;
#property (nonatomic, getter = getName) NSString *name;
-(BOOL)wasMatch:(NSMutableArray*) allCards;
-(void)addGlobal:(Card*) aCardName toArray:(NSMutableArray*) allCards;
-(void)setName:(NSString *)name;
-(void)setChosen:(BOOL)chosen;
#end
I get the error here - in the add global line.
#import "Card.h"
#implementation Card
#synthesize chosen = _chosen;
-(BOOL)chosen
{
return _chosen;
}
-(void)setChosen:(BOOL)chosen
{
_chosen = chosen;
}
#synthesize name = _name;
-(NSString*)getName
{
return _name;
}
-(void)setName:(NSString*)name
{
name = _name;
}
-(BOOL)wasMatch:(NSMutableArray *)allCards
{
for(Card *card in allCards)
{
if([self.name isEqualToString:card.name])
{
return true;
}
}
return false;
}
-(void)addGlobal:(Card *)aCardName toArray:(NSMutableArray *)allCards
{ NSLog(#" THE NS STRING %#",aCardName.name);
[allCards addObject:aCardName.name];
}
#end

Your setter is wrong.
-(void)setName:(NSString*)name
{
name = _name;
}
You assign the current instance variable (_name) to the parameter.
This should be the other way around.
-(void)setName:(NSString*)name
{
_name = name;
}
But you don't need this at all. There are various problems with your code.
Objective-C does not use getFoo for ordinary getters.
#property (nonatomic, getter = getName) NSString *name; should be
#property (copy, nonatomic) NSString *name;
And unless you are writing code in a ancient version of Objective-C (which you shouldn't) you don't need explicit getters and setters. In recent version you don't even have to explicitly synthesize.
In newer version of Objective-C your Card class should look more like this:
#interface Card : NSObject
#property (assign, nonatomic) BOOL chosen;
#property (copy, nonatomic) NSString *name;
-(BOOL)wasMatch:(NSMutableArray*) allCards;
-(void)addGlobal:(Card*) aCardName toArray:(NSMutableArray*) allCards;
#end
#import "Card.h"
#implementation Card
-(BOOL)wasMatch:(NSMutableArray *)allCards
{
for(Card *card in allCards)
{
if([self.name isEqualToString:card.name])
{
return YES;
}
}
return NO;
}
-(void)addGlobal:(Card *)aCardName toArray:(NSMutableArray *)allCards
{ NSLog(#" THE NS STRING %#",aCardName.name);
[allCards addObject:aCardName.name];
}
#end
Way less code. You might want to look for a more up to date learning resource.
The rest of your code looks questionable as well. I honestly don't see the point in addGlobal:toArray:. But I don't want to make this answer longer than necessary. ;-)
Also, for the love of your future self, don't use 1 in NSString *nName = [NSString stringWithCString:cName encoding:1]; use the constant. In your case NSASCIIStringEncoding. But because it's no longer 1980 you most likely want to use NSUTF8StringEncoding instead of ASCII.
As I said, please get more recent and decent learning resources.

In the -setName: property accessor method, you assign the the _name instance variable value to the name parameter. It should be swapped like this:
-(void)setName:(NSString*)name
{
_name = name;
}

To learn Objective-C i reccomment the most recent Stanford lectures by Paul Hegarty. CS193p the class is called. You can find all materials, and excellent videos on iTunes U. Other then Apple resource, there is no better then this.

Related

Do I need to override the getter for a property to return an immutable copy?

Suppose that I have a class that holds a mutable array. I want to make sure that if other classes ask for the array they will get a non-mutable type, but in the owning class, it is actually an instance of NSMutableArray, so that I can add and remove items.
#import "Person.h"
#class Asset;
#interface Employee : Person
{
NSMutableArray *_assets;
}
#property (nonatomic,copy) NSArray *assets;
-(void)addAssets:(Asset *)a;
The question is, do I have to modify the accessor methods into something like this, or will it automatically behave like I want?
#import "Employee.h"
#import "Asset.h"
#implementation Employee
/* Accessors for assets properties
-(NSArray *)assets
{
return [_assets copy];
}
-(void)setAssets:(NSArray *)assets
{
_assets = [assets mutableCopy ];
}
*/
-(void)addAssets:(Asset *)a
{
//is assets nil?
if (!_assets) {
//Create Array
_assets = [[NSMutableArray alloc]init];
}
[_assets addObject:a];
}
ppalancica's answer is incorrect. The copy attribute means only that the setter will take a copy when the property is set. The synthesized getter will not return a copy. You must implement that behavior yourself:
- (NSArray *)assets
{
return [_assets copy];
}
You might want to make an internal-only accessor that doesn't make a copy. You could also redeclare the property privately; client code will then be contracted to treat the array it requests as immutable.
This code demonstrates that the synthesized getter returns the uncopied object:
#import <Foundation/Foundation.h>
#interface ArrayReturner : NSObject
#property (copy, nonatomic) NSArray * array;
#end
#implementation ArrayReturner
{
NSMutableArray * _array;
}
- (BOOL)myArrayIsIdenticalTo:(NSArray *)otherArray
{
return _array == otherArray;
}
#end
int main(int argc, const char *argv[])
{
#autoreleasepool {
ArrayReturner * a = [ArrayReturner new];
[a setArray:#[#1, #2]];
NSArray * returnedArray = [a array];
// Does not throw
NSCAssert([a myArrayIsIdenticalTo:returnedArray],
#"Returned array is a separate instance.");
}
return 0;
}
Because you already specified the attribute "copy" for the array property, there is no need to override the getter and setter. The compiler will do all the heavy work for you.
If you specify "strong" instead, the getter and setter would look like:
-(NSArray *)assets
{
return _assets;
}
-(void)setAssets:(NSArray *)assets
{
_assets = assets;
}
And that may be a problem.
There is actually a WWDC conference that explains all these details. For NSString properties it is more recommended to use copy, and you can see it a lot like that in the iOS SDK frameworks.

Objective C - Custom Getter with inheritance

Recently I have worked with Core Data. When I want to set a default value for some fields, I came up with this problem:
So I made a simple represent:
We have 2 class Parent and Child, in which Child inherit from Parent.
// Parent.h
#interface Parent : NSObject
#property (strong, nonatomic) NSString *lastName;
// Child.h
#interface Child : Parent
In Parent class, I made a custom getter to set a default value when nothing is set:
// Parent.h
- (NSString *)lastName
{
if (_lastName) {
return _lastName;
} else {
return #"Parent Default Name";
}
}
But I cannot make a custom default value for the field "name" which Child inherits from its Parent.
// Child.h
#implementation Child
- (NSString *)lastName
{
if (super.lastName) {
return super.lastName;
} else {
return #"Child Default Name";
}
}
The main function to test:
int main(int argc, const char * argv[])
{
#autoreleasepool {
Parent *newParent = [[Parent alloc] init];
newParent.lastName = #"newParentName";
NSLog(#"Parent: %#", newParent.lastName);
Child *newChild = [[Child alloc] init];
NSLog(#"Child: %#", newChild.lastName);
}
return 0;
}
Apparently, #"Child Default Name" is never reach. The returned values would be #"Parent Default Name".
So my question here is: How can I set a custom getter for the field the Child class inherits from Parent without define an overriding property?
// Parent.h
#interface Parent : NSObject
{
NSString *_lastName; //just add this line.
}
#property (strong, nonatomic) NSString *lastName;
If you want another name to use, you can try this:
// Parent.h
#interface Parent : NSObject
{
NSString *_lastNameAlias; //can be used in child
}
#property (strong, nonatomic) NSString *lastName;
//Parent.m
#synthesize lastName = _lastNameAlias;
// Parent.m
- (NSString *)lastName
{
if (!_lastName) {
_lastName = #"Parent Default Name";
}
return _lastName;
}
// Child.m
- (NSString *)lastName
{
if (!_lastName) { //Or _lastNameAlias
_lastName = #"Child Default Name";
}
return _lastName;
}
And as Steven Fisher mentioned, lastname is different with lastName, but I think it is a spelling mistake.
Note:
- (NSString *)lastname
vs
- (NSString *)lastName
Objective-C is case sensitive. Make sure they have the same case and the behaviour you want should happen automatically.
That said, calling self.lastName is pretty weird (assuming you settle on lastName for both). I'm not sure that will work. I suggest calling [super lastName] instead.
The problem is that the parent implementation of the lastName method never returns nil. It either returns the current (non-nil) value of _lastName or it returns the default parent string. In neither case does it return nil. Hence the if [super.lastName] check will always return true.
To avoid the problem, the child getter needs to access the instance variable directly. This can only be done if the parent class explicitly declares the instance variable in the header file. So parent.h should be
#interface Parent : NSObject
{
NSString *_lastName;
}
#property (strong, nonatomic) NSString *lastname;
#end
and then the implementation in child.m can be
- (NSString *)lastname
{
if ( _lastName )
return( _lastName );
else
return #"Child default name";
}

Getting incompatible integer to pointer error

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 *

How to avoid temp objects when returning NSString under ARC

I've got a class with two properties:
#interface Contact : NSObject {
NSString *lastname;
NSString *lastNameUpper;
}
I've declared lastname as a property (and synthesize it in the .m-file):
#property (nonatomic, retain) NSString *lastname;
However, I want to write my own method to access the lastNameUpper, so I declared a method:
- (NSString *) lastNameUpper;
and implemented it like this:
- (NSString *) lastNameUpper {
if (!lastNameUpper) {
lastNameUpper = [lastname uppercaseString];
}
return lastNameUpper;
}
This works all right, but as this is called quite often, a lot of temporary objects are called. Interestingly, the Instruments show a lot of "Malloc (4k)", and the number increase each time lastNameUpper is accessed. I can also see that the memory is allocated in objc_retailAutoreleaseReturnValue.
As this was working fine before I converted my project to ARC, I'm assuming that I have to make some ARC specific additions to the method signature, but I can't seem to be able to make it work.
Any suggestions?
0: you should copy your NSString properties:
#property (nonatomic, copy) NSString * lastname;
I'm guessing that returning the string is implemented by copying it.
nope. copy of an immutable string is a retain operation. just run it in the profiler to see how much this costs in time and memory. also, there's no implicit copy in this case.
Update
I tested this on Lion-64. uppercaseString may return a mutable string.
To be safe, you may consider assigning a copy of the result of uppercaseString: lastNameUpper = [[lastname uppercaseString] copy];. that may result in more or less allocations, depending on how you used the string in your implementation. if your properties copy, then a copy will be made each time you assign it. the easy generalization is to assign a copy, and the rest usually takes care of itself.
Test Program
// ARC enabled
#import <Foundation/Foundation.h>
#interface Contact : NSObject
{
NSString * lastname;
NSString * lastNameUpper;
}
#property (nonatomic, copy) NSString *lastname;
#end
#implementation Contact
#synthesize lastname;
- (NSString *) lastNameUpper {
if (!lastNameUpper) {
lastNameUpper = [lastname uppercaseString];
}
return lastNameUpper;
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
int n = 0;
while (n++ < 100000) {
Contact * c = [Contact new];
c.lastname = #"skjdhskjdhaksjhadi";
NSString * lastNameUpper = c.lastNameUpper;
}
}
return 0;
}
Override the - (void)setLastname:(NSString*)aLastname method (created automatically by #synthesize lastname, and set lastNameUpper as in the existing method.
Now create a lastNameUpper property (and synthesize it):
#property (nonatomic, readonly) NSString *lastNameUpper;
Since this will return the pointer of the lastNameUpper instance variable, no copies should be made whenever this is accessed.

Request for member 'pData' with BOOL value TRUE is not a structure or union-Objective C

I could not use the pData[4096] to pass it to the other function from main.
data.m
------
#implementation data
static int msgID;
static char pData[4096]="\0";
+ (void)initialize
{
//some initialisations
msgID =123;
}
-(void)SwapEndian:(uint8_t*)pData withBOOLValue:(BOOL)bIsAlreadyLittleEndian
{
NSLog("%s %s",pData,bIsAlreadyLittleEndian);
}
#end
main.m
-------
[dat SwapEndian:dat.pData withBOOLValue:TRUE];
I am getting pData undeclared. As pData is declared as static inside the Data
implementation i tried with dat.pData to pass it from main.But when i do it i am getting
Request for member 'pData' with BOOL value TRUE is not a structure or union.
It is difficult to determine what the code is supposed to do, but here is how to create an Objective-C object that holds an integer identifier and a 4096-character array. Please note that this sort of thing is usually discouraged. Unless you have a really specific reason for using int and char[], the identifier should be NSInteger and the data should be an NSData or NSString object.
I have also used some of the "standard" naming conventions. If you are writing Cocoa code, it helps to drink a lot of the Kool-Aid.
Message.h:
#interface Message : NSObject
{
int identifier;
char data[4096];
}
#property (nonatomic, assign) int indentifier;
#property (nonatomic, readonly) char * data;
- (void)swapEndian:(BOOL)flag;
#end
Message.m:
#implementation Message
#synthesize identifier;
#synthesize data;
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
identifier = 0;
data[0] = '\0';
return self;
}
- (void)swapEndian:(BOOL)flag
{
NSLog(#"%s %d", data, flag);
}
#end
main.m:
#import "Message.h"
...
Message * message = [[[Message alloc] init] autorelease];
message.identifier = 123;
[message swapEndian:YES];