(NSString *) description -- not clear on something.... (Objective-C) - objective-c

Let's say I have two array's. Let's imagine one is a NSMutableDictionary, the other is an NSMutableArray.
I also have this defined:
-(NSString *) description {
// return a human readable version of the array contents
return self.contents;
}
Then, for clarity sake, I want to print an array using something like this:
self.descriptionOfLastFlip = [NSString stringWithFormat:#"Array %#",[cardsFaceUp componentsJoinedByString:#", "]];
Then, of course, using that self.descriptionOfLastFlip to print something to the screen.
Ok, stupid question time... How would I define two separate description methods for dealing with the array and the dictionary in a different way? Obviously I'd probably want to access the info slightly differently, but, self.description, while it might work for the array, wouldn't work for the dictionary..
I'd love some insight on how to deal with creating a description method for multiple array's/dictionary's (or, how can you 'target' a description to one type of array, etc..?)

I tend to do things like this:
- (NSString *)description {
return [NSString stringWithFormat:#"MyClass { array = %#, dictionary = %# }", someArray, someDictionary];
}
Replace someArray and someDictionary with whatever properties or ivars you wish to include.

Related

How do I concatenate strings together in Objective-C?

So I am trying to concatenate a bunch of input strings together as one string so I can save that to a text file.
So far I am trying to write something like this
NSString *tempString = [[NSString alloc]initWithFormat:#"%#%#%#", text1, text2, text3];
The only problem with this is that I need a total of 30 strings stored this way. I need a way to do this without typing out each string name. Is there a way to use a for loop or something to accomplish this? Type the strings like this perhaps?
text(i)
So that the variable name would change each time it went through the for loop. I've tried doing something like this and I can't get it to work. If you can help me with this method or another way that you know to do it I would be very thankful.
Okay, so all of the answers here take the wrong approach (sorry guys).
The fundamental problem is that you are using your "text boxes" as a data source, when they should simply be views. When someone changes the text, you should immediately store them in your model (which could be a simple array) and then reference that model later. (This is part of MVC. Look it up if you aren't familiar, as you should be if you are programming for iOS!)
Here is what I would do. (I'm assuming that your "text boxes" are UITextField's.)
Set the delegate for each text field to your view controller.
Set the tag for each text field to a number which represents the order that you want the strings joined in. (ie 1-30)
If you don't have a separate class for your data model, then setup a declared property in your view controller which stores a reference to a NSMutableArray which can contain all of the strings in order. Let's call it dataSource. In viewDidLoad: set this to an actual mutable array filled with empty values (or previously stored values if you are saving them). The reason that we store empty values is so that we can replace them with the user entered strings for any index, even if they are entered out of order:
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataSource = [[NSMutableArray alloc] initWithCapacity:20];
for(int i = 0; i < 30; i++)
[self.dataSource addObject:#""];
}
Then, use the following text field delegate method which stores the strings into the array as they are entered:
// This is called every time that a text field finishes editing.
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (textField.tag > 0)
[self.dataSource replaceObjectAtIndex:textField.tag-1 withObject:textField.text];
}
Congratulations! All of your strings are now stored in one array. Now we just have to combine them all:
NSMutableString *theString = [self.dataSource componentsJoinedByString:#""];
Note that I have NOT tested all of this so there may be typos. This should get you pointed in the right direction though!
If you set up your text boxes in Interface Builder with an IBOutletCollection(UITextField) you would have an array of text boxes that you could access the text value using KVC and join them.
//interface
...
#property (nonatomic, strong) IBOutletCollection(UITextField) NSArray *textBoxes;
//implementation
...
NSString *tempString = [[textBoxes valueForKey:#"text"]
componentsJoinedByString:#""];
Using iOS 4's IBOutletCollection
If you programmatically create your text boxes then add them to an array as you create them.
NSMutableString's appendString: method is your friend.
NSArray *strings = [NSArray arrayWithObjects: #"Hi", #" there", #" dude", nil];
NSMutableString *result = [[NSMutableString alloc] init];
for (NSString *string in strings) {
[result appendString:string];
}
NSLog(#"result: %#", result); // result: Hi there dude

Generate #property implementations with C preprocessor (uppercase a character in the preprocessor)

I may be trying to abuse the preprocessor. I want to see if what I have in mind is even possible.
My class has #properties that all have the same bodies. I want to generate these bodies with a preprocessor macro. E.g.:
- (float) accelerometerSensitivity {
return [dict floatForSelector:_cmd or:1];
}
- (void) setAccelerometerSensitivity:(float) n {
[dict setFloat:n forSelector:_cmd];
[dict writeToFile:[self globalDataFilename] atomically:YES];
}
- (float) returnSpringTension {
return [dict floatForSelector:_cmd or:0];
}
- (void) setReturnSpringTension:(float) n {
[dict setFloat:n forSelector:_cmd];
[dict writeToFile:[self globalDataFilename] atomically:YES];
}
// set*ForSelector methods are in a category on NSMutableDictionary and depend on a function that translates selectors into strings:
// NSString* keyFromSelector(SEL selector);
The idea is that instead of using string literals (or string constants) as keys into the dictionary, I derive the string from the selector name. This way I am sure that the spelling of the key matches the property name and essentially get the benefit of compile-time validation of dictionary keys.
What I want to do is say something like SELECTOR_PROPERY(accelerometerSensitivity) and have it expand into the the getter and the setter. The main difficulty I have in implementing this as a pre-processor macro is generating the setter name from the property name. I need to uppercase the first letter of the property name, and I have no idea how to do that in the preprocessor.
Nope, you can't do that.
But, you can combine identifiers, so in theory you could define this as:
MACRO(A,a,ccelerometerSensitivity)
It's somewhat klugey, but it's more terse than the alternative.
Here's how I'd do it:
#define MACRO(_a) { \
const char *name = #_a; \
NSString *getterName = [NSString stringWithUTF8String:name]; \
NSString *setterName = [NSString stringWithFormat:#"set%c%s:", toupper(name[0]), (name+1)]; \
NSLog(#"getter name: %#", getterName); \
NSLog(#"setter name: %#", setterName); \
}
Basically, you stringify the macro parameter, then use a simple C function to uppercase the first letter, and use an offset to get everything after the first letter.
Now when you do this:
MACRO(foo);
MACRO(bar);
It logs this:
2011-07-19 21:21:24.798 EmptyFoundation[16016:903] getter name: foo
2011-07-19 21:21:24.800 EmptyFoundation[16016:903] setter name: setFoo:
2011-07-19 21:21:24.801 EmptyFoundation[16016:903] getter name: bar
2011-07-19 21:21:24.802 EmptyFoundation[16016:903] setter name: setBar:
HOWEVER, these are strings. You can't use them as method names. Sorry. :(
Actually, you probably really don't want to do this purely for architectural reasons.
You'll likely be better off if you:
separate the notion of setting state from persisting state. That you are causing I/O with every single tiny little change is horribly inefficient. It is also a mode rife with potential for problems; what happens if you move to a UI where the values track the UI continuously? ... you really don't want disk I/O for every time a dial/slider is tracked under a finger!
use #synthesize for all your #properties and don't even declare ivars. Leverage the tool's ability to generate exactly correct setters / getters for you.
that code looks an awful lot like you've re-invented NSUserDefaults? Use NSUserDefaults for any user preference kinda stuff.

Problem with an Array, a Property and NSLog

So i have this block of code it adds players to an NSMutableArray in my ViewController playerList. For some reason i cannot print all the playernames to the log. Am I doing something wrong? I keep getting an error that says member refrence struc objc_object is a pointer. Can anyone see what im doing wrong?
p1,p2,p3,p4 are all NSString Objects that just have the players names.
the addPlayer method creates a new player object with a property named playerName.
- (IBAction)addPlayerButton:(id)sender {
[self.playerList addObject:[self addPlayer:p1]];
[self.playerList addObject:[self addPlayer:p2]];
[self.playerList addObject:[self addPlayer:p3]];
[self.playerList addObject:[self addPlayer:p4]];
for (id element in playerList) {
NSLog(element.playerName);
}
}
for (id element in playerList) {
NSLog(element.playerName);
}
The compiler warning/error is because element is of type id and you can't use the dot syntax with object references of type id (a specific design choice when creating that feature, btw).
Fixed code:
for (Player *element in playerList) {
NSLog(#"%#", element.playerName);
}
Two (unrelated) problems fixed:
explicitly type element to be a reference to your player class (I assumed the name). This'll allow the dot syntax to work.
Use a format string with NSLog. If a player's name were ever to contain a formatting sequence -- %#, for example -- then NSLog() would try to expand the next (non-existent) argument to NSLog and your app would crash or print garbage (say, if the player's name were "Bob %f %f %f").
doesnt look like they are getting
added to the array properly
Make sure you allocate an array and assign it to playerList somewhere:
self.playerList = [NSMutableArray array];
Use this instead:
NSLog(#"%#", element.playerName);
NSLog is sort of like printf() and friends, but not exactly. You must provide a first argument that is a string literal with the format you want to use, followed by any variables represented in the format. In Objective-C, the special format %# means "use the obeject's description method to fill in a value (if there is one)." Sometimes you get a debugger-like output for an object that does not have that method, e.g. or some such, which isn't too useful of course.
In your case, assuming playerName is an NSString, you'll see it's name output if you use the format %# in NSLog's first argument.
EDIT:
You should be able to use a for statement like this:
for(Player *p in playerList) {
NSLog(#"%#", p.playerName);
}
Just because you use addObject: to add the objects doesn't mean you have to give up using the objects' type when you look at them from the array.
If in fact the objects in playerList are just NSStrings, then your loop can simply be
for(NSString *name in playerList) {
NSLog(#"%#", name);
}

Question regarding programming structure of a recursive method

I have this method and it's kind of really big so I can't include it in this post. It takes an array as parameter and tests its objects (NSStrings). Sometimes -but not always-, the method calls itself with an array containing one of those strings. If every test is passed, then the NSString is sent to another method which processes it. This all works fine but now I'm working on a different approach. I want to call the method and let it return an array of all NSStrings that passed the test successfully. But since the method calls itself I don't really know how to do this. Instead of processing it, I could add all successfully tested NSStrings to an array but that array would then needed to be accessible in all methods. What is recommended here? I would like to avoid public variables..
- (void)doStuff: (NSArray *)array { //A quick (very short) example of what I have now.
for (NSString *string in array) {
if ([string isEqualToString: #"test"])
[self doStuff: [NSArray arrayWithObject: #"test2"]];
else
[self processStuff: string];
}
}
You can add a mutable array as one of its parameters and add the result there:
- (void)doRecursiveWithData:(NSArray *)array storeResultsIn:(NSMutableArray *)results {
if ( shoudGoDeeper )
[self doRecursiveWithData:(your new array)];
else
[results addObject:(whatever you want to store)]; // or use another method to so so
}
I know your example is just that, an example, but maybe it's worth thinking about how you can go without the recursion - in many ways that's easily doable and often performs better.

Making the Code check to see if the Text in a Text box matches any of the Strings in an NSArray

I am trying to make my app check the text in a text box and see if it matches any of the strings in an NSArray. I'm hoping you could point me in the right direction or give me some code to do it. I am pretty sure that I would need to use an if statement.
Thanks!
Send a containsObject: message to the array, passing the string you want to check for. That method will use isEqual: messages to compare your string to every object in the array. The message will return a BOOL, which you can test in your if statement.
If you want a more sophisticated definition of “matching” than looking for an equal string, you'll need to loop on the array yourself and perform your desired test on each object in the array one at a time.
Also, be aware that isEqual:/isEqualToString: comparisons are case-sensitive: #"foo" is not equal to #"Foo". If you want case-insensitive or locale-aware comparisons, you'll need to loop on the array yourself and use one of NSString's other comparison methods.
An 'if' statement is a pretty good assumption! - one of the basic tenets (if not the basic tenet) of all programming languages...
There are 101 ways you could do this, so you'll probably get a bunch of replies.
What you want to do in essence is this:
Create an NSEnumerator that will enable you to step through the array
Loop through that enumerator
Check the contents of the array each time
Note that there are all sorts of other approaches too, including using something called "fast enumeration", but I'll leave you to read up on these yourself.
Ok, so time for some example code:
NSEnumerator *enumerator;
enumerator = [myArray objectEnumerator]; // myArray is what we're checking
NSString *stringContents;
while (stringContents = [enumerator nextObject]) { // Cycles through array
if ([stringContents isEqualToString:#"SOMETHING"]) {
// We have a match! Call a method or something...
}
}
Like I said, many other ways of doing this too, but hopefully this will get you started!
Edit: If you want to examine multiple items, eg textField1, textField2 and textField3, your code could look like this (|| is the symbol for a logical "OR"):
if ([stringContents isEqualToString:textField1.text] ||
[stringContents isEqualToString:textField2.text] ||
[stringContents isEqualToString:textField3.text]) {
This would evaluate to true if any of the three textFields matched your array content. If you wanted to only evaluate to true if ALL of your statements matched, swap || for &&.
Edit 2: If you want to compare to multiple NSArrays, it makes sense to put the array enumeration and comparison into a separate method which takes an array and a string, and perhaps returns a BOOL dependent upon whether or not a match was found:
New method:
-(BOOL)checkArray:(NSArray *)myArray forString:(NSString *)myString {
NSEnumerator *enumerator;
enumerator = [myArray objectEnumerator];
NSString *stringContents;
while (stringContents = [enumerator nextObject]) { // Cycles through array
if ([stringContents isEqualToString:myString]) {
return YES;
}
}
return NO;
}
And you can then call this for multiple arrays:
BOOL result1 = [self checkArray:array1 forString:textField.text];
BOOL result2 = [self checkArray:array2 forString:textField.text];
// Etc...
Hope that all helps!...
Edit 3: If you need to fill the arrays with search terms, there is a nice convenience method for doing so:
NSArray *myArray = [NSArray arrayWithObjects:#"Item 1",#"Item 2", nil];
Note a couple of important things: each item is preceded by an # symbol if it's text in quotes, and the last item must always be the word nil. You can put as many items (pretty much) as you want.