I created a single view app, added a label, an un-editable text view and a button, I have an array of strings. Really simply just want to click the button and change the string at random.
- (IBAction)viewNextPressed:(id)sender {
NSArray *affirmationStrings = #[
#"String 1 Pressed",
#"String 2 Pressed",
#"String 3 Pressed"
];
//Generate a random index from our array
int randomNIndex = arc4random() % [affirmationStrings count];
//Display a string from our array
self.displayAffirmationText.text = affirmationStrings[randomNIndex];
}
#end
Obviously this works fine for this example but its horribly inefficient as its generating the array each time the button is clicked. Where is the best place to store the array so its generated upon load and I can just access it when needed?
I see viewDidLoad but as a beginner I want to try to understand best practice for simple tasks. Secondly is the way I am storing strings fine for a large sample of say 500-1k+ strings?
For a relatively small number of strings, a better option would be to:
add either a property or an instance variable to your class
initialise said property or instance variable either in your init method, or, in the case of a View Controller, in viewDidLoad.
So, in the case of an instance variable:
#implementation MyViewController
{
NSArray *_affirmationStrings;
}
...
- (void)viewDidLoad
{
[super viewDidLoad];
_affirmationStrings = #[ ... list of strings ... ];
}
Then refer to it via _affirmationStrings.
In the case of a property, visible to other classes, read-only, with lazy initialization:
In .h:
#interface MyViewController : UIViewController
#property (readonly) NSArray *affirmationStrings
#end
In .m:
- (NSArray *)affirmationStrings
{
if (!_affirmationStrings)
_affirmationStrings = #[ ... list of strings ... ]
return _affirmationStrings;
}
Then refer to it via self.affirmationStrings.
There are also alternatives to make it read/write (so you can set the values from another class), or visible only within the class, etc.
If you want to handle lots of strings, you probably at the very least want to move the list outside of your code, to an external file (text file, JSON, XML, plist...). You can then either load it from there at once and keep it, or load it on demand (and forget about it once you no longer need it, hence reloading it again if you need it again).
You could also store the data in a database, either via Core Data or directly with SQLite.
It really all depends on your goals/requirements.
Related
I have a UITabelView displaying a list of items(returned from the server), users will be able to select any item and add it to his own section.
Upon selecting an item, I am retrieving the selected item details and saving it to a pList file, the list will then be displaying a small icon informing the user that the item is added to his section. User will be able to remove it from his section by tapping the icon again.
To know if the item is already in his section, i am using
[self.myItemsArray containsObject:item]
Everything is working perfect if the user don't exit the application. The issue is occurring once the app is restarted. When I retrieve the tableview list from the database or from server, all the items will not be shown as on my list and [self.myItemsArray containsObject:item] will return NO for the previous added items.
I was searching for an alternative way, by creating an NSArray of my items id's and will then check if the new array will contains the item id to display the icon.
the new issue is that the id is returned as double from the server and stored as it is. The application is crashing when creating the array:
[self.myItemsArray valueForKey:#"Id"]
myItemsArray is an array of items(item is an NSDictionary containing all the details)
So i am desperate now,could anyone help me by resolving any of the above issues?
I prefer the first one, since containsObject will be more simple, but I don't mind to solve it with the second choice if the first will not work.
You'll need to make sure you have a good isEqual: method defined for your custom class, since that's what NSArray uses to determine containment: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/#//apple_ref/occ/instm/NSArray/containsObject:
Let's say this class has something like a 'key' or 'id' value that is unique for each instance of the class (which in your case is a double type). This will not always be the case, of course; it's often an aggregate of individual properties/ivars that constitutes a unique 'key', but for the purposes of this discussion let's say that such a field exists. Your code might look something like this (all in the .m):
static double const EPSILON = .000000001; // You'll need to be the judge of how "close" values can be to each other but still be distinct
#interface MyObjectClass: NSObject
#property (nonatomic, readonly) double uniqueKey;
#end
#implementation MyObjectClass
// ...
- (BOOL)isEqual:(id)object
{
if (self == object) { return YES; }
if (!object isKindOfClass:[MyObjectClass class]]) { return NO; }
MyObjectClass *myObject = (MyObjectClass *)object;
if (abs(self.uniqueKey - myObject.uniqueKey) < EPSILON) { return YES; }
return NO;
}
//...
#end
Note that you should NOT check for direct equality between two float or double values; see How dangerous is it to compare floating point values? for a good discussion about the pitfalls of working with floating point and double precision values.
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 8 years ago.
I'm hitting a bit of a brick wall in getting a program to work in Objective C. In fact, I've been struggling with this problem all day.
I have a ViewController which takes input from a view, which will contain a float variable (weight). I'll need to use this variable in other parts of my program, and therefore I've assigned the data to a very basic class, as follows:
//patient.h
#import <Foundation/Foundation.h>
#interface Patient : NSObject
#property float weight;
#end
and the .m:
//patient.m
#import "Patient.h"
#implementation Patient
float weight;
#end
My first view controller: -There's nothing of note in the .h file aside from connections to UI elements, and:
#import "Patient.h"
In the interests of clarity, I've omitted the code for simply servicing the UIview and getting the user inputs
//demographicsViewController.m
#import "demographicsViewController.h"
#interface demographicsViewController ()
{
// declares an array, for the custom picker
NSArray *_agePickerArray;
NSArray *_ageWeightArray;
}
#end
#implementation demographicsViewController
/* (omitting a sizeable chunk of code in the interests of clarity. Please let me know if you think it important to see) */
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
//captures the output on the text field and converts it into a float that can be slotted straight in to the model. Sets a default value if the field is blank
float weightTextFieldInterrogation;
if([[_weightDisplayTextBox text] length] != 0)
{
weightTextFieldInterrogation = [_weightDisplayTextBox.text floatValue];
}
else
{
weightTextFieldInterrogation = 70.0;
}
//Takes the weight value and plumbs it into the central weight database ready to be used in other parts of the app
NSLog(#"weight: %.1f",weightTextFieldInterrogation);
[Patient.weight] = weightTextFieldInterrogation;
Patient* thePatient = [[Patient alloc]init];
[thePatient setWeight:weightTextFieldInterrogation];
NSLog(#"Stored weight: %.1f",thePatient.weight);
}
#end
The problem I'm having is that when I try to look up the value I've stored for weight or thePatient.weight, the variable is either unrecognised (despite #import "patient" and #import "demographicsViewController), and if I instantiate it again, then the value is blank.
I've read up on a number of different solutions to this, but have been so far unsuccessful at getting any to work. I'd be really grateful if there are any bright ideas about fixing this.
Your problem is that the object you create is destroyed almost immediately after you create it. You are expecting that the instance will float around so that you can pick it up again in the future but it won't.
There are some ways to cheat, but really you should be taking that instance and passing it to some other view controller which wants / needs it, or storing that float value somewhere else (like in a file on disk, or in user defaults).
This is really an OOP question about what you use objects for and how long they exist for.
My usecase here is, I need to search for a string within a set of strings(say synonyms). If I find the relevant search-word within the set of strings, then I will display the set of values associated with that set of strings. I am currently thinking of implementing it as a Map, but not sure, if that's even possible.
I am confused about the best data-structure that can be used in this scenario?
Thanks for the help.
EDIT
Replaced Array with Set as it made more sense
Scenario:
Set-1 = {jug,jar,bottle,cup}
Set-2 = {"Store water", "Store Juice", "Store Coffee"}
For any input I receive from either jug,jar,bottle,cup, I should return the properties associated with it from Set-2. In simple words, I need to return whole of Set-2 when any word from Set-1 matches. Hopefully, this will make my question clear.
you can subclass NSObject for that kind of behaviour.
#interface Synonyms : NSObject
#property NSArray *strings;
#property NSArray *values;
-(NSArray *)searchForWord:(NSString *)searchWord;
#end
#implementation Synonyms
-(NSArray *)searchForWord:(NSString *)searchWord{
BOOL found=NO;
//add code to search for that search term in strings array
if(found)
return self.values;
else
return nil;
}
#end
so, first create all these synonyms instances and store them in NSArray.
Assign strings and values array to each instance.
When user enters search-word, call searchForWord function for each element of array.
If found ,it returns not nil object.else no synonym.
PART 2:
If you are looking for below kind of behaviour, example below
Dictionary myDic;
myDic.Add("car", "automobile");
myDic.Add("car", "autovehicle");
myDic.Add("car", "vehicle");
myDic.Add("bike", "vehicle");
myDic.ListOSyns("car") // should return {"automobile","autovehicle","vehicle" ± "car"}
// but "bike" should NOT be among the words returned
Then you can use NSDictionary,
NSDictionary *dictionary =#{#"car":#[#"automobile",#"autovehicle",#"vehicle"],#"bike":#[#"vehicle"]};
NSArray *values =[dictionary objectForKey:#"search-term"];
if(values==nil)
//no result
else
return values;
I have an NSMutableArray called playersArray in my singleton class which holds for my applications main datasource.
Each object of playersArray is a NSDictionary and the content is like :
{
sgfID = 1;
sgfPlayer = "<PlayerContact: 0xbf851b0>";
}
PlayerContact is a NSObject subclass containing properties like:
NSString * playerName, playerTeam, BOOL PlayerSelected and so on.
In one of my ViewControllers, in viewDidLoad, I want to take a deep copy of playersArray in to a NSMutableArray named duplicatePlayersArray. I do this by
playersArray = [[SGFHelper sharedHelpers] SGFContactsArray];
duplicatePlayersArray = [playersArray mutableCopy];
Now that I have two separate copies, I was under the impression that playersArray and duplicatePlayersArray are two totally different arrays in the memory. However I found that they are NOT!
Even if the debugger shows that they have different memory addresses, their contents have same memory addresses. So when i do this:
[((NSMutableDictionary *)[duplicatePlayersArray objectAtIndex:0]) setObject:#"333" forKey:#"sgfID"];
playersArray's dictionary at index:0 has ALSO "333" as key "sgfID" instead of "1" as it used to before the above line of code ran.
BUT, if I run the below code, only then, the two arrays start to differ
[duplicatePlayersArray replaceObjectAtIndex:0 withObject:tempDict];
Still this doesn't address my concern because the two arrays which I wanted to believe are different are still "connected". A change in one, results in the other array to change its contents.
Can you friends please show me a way to DEEP COPY the array I explained the contents of in a way where all of their contents are kept in different objects.
Use initWithArray:copyItems: to copy each entry in the array
NSMutableArray *duplicatePlayersArray = [[NSMutableArray alloc] initWithArray:playersArray copyItems:YES];
I get the above message in XCode 4.6. I've done a pretty thorough search and but nothing seems to match the exact circumstances surrounding my issue. Admittedly, I'm relatively new to iOS dev, and memory-management has never been my strong suit, but this just has me completely miffed.
I have an instance variable theLink which is defined in the class Game as follows:
#interface Game : NSObject
// Class objects
#property(nonatomic,retain) NSMutableArray *queryItems;
#property(nonatomic,retain) NSMutableArray *theArray;
#property(nonatomic,retain) NSString *theLink;
#property(nonatomic,retain) NSString *thePath;
theLink is set in the makeGame method which is called in the method initialiseGame in my view controller:
- (void) initialiseGame
{
bool gameCreated = FALSE;
while (!gameCreated)
{
gameCreated = [theGame makeGame:#"ptl"];
}
[loadingIndicator stopAnimating];
[loading setText:#"Tap to Start"];
[self performSelector:#selector(setLabels) withObject:nil afterDelay:0.0];
}
(Note: the performSelector afterDelay is used to allow the view to update before continuing. Bit of a hack but I couldn't work out a better way!)
The app then loads the game, and when the user taps the screen to start, the next method which is called from the view controller is:
- (void) setupLink
{
...
for(int i=0; i<[theGame.theLink length]; i++) {
...
}
}
It is on this reference to theGame.theLink where I'm am getting the crash.
What has me most confused is that if I call theGame.theLink from inside the initialiseGame method, it is displays correctly, and also calling any other variable from the Game class (such as thePath or theArray works perfectly, so theGame object has not been deallocated in it's entirety, only the variable theLink.
It seems to me that the variable is being deallocated somewhere as the view controller is being updated. I haven't released the variable, and can't work out why only this variable is being deallocated. As I said at the start, memory-management is not my strength!
Any help/ideas would be hugely appreciated. Let me know if you require any more details.
Thanks heaps,
Andrew
EDIT: Setting of theLink within makeGame
- (bool) makeGame:(NSString*)gameType
{
...
[self getLink];
}
- (void) getLink
{
...
if (... && ((arc4random() % 10) > 8))
{
theLink = #"Animals";
}
}
There are many different ways theLink may be set, depending on random numbers and other factors. This is the most basic form which simply sets it to a static string. It doesn't matter how theLink is set or what it is set to, the program always crashes at the same point.
If theLink is being set to the parameter being passed to it ,#"ptl" or some similar temporary string, it will give you a problem, because it is just a pointer pointing at the current location that is holding #"ptl". After the makeGame method is completed, your system will assume that it is all done with #"ptl" and just free it up.
When you make an #"stringwhatever" in your code, it is supposed to be the equivalent of making an NSObject that is an immutable literal instance of #"stringwhataver". It should, in theory handle all the reference counting in a nice way, but when you are doing your own memory management, there are so many ways to lose count of your references.
There's a pretty simple rule to follow. If you've declared properties, access them via the property. To do otherwise (as you are doing above, with theLink = ...) bypasses all of the memory management built into the property accessors.
self.theLink = ...
Would have solved this problem under MRC. Switching to ARC has "solved" your problem without you understanding the root cause.