I am declaring an instance of NSString named *strKey and #synthesize and i pass one string value to it.
But when I want to send that string to another class. it is giving me error of
Here is my code
Assigning one value to the strKey here
-(IBAction)getLocal:(UIButton *)sender
{
[self startDeckDownload];
GroupDeckExpandableViewObject *objConfiguration=[[GroupDeckExpandableViewObject alloc] init];
GroupListCell *celltest=(GroupListCell *)sender.superview.superview.superview;
if(celltest != nil){
celltest.viewDownload.hidden =NO;
celltest.viewGetLocal.hidden=YES;
//objConfiguration.vwWelcome=celltest.viewWelcome;
objConfiguration.vwGetLocal=celltest.viewGetLocal;
objConfiguration.vwDownLoad=celltest.viewDownload;
strKey=[NSString stringWithFormat:#"%d",[sender tag]]; // i am assign one value to the string here.
[delegate setGroupViewConfiguration:objConfiguration withtag:[NSString stringWithFormat:#"%d",[sender tag]]];
}
}
passing to another class(using protocol).
-(void)setDownloadProgress:(float)progress
{
[delegate setDownloadProgress:progress withkey:strKey];
}
Here is the definition to the protocol method.
#protocol groupListCellDelegate<NSObject>
#optional
- (void) setGroupViewConfiguration:(id)objConfiguration withtag:(NSString *)key;
-(void)setDownloadProgress:(float)progress withkey:(NSString *)key;
#end
I use this method in my GroupView.m
#pragma mark - delegate method
-(void)setGroupViewConfiguration:(id)objConfiguration withtag:(NSString *)key
{
[arrGroupViewConfiguration setValue:objConfiguration forKey:key];
GroupDeckExpandableViewObject *objgetviews=[arrGroupViewConfiguration valueForKey:key];
[tblData reloadRowsAtIndexPaths:[NSArray arrayWithObjects:objgetviews.cellIndexPath, nil] withRowAnimation:UITableViewRowAnimationNone];
// [tblData reloadData];
}
-(void)setDownloadProgress:(float)progress withkey:(NSString *)key
{
NSLog(#"Reloaded...");
progress_percent = progress;
GroupDeckExpandableViewObject *objgetviews=[arrGroupViewConfiguration valueForKey:key];
NSLog(#"%d",objgetviews.cellIndexPath.section);
[tblData reloadRowsAtIndexPaths:[NSArray arrayWithObjects:objgetviews.cellIndexPath, nil] withRowAnimation:UITableViewRowAnimationNone];
// [tblData reloadData];
}
What am I missing here?
You own the object returned from initWithFormat which you are responsible for releasing, but you don't own the object returned from stringWithFormat which returns an autoreleased string and so do not need to release it (if you do want to have ownership of it, you must retain it).
So for resolving your issue try to assign your value like this,
strKey=[[NSString alloc] initWithFormat:#"%d",[sender tag]];
Related
I have this code:
// .m
- (void)viewDidLoad {
NSMutableArray *array = [[NSMutableArray alloc] init];
[self addToArray];
}
- (void)addToArray {
NSString *stringA;
[stringA isEqualToString:#"door"];
NSString *stringB;
[stringB isEqualToString:textField.text];
[array addObject:stringA];
if ([stringA isEqual:stringB]) {
[stringA isEqual:nil];
[tableView reloadData];
} else {
[array addObject:stringB];
[tableView reloadData];
}
}
When I call the method addToArray it keeps returning me an error called Thread 1: Program recived signal "EXC_BAD_ACCESS", and the debugger output says : Single stepping until exit from function objc_msgSend, which has no line number information. at the line [self addToArray]. Any idea of how to solve it? I have wasted to much time with it, please help me!
As was said by others, array should be an instance variable or property of the class, declared in the .h file:
#property (strong) NSMutableArray *array;
Or, without ARC:
#property (retain) NSMutableArray *array;
Now you #synthesize array; in your implementation file and can access it from anywhere. Then you can do:
- (void) viewDidLoad
{
self.array = [[NSMutableArray alloc] init];
[self addToArray];
}
You seem to assume that isEqualToString does an assignment. It doesn't, it checks strings for (textual) equality. Try this:
- (void) addToArray
{
NSString *stringA = #"door";
NSString *stringB = textField.text;
[array addObject: stringA];
if (![stringA isEqualToString: stringB])
[array addObject: stringB];
[tableView reloadData];
}
These two variables are uninitialized and will cause you big problems:
NSString *stringA;
[stringA isEqualToString:#"door"];
NSString *stringB;
[stringB isEqualToString:textField.text];
You have not assigned anything to either stringA or stringB. Besides the result of your call to isEqualToString is never used.
Two things I can notice in your code:
1) Make array a class variable, so you can access it from your -[addToArray] method. Better do this in your .h file, for example:
#interface MyViewController : UIViewController {
#private
// ...skipped...
NSMutableArray * array;
// ...rest of class skipped...
}
#end
Then, in your .m file the method should look like this:
// .m
- (void)viewDidLoad {
array = [[NSMutableArray alloc] init];
[self addToArray];
}
And don't forget to release the array:
- (void)dealloc {
[array release];
[super dealloc];
}
2) Do not mess up -[NSString isEqualToString:] method with simple assigment to a variable. So in your -[addToArray] method, for example, replace this:
NSString *stringA;
[stringA isEqualToString:#"door"];
with this:
NSString *stringA = #"door";
And this:
NSString *stringB;
[stringB isEqualToString:textField.text];
with this:
NSString *stringB = textField.text;
3) Check the logic of -[addToArray] method - it is not very clear what are you going achieve.
I am parsing some JSON from the internet and then adding them to an array which is the datasource for my UITableView. I am not sure when I should be releasing my array?
.h: items
#property(nonatomic,retain)NSMutableArray* items;
.m: connectionDidFinishLoading
// fetch succeeded
NSString* json_string = [[NSString alloc] initWithData:retrievedData encoding:NSUTF8StringEncoding];
//Check ST status
int status = [[[[json_string objectFromJSONString] valueForKey:#"response"] valueForKey:#"status"]intValue];
//NSLog(#"Status: %d", status);
items = [[NSMutableArray alloc] init];
NSDictionary* messages = [[NSDictionary alloc] init];
switch (status) {
case 200:
messages = [[[json_string objectFromJSONString] valueForKey:#"messages"] valueForKey:#"message"];
for (NSDictionary *message in messages)
{
[items addObject:message];
}
[self.tableView reloadData];
break;
default:
break;
}
One, you might want to declare items as an instance of NSMutableArray if you intend to call addObject: on it.
Two, declare it as a property so that if you end up getting it multiple times the older value will be released when you do.
self.items = [NSMutableArray array];
And the correct point of releasing it would be dealloc.
Probably you don't want to release it immediately if you:
use didSelectRowAtIndexPath: method for detail views and pass this data to them
define custom UITableViewCell styles in cellForRowAtIndexPath: method
use this data elsewhere
Best practice is declare an instance variable and synthesize it in .m, use in appropriate operations and release in dealloc method.
One possible release point that you could use is where you refresh your data that shown on table.
Example:
I get dictionaries in an array from an API in my app and use something like that.
MyTableViewController.h
#interface MyTableViewController {
NSMutableArray *items;
}
#property (nonatomic, retain) NSMutableArray *items;
#end
MyTableViewController.m
#implementation MyTableViewController
#synthesize items;
- (void)dealloc
{
[items release];
[super dealloc];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [items count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"FilesCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
cell.textLabel.text = [[items objectAtIndex:indexPath.row] valueForKey:#"name"];
cell.imageView.image = [UIImage imageNamed:[[NSString alloc] initWithFormat:#"filetype_%#.png", [[items objectAtIndex:indexPath.row] valueForKey:#"type"]]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MyDetailViewController *detailViewController = [[MyDetailViewController alloc] initWithNibName:#"MyDetailViewController" bundle:[NSBundle mainBundle]];
detailViewController.item = [items objectAtIndex:indexPath.row];
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
detailViewController = nil;
}
}
- (void)getItems
{
[items release];
items = [[NSMutableArray alloc] init];
//Do some requests here
for (NSDictionary *dict in results)
{
[items insertObject:dict atIndex:0];
}
[self.tableView reloadData];
[self stopLoading];
}
#end
Releasing at wrong places some time lead to memory leaks, before allocation itself u can have a condition like if() { [...release]}.Not tested but this kind of release avoid leaks.
The most common is to have the items variable as an attribute of your class, once you will probably need it to use in your tableView:cellForRowAtIndexPath: method.
So, having it as an attribute variable you can release it on the dealloc method.
It's clear that your array item will be used by UITableView to show data.
First declare it as instance variable in your .h class.
.h class
#interface MyClass
{
MSMutableArray* items;
}
#property(nonatomic,retain) MSMutableArray* items;
#end
In your .m class.
#synthesis iMyArray;
And you code for filling the array should be
NSMutabelArray* itemsTemp = [[NSMutabelArray alloc] initWithCapacity:1];
messages = [[[json_string objectFromJSONString] valueForKey:#"messages"] valueForKey:#"message"];
[json_string release];
for (NSDictionary *message in messages) {
NSLog(#"%#",[message valueForKey:#"body"]);
[itemsTemp addObject:message];
}
self.items= itemsTemp;
[itemsTemp release];
itemsTemp = nil;
[self.tableView reloadData];
Now in dealloc release your array instance.
-(void) dealloc
{
if(items )
{
[items release];
items = nil ;
}
[super dealloc];
}
Proper way is make it property in .h class, since you have declared it as property: remember one thing always alloc a property by using self.
your statement items=[[NSMutableArray alloc] init];
is wrong.(use self) also since your property is retain type the using alloc on it increase retain count.that gives you a leak.
so use in this way in viewDidLoad
NSMutableArray *tempArray=[[NSMutableArray alloc] init];
self.items=tempArray;
[tempArray release];
then release your items array in dealloc and set it nil in viewDidUnload
- (void)viewDidUnload {
[super viewDidUnload];
self.items=nil;
}
- (void)dealloc {
[self.items release];
[super dealloc];
}
Hope now you can understand how you should use this.
According to Apple's documentation of UITableView reloadData method:
"[...] For efficiency, the table view redisplays only those rows that are visible"
That means yo should not release the items array as long as the table is being used, i.e. you have to declare the array as a property.
First because if you scroll the view, you will still need the items information to display the rows below or above.
And second, because by being a property you ensure that a previous value is going to be released if you happen to assign a new value to items.
Finally, the common place to release a property is in the dealloc method and depending on your implementation in viewDidUnload method.
I am building a undo/redo functionality for my app. I am using the NSInvocation method of NSUndoManager.
This is how I build the invocation
NSNumber *firstState = [NSNumber numberWithInt:fsNumber];
NSInvocation *initialState = [self restoreStateInvocation:firstState];
// ... the code continues...
these are the methods relates
- (NSInvocation *) restoreStateInvocation:(NSNumber*)number {
NSMethodSignature *executeMethodSignature = [self methodSignatureForSelector:
#selector(makeUNDO:)];
NSInvocation *moveInvocation = [NSInvocation invocationWithMethodSignature: executeMethodSignature];
[moveInvocation setTarget:self];
[moveInvocation setSelector:#selector(makeUNDO:)];
[moveInvocation setArgument:&number atIndex:2];
return moveInvocation;
}
- (void) makeUNDO:(NSNumber*)number {
int num = (int)[number intValue];
// code crashes at this line... number appears to be deallocated at this time
//
...
}
when the UNDO/REDO calls initialState the app crashes on the first line of makeUNDO, as pointed on the code.
how can I retain number without leaking?
thanks.
the correct answer is to add the following line to restoreStateInvocation...
[moveInvocation retainArguments];
you could retain NSNumber object because it's inherited from NSObject.
NSNumber* myNumber = [number retain];
you also need to release once you finished using myNumber.
[myNumber release].
EDITED:
Use the approach described below...
you could have myNumber as your class member.
in .h
#interface myClass {
NSNumber* myNumber;
}
............
............
#property(nonautomic,retain) NSNumber* myNumber;
#end
In implementation file (.m file).
#implementation myClass
#synthesize myNumber;
-(void) dealloc{
if(myNumber)
{
[myNumber release];
myNumber = nil ;
}
}
- (void) makeUNDO:(NSNumber*)number {
self.myNumber = nil;
self.myNumber = number;
int num = (int)[self.myNumberintValue];
NSLog(#"My Number -->%d", num )
//
...
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
PushOnStackViewController *vc = [[PushOnStackViewController alloc] init];
vc.key = [self.keys objectAtIndex:[indexPath row]];
[self.navigationController pushViewController:vc animated:YES];
}
and
in the init method of the PushOnStackViewController class I have
- (id)init {
self.navigationItem.title = key;
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"texts" ofType:#"plist"]];
self.keys = [dict objectForKey:key];
[dict release];
NSLog(#"%#", self.key);
NSLog(#"%i", [self.keys count]);
return self;
}
But why can't I access the self.key? It returns null, even though it has been set(it is a string).
When I access it in viewDidLoad it returns the correct value...anything I haven't read, or am I doing anything wrong?
Thanks in advance.
You can't access self.key inside the -init function because at that point it hasn't been set yet. you are setting it afterwards:
PushOnStackViewController *vc = [[PushOnStackViewController alloc] init]; // init runs here.
vc.key = [self.keys objectAtIndex:[indexPath row]]; // but you don't set the key property until here.
You might try adding a "key" parameter to the init function, like so:
-(id)initWithKey:(NSString*)key {
self = [super init];
if (self) {
self.key = key;
...etc...
}
return self;
}
Your init method is called before you set the property. Get rid of that init method and move your code into viewDidLoad to ensure that it's called after you've done all the property setup.
Don't create new init method for a UIViewController unless you know what you're doing. It's much easier to create a property (like you've done) and access that property inside the viewDidLoad method.
Update: Unfortunately the help offered below did not solve my problem of sharing a class property across functions. Can anyone else suggest a possible problem? Here's the latest code:
Header .h:
#interface FirstViewController:UIViewController <UITableViewDataSource, UITableViewDelegate, UITabBarControllerDelegate> {
NSDictionary *sectorDictionary;
NSInteger sectorCount;
}
#property (nonatomic, retain) NSDictionary *sectorDictionary;
- (id)initWithData:(NSMutableDictionary*)inData;
Implementation .m:
#synthesize sectorDictionary;
- (id) testFunction:(NSDictionary*)dictionary {
NSLog(#"Count #1: %d", [dictionary count]);
return nil;
}
- (id)initWithData:(NSMutableDictionary *)inData {
self = [self init];
if (self) {
[self testFunction:inData];
// set the retained property
self.sectorDictionary = inData;
}
return self;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Count #2: %d", [self.sectorDictionaryCopy count]);
return [self.sectorDictionaryCopy count];
}
Console output:
2010-05-05 20:11:13.126 JSONApp[17378:207] Count #1: 9
2010-05-05 20:11:13.130 JSONApp[17378:207] Count #2: 0
Following up on this question about sharing an object between classes, I now need to figure out how to share that object across various functions in a class.
First, the setup: In my App Delegate I load menu information from JSON into a NSMutableDictionary and message that through to a view controller using a function called initWithData. I need to use this dictionary to populate a new Table View, which has methods like numberOfRowsInSection and cellForRowAtIndexPath.
I'd like to use the dictionary count to return numberOfRowsInSection and info in the dictionary to populate each cell. Unfortunately, my code never gets beyond the init stage and the dictionary is empty so numberOfRowsInSection always returns zero.
I thought I could create a class property, synthesize it and then set it. But it doesn't seem to want to retain the property's value. What am I doing wrong here?
In the header .h:
#interface FirstViewController:UIViewController <UITableViewDataSource, UITableViewDelegate, UITabBarControllerDelegate> {
NSMutableDictionary *sectorDictionary;
NSInteger sectorCount;
}
#property (nonatomic, retain) NSMutableDictionary *sectorDictionary;
- (id)initWithData:(NSMutableDictionary*)data;
#end
in the implementation .m:
#synthesize sectorDictionary;
- (id) testFunction:(NSMutableDictionary*)dictionary {
NSLog(#"Count #1: %d", [dictionary count]);
return nil;
}
- (id)initWithData:(NSMutableDictionary *)data {
if (!(self=[super init])) {
return nil;
}
[self testFunction:data];
// this is where I'd like to set a retained property
self.sectorDictionary = data;
return nil;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Count #2: %d", [self.sectorDictionary count]);
return [self.sectorDictionary count];
}
Output from NSLog:
2010-05-04 23:00:06.255 JSONApp[15890:207] Count #1: 9
2010-05-04 23:00:06.259 JSONApp[15890:207] Count #2: 0
Any init function should return self if it completes successfully. Change the return nil to return self at the end of initWithData.
-(id) initWithData:(NSMutableDictionary *)inData {
self = [self init]; // best practice use super for method with same name
if ( self ) {
self.sectorDictionary = inData;
}
return self;
}
The sectorDictionary member should not be mutable. If it changes, you need to call reloadData on the table. Your setter function should make an immutable copy.
-(void) setSectorDictionary:(NSDictionary *)inData {
NSDictionary *old = sectorDictionary;
sectorDicitonary = inData ? [[NSDictionary alloc] intWithDictionary:inData] : nil;
[self.table reloadData];
[old release];
}
I believe your problem lies within the line self.sectorDictionary = data.
You need to firstly allocate some memory for the dictionary you are creating, so a line like
self.sectorDictionary = [[NSMutableDictionary alloc] init]; is going to be necessary.
After you have initiated the new dictionary you need to populate it with the contents of the dictionary that was passed in.
So...
Try the line:
self.sectorDictionary = [[NSMutableDictionary alloc] initWithDictionary:data];
instead of:
self.sectorDictionary = data;
instead of
self.sectorDictionary = data;
try this way...
self.sectorDictionary = [[NSMutableDictionary alloc] init];
self.sectorDictionary = (NSMutableDictionary *) data;