Here's my code of generating data
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[array initWithCapacity:20];
}
- (IBAction) readlog:(id)sender {
for (int i = 0; i < 20; i++) {
NSDictionary *d = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingFormat:#"/%d.log",i]];
[array addObject:d];
}
}
- (IBAction) writelog:(id)sender {
for (int i = 0; i < 20; i++) {
NSMutableDictionary *d = [NSMutableDictionary dictionary];
NSString *name = [NSString stringWithFormat:#"testfile%d", i];
[d setObject:[NSDate date] forKey:#"date"];
[d setObject:[path stringByAppendingFormat:#"/%d.log", i] forKey:#"path"];
[d setObject:name forKey:#"name"];
[d writeToFile:[path stringByAppendingFormat:#"/%d.log", i] atomically:YES];
}
and I bind my tableview column with appdelegate.array with keypath name/path/date
but it doesn't show any data in the array.. is there anything wrong here?
Thanks!
You haven't created an array.
init methods, including NSMutableArray's initWithCapacity:, initialize an existing (freshly-created) instance. You haven't created one, so you're sending that initWithCapacity: message to nil, which means it has no effect.
You need to create the array, then initialize it, then assign it to your array variable, preferably all in the same line.
There's also the issue that your table view will have already asked for the array by the time you receive the applicationDidFinishLaunching: message. You don't have one yet, so it gets nothing; by the time you create one, it has already asked you for it and gotten its answer, and does not know that it should ask again.
Create your array in init or initWithCoder: (I believe you will need the latter if your app delegate is in a nib), and implement and use Key-Value-Coding-compatible accessor methods to fill the array with values. When you send yourself accessor messages, you'll cause KVO notifications that will tip off the table view that it needs to ask for the array again. Assigning directly to the instance variable will not cause this effect.
A couple of other things…
You have three [path stringByAppendingFormat:#"/%d.log", i] expressions in two different methods. Don't repeat yourself. Move that to a method named something like logFileNameWithLogFileNumber: and send yourself that message to generate the filename. This will make the code both clearer and easier to maintain.
Finally, as a matter of style, you should not use stringByAppendingFormat: or stringWithFormat: to construct paths. Use stringByAppendingPathComponent: (in this case, together with stringWithFormat: to generate the filename). Clarity and pathname-separator-independence are virtues.
Related
I am having an issue with NSMutableArray wiping its contents.
Consider my code: (int i; is in my file's .h as is NSMutableArray* newFileControllerArray)
-(void)awakeFromNib{
i = 0;
newFileWindowControllerArray = [[NSMutableArray alloc]init];
}
-(IBAction)newFileMenubar:(id)sender{
[newFileWindowControllerArray addObject:[[NewFileWindowController alloc]initWithWindowNibName:#"NewFileWindowController"]];
NSUInteger elementsInArray = [newFileWindowControllerArray count];
NSLog(#"%lu",(unsigned long)elementsInArray);
[[newFileWindowControllerArray objectAtIndex:i] showWindow:nil];
}
-(IBAction)OKButtonClicked:(id)sender{
NSUInteger elementsInArray = [newFileWindowControllerArray count];
NSLog(#"THERE ARE %lu ELEMENTS IN THE ARRAY",(unsigned long)elementsInArray);
}
The first method called (other than awakeFromNib:) is newFileMenubar: This will add one element to the array. I can confirm that this works because 1 is printed in the console. However, once OKbutton is called and I print out the number of elements in my array it says that no elements are in the array. Why is that?
Am I missing something very obvious here? Why does my array reset itself?
EDIT:
The comments have gotten long and unwieldy so here is the code w/NSLogs and outputs:
-(void)awakeFromNib{
i = 0;
newFileWindowControllerArray = [NSMutableArray array];
NSLog(#"self=%p, array=%p", self, newFileWindowControllerArray);
}
-(IBAction)newFileMenubar:(id)sender{
[newFileWindowControllerArray addObject:[[NewFileWindowController alloc]initWithWindowNibName:#"NewFileWindowController"]];
[[newFileWindowControllerArray objectAtIndex:i] showWindow:nil];
i++;
NSLog(#"self=%p, array=%p", self, newFileWindowControllerArray);
}
-(IBAction)OKButtonClicked:(id)sender{
NSUInteger elementsInArray = [newFileWindowControllerArray count];
NSLog(#"self=%p, array=%p", self, newFileWindowControllerArray);
[documentController newDocument:sender];
[[newFileWindowControllerArray objectAtIndex:i]close];
}
When the program launches, this is the output: self=0x100141480, array=0x100140f30
This should be coming from awakeFromNib:
The next method called is newFileMenubar:
The output from this is
self=0x1001ac990, array=0x1005228a0 and immediately after self=0x100141480, array=0x100140f30
The last method called is OKButtonClicked:
The output from the last method (OKButtonClicked:) is self=0x1001ac990, array=0x1005228a0
As you can see from the code, the name of the array doesn't change, but my outputs beg to differ? What could cause this?
There are good clues in your log output. There are multiple instances of the view controller (see the different values for 'self'?). They each have their own array. See this code...
-(IBAction)newFileMenubar:(id)sender{
[newFileWindowControllerArray addObject:[[NewFileWindowController alloc]initWithWindowNibName:#"NewFileWindowController"]];
When you press the button associated with that action, your app builds another view controller and places it in the array. That view controller gets the awake from nib message and allocates another array, and so on.
To confirm this, change the code as follows:
-(IBAction)newFileMenubar:(id)sender{
[newFileWindowControllerArray addObject:#"Hello world"];
// and comment this out, for now:
// [[newFileWindowControllerArray objectAtIndex:i] showWindow:nil];
In the other methods, comment out your expectations that the array has anything other than strings in it, and see what you get. e.g. ...
- (IBAction)OKButtonClicked:(id)sender {
NSUInteger elementsInArray = [newFileWindowControllerArray count];
NSLog(#"self=%p, array=%p", self, newFileWindowControllerArray);
[documentController newDocument:sender];
// and comment this out, for now:
// [[newFileWindowControllerArray objectAtIndex:i]close];
// instead...
NSLog(#"danh thinks my array will be ok: %#", newFileWindowControllerArray);
}
You probably do not mean to create another view controller on every button press, but I'm not sure what function you do want. Maybe you want an array of views? (To create many view controllers under the control of another, you'll want to read up on container view controllers, here).
Due to an example from Apress,for a search criteria,it has a soultion:it will firstly get a mutable copy and search the string and then will remove objects that are not in range of that string.
in the Mutable DeepCopy it has function as follow:
#implementation NSDictionary(MutableDeepCopy)
-(NSMutableDictionary *)mutableDeepCopy{
NSMutableDictionary *returnDict=[[NSMutableDictionary alloc]initWithCapacity:[self count]];
NSArray *keys=[self allKeys];
for(id key in keys)
{
id oneValue=[self valueForKey:key];
id oneCopy=nil;
if([oneValue respondsToSelector:#selector(mutableDeepCopy)])
oneCopy=[oneValue mutableDeepCopy];
else if([oneValue respondsToSelector:#selector(mutableCopy)])
oneCopy=[oneValue mutableCopy];
if(oneCopy==nil)
oneCopy=[oneValue copy];
[returnDict setValue:oneCopy forKey:key];
}
return returnDict;
}
#end
when I don't understand any code I will debug it,so I debugged it and in the beginnings when it wants to show the whole array,in the if statement it will go to oneCopy=[oneValue mutableCopy]; I want to know that why it will choose this selector and not the MutableDeep selector? I can't understand this function totally.what is the main purpose of this function?
for searching it has this function
-(void)handleSearchForTerm:(NSString *)searchTerm
{
NSMutableArray * sectionsToRemove=[[NSMutableArray alloc]init];
[self resetSearch];
for(NSString * key in self.keys)
{
NSMutableArray * array=[names valueForKey:key];
NSMutableArray *toRemove=[[NSMutableArray alloc]init];
for(NSString * name in array)
{
if([name rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location==NSNotFound)
[toRemove addObject:name];
}
if([array count]==[toRemove count])
[sectionsToRemove addObject:key];
[array removeObjectsInArray:toRemove];
}
[self.keys removeObjectsInArray:sectionsToRemove];
[table reloadData];
}
and for reset the search and recovering whole it has this function below.
my problem is that in self.keys we removed objects with help of handleSearchForTerm but when we go to resetsearch it will return again those whole keys without any removal maybe my main problem is that I can't understand Mutablecopy and DeepMutableCopy.
-(void)resetSearch{
NSMutableDictionary *allNamesCopy=[self.allNames mutableDeepCopy];
self.names=allNamesCopy;
NSMutableArray *keyArray=[[NSMutableArray alloc]init];
[keyArray addObjectsFromArray:[[self.allNames allKeys]sortedArrayUsingSelector:#selector(compare:)]];
self.keys=keyArray;
}
They both have some compare & contrast features...
First comparing: Both are mutable, you can alter the object itself.
Second contrasting/differences:
MutableCopy: Similar to Call-by-Reference.
MutableDeepCopy: Similar to Call-by-Value.
A copy of a container (e.g. NSArray) copies only the pointers to objects and sends them retain.
A mutableCopy does the same thing, but the resulting container can be modified, i.e. Pointers can be added or removed.
A deep copy would also make copies of the individual elements.
Take for example an NSArray of NSMutableString instances. You cannot modify the array itself, but you can alter each mutable string. Now if you copy the array and modify one of the strings then the copied array also has the changes because it's pointers point to the same instances as the first array.
If you deep copy the array and alter a mutable string, then the new array elements are not modified. In fact copy always makes an immutable copy.
I would like to make a history list in my app. I have surfed and looked at many questions here, but so far I haven´t found the answer to my question.
My codes:
-(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
NSLog (#"search bar text did end editing!");
NSString *bookmarked = mySearchBar.text;
NSMutableArray *bookmarkl = [[NSMutableArray alloc] init];
int i;
for (i = 0; i < [bookmarkl count]; i ++)
{
[bookmarkl addObject:bookmarked];
}
for (NSString *sa in bookmarkl);
NSLog (#"whole array: %#", bookmarkl);
}
But all I get is one string at a time. What is wrong?
I want to add a new string to the mutable array, how should I do this?
You are creating a new list every time the function is called. You should add bookmarkl to your class as a property and only create it once during initialization. If you want the history to persist when your app is closed you will also want to write the data to a file. You can use [NSMutableArray arrayWithContentsOfFile] to load the history and writeToFile to save it.
i am adding the arrays to an nsmutabledictionary but its giving 0 count
i allocated the dictionary in viewDidLoad method.
can any body help me out please..
thanks.
- (void)creatingDictionary{
songsDict = nil;
NSMutableArray *hrSongs = [[NSMutableArray alloc]init];
for(int i=0;i<[onAirSongs count];i++){
for(int j=i;j<[onAirSongs count];j++){
Song *a1 =(Song *)[onAirSongs objectAtIndex:i];
Song *a2 =(Song *)[onAirSongs objectAtIndex:j];
if(a1.cellId == a2.cellId)
[hrSongs addObject:a2];
else{
[songsDict setObject:hrSongs forKey:[NSString stringWithFormat:#"%d",i]];
hrSongs = nil;
i=j-1;
break;
}
}//inner for
}//out for
[hrSongs release];
NSLog(#"songsdictionary count....%d",[songsDict count]);
}
You're setting songsDict to nil. You can't add an array to a dictionary that doesn't exist.
blindJesse is correct, but there are more problems here:
You are setting songsDict to nil. In Objective-C, nil-eats-messages and, thus, you are silently not doing anything at all when you [songsDict setObject:.....].
In the middle of the loop, you hrSongs = nil;. Subsequent iterations through the loop will happily call addObject: on nil, doing nothing at all.
This whole thing seems kinda weird; what does onAirSongs contain? Cells? This seems like you haven't really preserved any kind of Model-View-Controller separation. It might work, but you are in for a severe maintenance headache down the road if you ever have to change your UI.
I want to use NSMutableDictionary to cache some data i will use later. My custom object is following:
#interface MyData : NSObject {
NSRange range;
NSMutableArray *values;
}
#property (nonatomic, retain) NSMutableArray *values;
and implement:
- (id)init {
if (self = [super init]) {
values = [[NSMutableArray alloc] init];
}
return self;
}
and when i wanna cache it, i use it like this:
NSMutableDictionary *cache = [[NSMutableDictionary alloc] init];
NSString *key = #"KEY";
MyData *data = [[MyData alloc] init];
// save some data into data
[data.values addObject:"DATA1"];
[data.values addObject:"DATA2"];
//... ...
[cache setObject:data forKey:key];
My questions is the count of cache.values is zero when i retrieve this object later as follow:
[cache objectForKey:#"KEY"];
i can retrieve "data" and the object's memory address is the same as the address when i put it into cache.
what's wrong? i need some kind guys help, any info is helpful. thanks
As Carl Norum pointed out, you're passing C strings to addObject:. addObject:, as its name suggests, requires a pointer to a Cocoa object; a C string is a pointer to characters. You need to pass NSString objects there; for literal strings, this simply requires prefixing them with #: "Fred" is a constant C string, whereas #"Fred" is a constant NSString object.
Is cache an instance variable? It looks like it's not; it appears to be a local variable, which means you're creating a new dictionary object every time. That's why there's nothing you've added previously (to previous dictionaries) in the new one. It also means you're leaking those previous dictionaries, since you're not releasing them (not in the code you showed, anyway).
Make cache an instance variable and only create the dictionary when you don't already have one (i.e., when cache == nil). Creating the dictionary in your init method is one good way. And make sure you manage its lifetime appropriately, so you don't leak and/or crash.
First of all your objects your adding don't look right it should have an # before the string. Like #"DATA1"
Second when you add an object to a dictionary or an array it does not make an actual copy of it. It just creates a pointer to it so if those objects are destroyed or moved somewhere also they are also gone out of your dictionary. A better way to make a cache of your values would be to copy the objects like so:
MyData* cache = [[MyData alloc] init];
for (int i = 0; i < [data.values count]; i ++){{
[cache.values addObject:[NSString stringWithString:[data.values objectAtIndex:i]]];
}
Don't use a dictionary in this situation.