I've programmed for a while in Java and .Net, but never really used C or Objective C. I'm still trying to understand a few concepts. I was working on a simple program just to see how I can make an array of structures. Which I believe I got right. I'm having a hard time figuring out how to access the subclasses and store values to the subclasses I created.
I'm guessing I'm getting the error because of my use of scanf. Can anyone offer any help?
Here's what I have so far.
#import <Foundation/Foundation.h>
//Player Prototype: Stores name and wins so far. It can also print out the name and wins
#interface Player : NSObject
{
NSString *name; //Player name
NSInteger wins; //Player wins
NSInteger losses; //Player losses
NSInteger bp; //extra value for anything I might need in the future.
}
#property (retain, nonatomic) NSString *name;
#property NSInteger wins;
#property NSInteger losses;
#property NSInteger bp;
#end
//Next part
#implementation Player
#synthesize name;
#synthesize wins;
#synthesize losses;
#synthesize bp;
#end
//Brackets
#interface Bracket : NSObject
{
NSMutableArray *playerarray;
Player *addplayer;
}
#property (retain, nonatomic) NSMutableArray *playerarray;//array of players
#property (retain, nonatomic) Player *addplayer;//player and data
-(void) SetUp;
#end
//Starting Bracket, working with only 8. Later moving up to 32
#implementation Bracket
#synthesize playerarray;
#synthesize addplayer;
-(void) SetUp;//sets up the array
{
int i;//counting fun!
playerarray = [[NSMutableArray alloc] init];//initialize a bracket
for(i = 0; i < 8; i++)//To add the players
{
Player *addplayerx = [Player new];//New instance of Player
NSString *p;//Not sure if I need two of them.
NSString *tempname = #"bye";
NSLog(#"Player %d Name:", i);
scanf("%s",&p);
tempname = p;
NSLog(#"%s", tempname);
addplayerx.name = p;
NSLog(#"%s", addplayerx.name);
addplayerx.wins = 0;
addplayerx.losses = 0;
addplayerx.bp = 0;
[playerarray addObject: addplayerx];
[addplayerx release];
[p release];
}
}
#end
//End function
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Bracket *starting = [Bracket new];
[starting SetUp];
[pool drain];
return 0;
}
You can't scanf() into an NSString. You need to scan into a regular C string (make sure you allocate memory for it), and then you can construct the NSString from that using stringWithUTF8String:, or something along those lines.
Don't guess: run the application under the debugger, and when it crashes, examine the backtrace. You can also look at the backtraces in ~/Library/Logs/DiagnosticReports/foo.crash.
What are you trying to do, read data line-by-line from a file? It would be much easier to just use text = [NSString stringWithContentsOfFile:path] then split text on all newline characters:
NSCharacterSet *newlines = [NSCharacterSet newlineCharacterSet];
NSArray *lines = [text componentsSeparatedByCharactersInSet:newlines];
You can then just loop across and grab the player names:
NSMutableArray *players = [NSMutableArray arrayWithCapacity:[lines count]];
NSString *whitespace = [NSCharacterSet whitespaceCharacterSet];
for (NSString *line in lines) {
NSString *name = [line stringByTrimmingCharactersInSet:whitespace];
Player *player = [[[Player alloc] init] autorelease];
player.name = name;
[players addObject:player];
}
Related
I am trying to learn how to make simple classes.
So far I am not getting the results expected using addObject and my class.
Here is what I have:
In my view controller:
#import "onoffclass.h"
In its viewDidLoad:
NSMutableArray *inTable;
onoffclass *therec;
onoffclass *readrec;
inTable = [NSMutableArray array];
therec = [[onoffclass alloc]init];
readrec = [[onoffclass alloc]init];
for (int lop=0;lop<3;lop++){
therec.parsedID = [NSString stringWithFormat:#"%i",lop];
[inTable addObject:therec];
NSLog(#"lop=%i onoff.parsedID=%#",lop,therec.parsedID);
for (int z=0;z<[inTable count];z++){
readrec = inTable[z];
NSLog(#" inTable[%i] parsedID=%#",z,readrec.parsedID);
}
}
In my onoffclass.h:
#interface onoffclass : NSObject
#property NSString *parsedID;
#property NSString *parsedOn;
#property NSString *parsedOff;
#property NSString *parsedAdj;
#property NSString *parsedRoom;
#property NSString *parsedBuilding;
#property NSString *parsedWho;
#property NSString *parsedInfo;
#property NSString *parsedBillable;
-(onoffclass*)initWithSomeString: (NSString*)blah AndSomeNum: (int)num;
-(NSString*)description;
#end
In my onoffclass.m:
#import <Foundation/Foundation.h>
#import "onoffclass.h"
#implementation onoffclass {
NSString *_parsedID;
NSString *_parsedOn;
NSString *_parsedOff;
NSString *_parsedAdj;
NSString *_parsedRoom;
NSString *_parsedBuilding;
NSString *_parsedWho;
NSString *_parsedInfo;
NSString *_parsedBillable;
}
-(onoffclass*)initWithSomeString: (NSString*)blah AndSomeNum: (int)num {
self = [super init];
_parsedID = blah;
_parsedOn = #"on";
_parsedOff = #"off";
_parsedAdj = #"adj";
_parsedRoom = #"room";
_parsedBuilding = #"building";
_parsedWho = #"who";
_parsedInfo = #"info";
_parsedBillable = #"billable";
return self;
}
-(NSString*)description {
return [NSString stringWithFormat: #"%#", _parsedID];
}
#end
Here is the output:
lop=0 onoff.parsedID=0
inTable[0] parsedID=0
lop=1 onoff.parsedID=1
inTable[0] parsedID=1
inTable[1] parsedID=1
lop=2 onoff.parsedID=2
inTable[0] parsedID=2
inTable[1] parsedID=2
inTable[2] parsedID=2
Why does it appears that addObject is updating all array indices and how do I fix this?
Thanks,
Dale
You are creating one instance of onoffclass (by the way class names should start with a capital letter) then you are adding the same instance again and again in the loop. Since classes are reference types changing a property affects all occurrences of the same instance.
Solution is to put the line to create an instance in the loop
for (int lop = 0; lop < 3; lop++) {
therec = [[onoffclass alloc] init];
...
Here is an example of how the properties of a javascript object can be enumerated through. I noticed that the loop construct used was a for...in loop. Objective-C also has a for...in loop, so is the same behavior possible in Objective-C?
#interface Bar : NSObject
#property (nonatomic) NSString * stringA;
#property (nonatomic) NSString * stringB;
#property (nonatomic) NSString * stringC;
#end
int main(int argc, const char *argv[]) {
Bar obj = [[Bar alloc] init];
obj.stringA = #"1";
obj.stringB = #"2";
obj.stringC = #"3";
for (NSString *property in obj) {
NSLog(#"%#", property);
}
}
Is this possible with Objective-C? If not, is there an alternative that would mimmic this behavior of iterating through an objects properties?
Short answer: yes it is possible.
Here's some sample code of what you're trying to achieve.
Header
#interface Bar : NSObject
#property (nonatomic, retain) NSString *stringA;
#property (nonatomic, retain) NSString *stringB;
#property (nonatomic, retain) NSString *stringC;
#end
Main
#implementation Bar
// don't forget to synthesize
#synthesize stringA, stringB, stringC;
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
unsigned int numberOfProperties = 0;
objc_property_t *propertyArray = class_copyPropertyList([Bar class], &numberOfProperties);
for (NSUInteger i = 0; i < numberOfProperties; i++)
{
objc_property_t property = propertyArray[i];
NSString *letter = [[NSString alloc] initWithUTF8String:property_getName(property)];
NSString *attributesString = [[NSString alloc] initWithUTF8String:property_getAttributes(property)];
NSLog(#"Property %# attributes: %#", letter, attributesString);
}
free(propertyArray);
}
}
Let me know if you have any questions.
Fast enumeration
Bar *obj = [[Bar alloc] init];
// ...
for (id elem in obj) {
...
}
requires that the class Bar conforms to the NSFastEnumeration Protocol, i.e. it must implement the
countByEnumeratingWithState:objects:count:
method. (This is the case for all Objective-C collection classes such asNSArray, NSDictionary, NSSet.)
So the direct answer to your question is no, you cannot use the fast enumeration syntax for (... in ...) to enumerate all properties of an arbitrary class.
However, it is possible to implement the fast enumeration protocol for a custom class.
Examples how this is done can be found here
https://developer.apple.com/library/mac/samplecode/FastEnumerationSample/Introduction/Intro.html
http://www.cocoawithlove.com/2008/05/implementing-countbyenumeratingwithstat.html
UPDATE: Now working (added fixes as suggested - Thanks!)
I've been trying to clone an NSDictionary of employee info. The main NSDictionary is created in a different class and passed along in prepareForSegue. I want to be able to create a mutable copy of that NSDictionary in another class which can then update the employee info and send it off to another class for processing so I still have the original unchanged dataset to work with at a later time. I've found a few different examples on Stack, but nothing I could get working. When I break on the btn_click method and examine the local pp object after the ..objectForKey call, pp is still nil. What have I done wrong here?
obj_person.h
#import <Foundation/Foundation.h>
#interface obj_person : NSObject
#property (strong,nonatomic) NSString *personID;
#property (strong, nonatomic) NSString *personName;
#property (strong, nonatomic) NSString *personTitle;
#end
obj_person.m
#import "obj_person.h"
#implementation obj_person
#synthesize personID = _personID;
#synthesize personName = _personName;
#synthesize personTitle = _personTitle;
#end
viewcontroller.m
#import "ViewController.h"
#import "obj_person.h"
#interface ViewController ()
#end
#implementation ViewController
int mCounter = 1;
NSMutableDictionary *mCopy;
NSMutableDictionary *mNsd;
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *arnames = [[NSArray alloc] initWithObjects:#"mary", #"jane", #"stan", #"cartman", nil];
NSArray *arkeys = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:1], [NSNumber numberWithInt:2],[NSNumber numberWithInt:3], [NSNumber numberWithInt:4], nil];
mNsd = [[NSMutableDictionary alloc] initWithCapacity:[arnames count]];
int i = 0;
for (NSString *name in arnames)
{
obj_person *p = [[obj_person alloc] init];
p.personID = [arkeys objectAtIndex:i];
p.personName = name;
[mNsd setObject:p forKey:p.personID];
i++;
}
mCopy = [mNsd mutableCopy];
}
- (IBAction)btn_click:(id)sender
{
NSLog (#"%d original items", [mNsd count]);
obj_person *pp = [mCopy objectForKey:[NSNumber numberWithInt:mCounter]];
NSLog(#"%#", pp.personName);
pp.personName = #"Gerald";
if (++mCounter > [mCopy count])
mCounter = 1;
}
#end
Don't define:
NSMutableDictionary *mCopy;
NSMutableDictionary *mNsd;
Outside of the #interface and #implementation. They should be instance variables, so define instance variables or use properties to define them.
It's a good job you don't use n from:
for (NSArray *n in arnames)
because it isn't an NSArray, it's an NSString. You should fix that and you should probably both name it better than n and use it.
This:
obj_person *pp = [mCopy objectForKey:[NSNumber numberWithInt:1]];
fails because the key you originally stored with is an NSString instance and the thing you are using to try to get the data out is an NSNumber instance (so they can never match).
You might try:
mCopy = [mNsd mutableCopy];
[mCopy retain]
One theory is that the mutableCopy returns is an autoreleased object and it's being killed off before the btn_click function fires. According to this post: Retain/release of returned objects, mutableCopy should not be autoreleasing the array, but bugs do happen.
Else, maybe try iterating through with a for-loop instead.
int cnt = [arnames count];
for(int i=0; i<cnt; i++)
...
I'm trying to store some data in an NSMutableArray. This is my struct:
typedef struct{
int time;
char name[15];
}person;
This is the code to add a person:
person h1;
h1.time = 108000;
strcpy(h1.name, "Anonymous");
[highscore insertObject:[NSValue value:&h1 withObjCType:#encode(person)] atIndex:0];
So, I try to extract in this way:
NSValue * value = [highscore objectAtIndex:0];
person p;
[value getValue:&p];
NSLog(#"%d", p.time);
The problem is that the final log doesn't show me 108000!
What is wrong?
Your code looks correct (and works for me), so I deduce that you aren't initializing highscore. So when you send the insertObject:atIndex: message to it, nothing happens. When you then send the objectAtIndex: method to it, you get nil back. When you send getValue: to the nil NSValue *value, it does nothing, so your person p is left filled with random stack garbage, which is why your NSLog doesn't print 108000.
As stated in my initial comment there rarely is a reason to do this kind of stuff with pure c structs. Instead go with real class objects:
If you're unfamiliar with the syntax below you may want to look at these quick tutorials on ObjC 2.0 as well as read Apple's documentation:
A Quick Objective-C 2.0 Tutorial
A Quick Objective-C 2.0 Tutorial: Part II
Person Class:
// "Person.h":
#interface Person : NSObject {}
#property (readwrite, strong, nonatomic) NSString *name;
#property (readwrite, assign, nonatomic) NSUInteger time;
#end
// "Person.m":
#implementation Person
#synthesize name = _name; // creates -(NSString *)name and -(void)setName:(NSString *)name
#synthesize time = _time; // creates -(NSUInteger)time and -(void)setTime:(NSUInteger)time
#end
Class use:
#import "Person.h"
//Store in highscore:
Person *person = [[Person alloc] init];
person.time = 108000; // equivalent to: [person setTime:108000];
person.name = #"Anonymous"; // equivalent to: [person setName:#"Anonymous"];
[highscore insertObject:person atIndex:0];
//Retreive from highscore:
Person *person = [highscore objectAtIndex:0]; // or in modern ObjC: highscore[0];
NSLog(#"%#: %lu", person.name, person.time);
// Result: "Anonymous: 108000"
To simplify debugging you may also want Person to implement the description method:
- (NSString *)description {
return [NSString stringWithFormat:#"<%# %p name:\"%#\" time:%lu>", [self class], self, self.name, self.time];
}
which will allow you to just do this for logging:
NSLog(#"%#", person);
// Result: "<Person 0x123456789 name:"Anonymous" time:108000>
Reimplement Person as an Objective-C object and reap the benefits:
Person.h:
#interface Person : NSObject
{
int _time;
NSString *_name;
}
#property (assign, nonatomic) int time;
#property (retain, nonatomic) NSString *name;
#end
Person.m:
#import "Person.h"
#interface Person
#synthesize time = _time;
#synthesize name = _name;
- (id)init
{
self = [super init];
if (self != nil)
{
// Add init here
}
return self;
}
- (void)dealloc
{
self.name = nil;
[super dealloc];
}
#end
Is there a way to set a value to readonly attribute in Objective-C?
I actually don't care how nasty the code is unless it isn't stable anymore.
Never mind my comment, here's the two ways you do it:
#interface Grimley : NSObject
#property (readonly, copy) NSString * blabber;
#property (readonly, copy) NSString * narwhal;
- (id) initWithBlabber:(NSString *)newBlabber;
#end
#implementation Grimley
#synthesize blabber;
#synthesize narwhal = unicorn;
- (id) initWithBlabber:(NSString *)newBlabber {
self = [super init];
if( !self ) return nil;
// Any object can of course set its own ivar regardless
// of how the property it backs is declared.
blabber = [newBlabber copy];
// Refer to the _ivar_, not the property.
unicorn = #"One horn";
return self;
}
#end
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Grimley * g = [[Grimley alloc] initWithBlabber:#"Excelsior"];
// This is how you get around the property.
[g setValue:#"Nimitz" forKey:#"blabber"];
// Again, use the name of the variable, not the property
[g setValue:#"Pearly horn" forKey:#"unicorn"];
NSLog(#"%#", [g blabber]);
NSLog(#"%#", [g narwhal]);
[g release];
[pool drain];
return 0;
}