Create Instances During Runtime in Objective C - objective-c

I have the following class so far:
#interface BRPerson : NSObject
#property (nonatomic) NSString *name;
#property (nonatomic) NSString *address;
-(instancetype)initWithName:(NSString*)name andAddress:(NSString*)address;
#implementation BRPerson
-(instancetype)initWithName:(NSString*)name andAddress:(NSString*)address{
self.name = name;
self.address = address;
return self;
}
#end
and my main is:
#import <Foundation/Foundation.h>
#import "BRPerson.h"
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSString *filepath = [[NSBundle mainBundle] pathForResource:#"Directory"
ofType:#"txt"];
}
return 0;
}
The Directory.txt file is a plain file I created that is set up as:
name, address
name2, address2
name3, address3
I am trying to write a program that reads names and addresses and creates a new instance of the BRPerson class with the name being the instance name. Would it first have to be stored in NSDictionary (with name being the key and address the value)?
Any help is appreciated. Thank you in advance.

Following Objective-C's pattern for initializers, do the following:
-(id)initWithName:(NSString*)name andAddress:(NSString*)address{
self = [super init];
if (self) {
_name = name;
_address = address;
}
return self;
}
You might also consider providing a convenience factory method, like this:
+ (instancetype)personWithName:(NSString*)name andAddress:(NSString*)address {
return [[self alloc] initWithName:name andAddress:address];
}
With this, the loop that builds the instances will look like many parts of the SDK:
NSMutableArray *persons = [#[] mutableCopy];
while (/* more input */) {
NSString *name = // next name from input
NSString *address = // next address from input
BRPerson *person = [BRPerson personWithName:name andAddress:address];
[persons addObject];
}
// here, the array persons will contain several BRPerson instances
// as specified by the input

Related

Objective-C: How to create object instance with constructor that references arrays

I'm trying to create an array of objects, but it's not working as expected. I have a Person class with a name property and I'm trying to instantiate Person objects with the names from another array like the code below. Instead of the names in the names array being used, the program outputs "(null)". So that means that it's not working as expected.
ViewController.m
names = [NSArray arrayWithObjects:#"Mike", #"John", #"Jimmy", #"Tim", nil];
personsArray = [[NSMutableArray alloc] initWithCapacity:4];
for (int i = 0; i <= 4; i++) {
Person *person = [[Person alloc] initWithName:[names objectAtIndex:i]];
NSLog(#"%#", [person name]); // outputs "(null)"
[personsArray addObject:person];
}
Person.m
#import "Person.h"
#implementation Person
-(id)initWithName:(NSString *)name {
if (self = [super init]) {
name = name;
}
return self;
}
#end
Person.h
#import <Foundation/Foundation.h>
#interface Person : NSObject
#property (nonatomic, strong) NSString *name;
-(id)initWithName:(NSString *)name;
#end
Please help!
This line in -[Person initWithName:] is your problem:
name = name;
You are just assigning the argument variable to itself. You need to assign the instance variable. You probably want to copy the input string, so:
_name = [name copy];

Objective-C addObject seems to put object into all array indices

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];
...

Write a complex array of custom structs to file Objective C

I need to save and load the contents of an array of structs, but I know that Objective C is very particular about which data types you can read/write with.
Here is my struct:
struct SCourse
{
NSMutableArray* holes; // holds integers (pars)
NSString* name;
int size;
BOOL inUse;
};
#interface CoursesManager : NSObject
{
struct SCourse courses[5];
}
What are the data types I'll need to use? Do they each have different methods needed in order to read/write? I'm just looking for a non-complex way to get all the data I need to and from a file. I could do this quite easily in a language I'm more familiar with (C++), but some of the particulars of Objective-c are still lost on me.
EDIT: Solution (thanks for the help, everyone)
-(void)applicationWillResignActive:(UIApplication *)application {
// save the courses
NSMutableArray* totalWriteArray = [[NSMutableArray alloc] initWithCapacity:MAX_COURSES];
for (int i = 0; i < MAX_COURSES; ++i)
{
struct SCourse saveCourse = [coursesManager GetCourseAtIndex:i];
NSNumber* nInUse = [NSNumber numberWithBool:saveCourse.inUse];
NSNumber* nSize = [NSNumber numberWithInt:saveCourse.size];
NSMutableArray* writeArray = [[NSMutableArray alloc] initWithCapacity:4];
[writeArray addObject:nInUse];
[writeArray addObject:nSize];
[writeArray addObject:saveCourse.name];
[writeArray addObject:saveCourse.holes];
[totalWriteArray addObject:writeArray];
}
[totalWriteArray writeToFile:[self saveFilePath] atomically:YES];
}
And for the loading back in...
-(void)loadFile {
NSString *myPath = [self saveFilePath];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
if (fileExists) {
NSMutableArray* totalReadArray = [[NSMutableArray alloc] initWithContentsOfFile:[self saveFilePath]];
for (int i = 0; i < MAX_COURSES; ++i)
{
struct SCourse loadCourse = [coursesManager GetCourseAtIndex:i];
NSMutableArray* loadArray = [totalReadArray objectAtIndex:i];
NSNumber* nInUse = [loadArray objectAtIndex:0];
loadCourse.inUse = [nInUse boolValue];
NSNumber* nSize = [loadArray objectAtIndex:1];
loadCourse.size = [nSize integerValue];
NSString* inName = [loadArray objectAtIndex:2];
loadCourse.name = inName;
NSMutableArray* inHoles = [loadArray objectAtIndex:3];
loadCourse.holes = inHoles;
[coursesManager ReplaceCourseAtIndex:i With:loadCourse];
}
}
}
First thing first. You shouldn't use plain old C structures. The ARC memory management will not appreciate.
If you are familiar with C++, you should maybe use a C++ class instead, which will please the compiler and runtime. Depends on what you want to do.
Array. Use either NSArray or std::vector but please, no plain C arrays. Not sure how ARC will handle this but I suppose it will not appreciate much. Objective-C and C++ both provides all the tools you need to handle collections of whatever.
Serialization. You have several possibilities, one of them is NSCoder.
Last word, with the so called modern syntax, converting things into ObjC objects is quite easy.
BOOL b = YES;
int i = 10;
double d = 3.14;
char* s = "Pouf pouf";
You get the ObjC equivalents with the boxin' thingy:
NSNumber* bo = #( b );
NSNumber* io = #( i );
NSNumber* do = #( d );
NSString* so = #( s );
NSArray* ao = #[ #( i ), do ];
NSDictionary* = #{ #"num" : io, #"str" : #( s ) };
To write something in a file, in one gracious step:
[#{ #"bool" : bo, #"array" : #[ #"string", #10, #( 10 + 20 ) ] }
writeToFile: #"path.plist" atomically: YES];
But the question remains, what are you trying to accomplish?
One easy approach is to store these arrays in an NSMutableDictionary object and use the method:
[mutableDict writeToFile:#"path/to/file" atomically:YES];
To store the data and:
NSMutableDictionary *anotherDict = [NSMutableDictionary dictionaryWithContentsOfFile:#"path/to/file"];
To read the contents back in.
Here's what I'd suggest:
Make a custom class with the properties you want (.h file):
#import <Foundation/Foundation.h>
#interface CustomHolder : NSObject {
NSString *last;
NSString *first;
NSString *middle;
}
#property (strong, nonatomic) NSString *last;
#property (strong, nonatomic) NSString *first;
#property (strong, nonatomic) NSString *middle;
#end
And then set the .m file up so that you can encode/decode the object
#import "CustomHolder.h"
#implementation CustomHolder
#synthesize last, first, middle;
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:first forKey:#"first"];
[encoder encodeObject:last forKey:#"last"];
[encoder encodeObject:middle forKey:#"middle"];
}
- (id)initWithCoder:(NSCoder *)decoder
{
if (self = [super init])
{
self.first = [decoder decodeObjectForKey:#"first"];
self.last = [decoder decodeObjectForKey:#"last"];
self.middle = [decoder decodeObjectForKey:#"middle"];
}
return self;
}
#end
Then you can just
[NSKeyedArchiver archiveRootObject:obj toFile:[self saveFilePath]] to save and
[NSKeyedUnarchiver unarchiveObjectWithFile:[self saveFilePath]] to load
That's probably the most similar to using C-structs (especially because ARC doesn't let you use structs).

How to return an NSString * in Objective C (keep getting invalid summary)

So I presume this is a memory issue, here's the code:
- (NSString *)giveMeAStringGoddammit
{
NSString *s;
// switch statement to choose which string to assign to s, so essentially:
s = #"a string";
return s;
}
And the calling code:
NSString *aString;
aString = [self giveMeAStringGoddammit];
However after this call, aString has an invalid summary when debugged and crashes when run.
I suspect I'm missing a retain or something, can someone help? Thanks.
What you've got works just fine:
#import <Foundation/Foundation.h>
#interface Test : NSObject
- (NSString *)giveMeAStringGoddammit;
#end
#implementation Test
- (NSString *)giveMeAStringGoddammit
{
NSString *s;
// switch statement to choose which string to assign to s, so essentially:
s = #"a string";
return s;
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
Test *t = [[Test alloc] init];
NSLog(#"t says: %#", [t giveMeAStringGoddammit]);
}
return 0;
}
The output of this program is:
t says: a string
To make this a little more realistic, let's change it to use a property:
#import <Foundation/Foundation.h>
#interface Test : NSObject
#property(copy, nonatomic) NSString *string;
- (NSString *)giveMeAStringGoddammit;
#end
#implementation Test
#synthesize string;
- (NSString *)giveMeAStringGoddammit
{
NSString *s;
// switch statement to choose which string to assign to s, so essentially:
s = self.string;
return s;
}
#end
int main (int argc, const char * argv[])
{
#autoreleasepool {
Test *t = [[Test alloc] init];
t.string = #"Hello world!";
NSLog(#"t says: %#", [t giveMeAStringGoddammit]);
}
return 0;
}
This does what you'd expect:
t says: Hello world!
You have created a pointer object and it's expected to increment it's retain count whenever you referring them, for increasing retain count the string should be initiated and allocated with memory else you could use [NSString stringwithString:[self giveMeAStringGoddammit]. You can use this definition only when you exactly need it reference locally. because whenever you try to refer it out side the auto release pool will crash the app (hence it's not retained manually). So if you need to use it out side of the function, better use [NSString alloc]init] and then load your string to the pointer object. Well the way to make your code to work is add the lines NSString *aString = [NSString stringWithString:[self giveMeAStringGoddammit]];
NSLog(#"My Str:%#",aString); Hooray now the goddammit string was given......

Objective C class initialization errors

I have the following objective C class. It is to store information on a film for a cinema style setting, Title venue ID etc. Whenever I try to create an object of this class:
Film film = [[Film alloc] init];
i get the following errors: variable-sizedobject may not be initialized, statically allocated instance of Objective-C class "Film", statically allocated instance of Objective-C class "Film".
I am pretty new to Objective C and probably am still stuck in a C# mindset, can anyone tell me what I'm doing wrong?
Thanks michael
code:
// Film.m
#import "Film.h"
static NSUInteger currentID = -1;
#implementation Film
#synthesize filmID, title, venueID;
-(id)init {
self = [super init];
if(self != nil) {
if (currentID == -1)
{
currentID = 1;
}
filmID = currentID;
currentID++;
title = [[NSString alloc] init];
venueID = 0;
}
return self;
}
-(void)dealloc {
[title release];
[super dealloc];
}
+(NSUInteger)getCurrentID {
return currentID;
}
+(void)setCurrentID:(NSUInteger)value {
if (currentID != value) {
currentID = value;
}
}
#end
// Film.h
#import <Foundation/Foundation.h>
#interface Film : NSObject {
NSUInteger filmID;
NSString *title;
NSUInteger venueID;
}
+ (NSUInteger)getCurrentID;
+ (void)setCurrentID:(NSUInteger)value;
#property (nonatomic) NSUInteger filmID;
#property (nonatomic, copy) NSString *title;
#property (nonatomic) NSUInteger venueID;
//Initializers
-(id)init;
#end
You need your variable that holds the reference to your object to be of a reference type. You do this by using an asterisk - see below:
Film *film = [[Film alloc] init];
Coming from Java I often think of the above as:
Film* film = [[Film alloc] init];
I tend to associate the 'reference' marker with the type. But hopefully someone more versed in C/C++/ObjC will tell me why this is wrong, and what the 'asterisk' is actually called in this context.