Custom subview gives memory leak when analyzed - objective-c

I am trying to create my own custom subview for an app. The view is a subclass of UIView and contains a couple of UIButtons and a UITextField. The code actually builds and runs without problems, but when I do an "Analyze" of the code it gives me 4 potential memory leaks. I will be including this subview in a larger project so I want to nip any errors here before adding it to the bigger project. Here's the code where one of the leaks occurs:
- (void)viewDidLoad
{
[super viewDidLoad];
self.nameView.delegate = self;
[self.nameView.newName resignFirstResponder]; // this is line 60
[self.nameView setHidden:YES];
[self.nameView setNeedsDisplay];
}
The error it gives me is:"Potential leak of an object created on line 60"
Then if I click on that error is says: "Object allocated on line 60 is not referenced later in this execution path and has a retain count of +1, object leaked"
Thanks in advance for any help you can give me.
Gil
CocoaFu, thanks for the help - I am new to the site and I will try to give the proper feedback. As far as this problem goes I should have included a bit more information.
nameView is the name of my custom view
newName is the name of a UITextField in the view.
I will try then suggestions here and let you know if they work. Thanks again. This is a great site!

[self.nameView.newName resignFirstResponder];
is the same as:
[[[self nameView] newName] resignFirstResponder];
which means that newName is a method and methods that begin with new or copy are expected to return a retained instance. I suspect that newName does not do that but the rules say it does. The solution is to follow Objective-C naming rules: change the name newName.
Not, you don't say but perhaps newName is a property. Well, #synthesize creates accessor methods newName and setNetName.

Related

why setStringValue call works in viewDidLoad and viewDidAppear but not in other methods?

I am trying to change the value of the label here.
Can anyone explain me why the following method call:
[_detailLabel setStringValue:#"this is new label value"];
works if I make the call from viewDidLoad and viewDidAppear methods, but it doesn't work in the method that I created:
-(void)changeLabelValue : (NSString *) newVal {
[_detailLabel setStringValue:#"this is new label value"];
NSLog (#"Hello from changeLabelValue method");
}
Please note that when I call this method from anywhere from my code, the NSLog message IS displayed but the value of the label is not changed...
Any help is deeply appreciated.
Pointing me to resource where I can learn more on this subject will also do the trick, and will be also deeply appreciated.
Regards, John.
Make sure that changeLabelValue: is being called after viewDidLoad is called (or the view controller has been displayed). If you call it before the view is loaded, _detailLabel will be nil since it hasn't been loaded yet.

collectionView:cellForItemAtIndexPath: doesn't get called

I want to add new cells in my collection view, but nothing shows up when I add data.
I have a custom UICollectionViewLayout class, which has been working just fine, and I've been keeping dummy data in my datasource to adjust the layout. Now that I got rid of the dummy data, nothing's showing up.
Since the app didn't break and there weren't any warnings, it was difficult to track down where the problem was, and here's where I found a clue:
(UICollectionViewLayout class)
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSLog(#"ElementsInRect: – Visible cells info: %#", [self.collectionView.visibleCells description]);
...
}
Here, -visibleCells returns an empty array, even when I add data, call -reloadData and invalidate the layout. So I placed a breakpoint in -collectionView:cellForItemAtIndexPath:, and it turns out this method is not called at all. How did the cells show up before?
Any help would be appreciated.
The data source method, collectionView:numberOfItemsInSection:, has to return a non-zero number for collectionView:cellForItemAtIndexPath: to be called. When you had dummy data in your data source, it was. Now that you removed that dummy data, that method is probably returning 0. When you add data, it should put items into your data source, and then a call to reloadData should work. You should put a log in collectionView:numberOfItemsInSection:, and see what it's returning.
Okay, it turns out the issue was in UICollectionViewLayout. I doubt anyone else will be having this problem, but I'll write my answer for the sake of completeness:
I'd been tweaking my custom UICollectionViewLayout class, and after I'd thought that it was working well, I made the code look neat by deleting old code that was commented out, move methods, etc.
While doing that, I recalled having read somewhere that it's good practice to create attributes in -prepareLayout method, and return those attributes when -layoutAttributesForItemAtIndexPath: or -layoutAttributesForElementsInRect: is called. For me, it was a matter of moving a block of code, so I thought no biggie. And during this "cleaning process" I must have made a mistake.
What's really frustrating is that the code itself actually works regardless of where the attributes are created, and I can't tell what went wrong for the last few days.
The following is a snippet of code that I used to create the attributes objects. My initial question was asking why -collectionView:cellForItemAtIndexPath: was not called while executing the 3rd line. I did not change this part of the code, other than moving it around.
for (int i = 0; i < 3; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:self.topLayer];
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
if (cell) {
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
[self.array addObject:attributes];
} else {
NSLog(#"prepLayout: the cell doesn't exist for the index path {%d – %d}", indexPath.section, indexPath.item);
}
}
Number of Rows in Section - the count that can be used will determine if the cellForItemAtIndexPath gets called.
Initially when the view loads this will be called. Within the numberOfItemsInSection, if you have an array, the [array count] might return a nil value.
Complete the procedure where the array is populated, then reload the data in the collection view which will re-assess the numberOfItemsInSection. This can be done with the following code:
[self.myCollectionView reloadData];
"myCollectionView is the name given to the collection view item in your view"

how to call a method from a class in another class

I'm working to a new app for mac osx where i'm using a drag and drop system to let the user to input some files [this part works well] and i have a tabelView where i would like to display the paths of files inputed.
I have the next method in tabelViewController.m:
-(void)add{
NSLog(#"da");
[list addObject:[[Source alloc] init]];
[tableView reloadData];
}
In the DropView.m i included the tabelViewController.h and i'm trying to call the add method but it does nothing:
#import "TableViewController.h"
.....
- (void)concludeDragOperation:(id<NSDraggingInfo>)sender{
[self setNeedsDisplay:YES];
TableViewController *tvc;
[tvc add];
}
Can someone to figure out why it doesn't do anything ?
Edit1:
Ok after I fallow the answers, my concludeDragOperation method looks like this:
- (void)concludeDragOperation:(id<NSDraggingInfo>)sender{
[self setNeedsDisplay:YES];
TableViewController *tvc = [[TableViewController alloc] init];
[tvc add];
[tvc rD];
}
rD is a method from tableViewController which contain the reloadData method.
But it doesn't want to work it don't reload the table view.
Any ideea ???
tvc needs to point to an actual object. [[tvc alloc] init]
Otherwise you are simply calling add on nil. This doesn't cause your program to crash as you might expect in other languages. Try it out and see what happens.
it seems as if you missed a great chunk regarding how OOP and Objective-C work (seriously, no offense there).
What link is there between DropView.m and tableViewController.h do you have?
By typing TableViewController *tvc; all you are doing is creating a pointer. You are neither creating an object nor pointing to an object, you have just simply created a pointer that can eventually point to an object in memory of type tableViewController.
Solution:
What you will need to do, is to somehow create a link between the two classes. For instance, you could create a custom delegate method for DropView that could communicate with any class who uses that custom DropViewDelegate methods. So, you could create a delegate method that tells objects that follow that delegate protocol that you just concluded a drag operation. A tutorial how to do so can be found at my blog [it's a permalink].
I am happy to post code, or you can read it on my blog. Good Luck.

delegate not getting nil

I am working on a project in which I perform lazy loading of images. When the imagedownloader downloads the images,it sends the message to its delegate to handle the image. But when its delegate,which is a view controller, gets deallocated from memory,I dont want imagedownloader class to send messages to its delegate as its already dead. I need to know when can i set delegate of imagedownloader to nil?? My target is set to iOS4.0 so i cant use weak references. And i have many instances of imagedownloader stored in a dictionary ready to sent their delegate the completion message . I have to set delegte of all those stored instances to nil.For now i am doing
'
-(void)viewWillDisappear:(BOOL)animated
{
for(imagedownloader *imagedownloaderObj in dict)
{
imagedownloaderObj.delegate = nil;
}
[super viewWillDisAppear:animated]
}
but it crashes in the loop. Please help anyone...and sorry about my bad english but i hope you got it whats my problem..
You have a problem in your code - you are enumerating a dictionary which enumerates its keys, not its objects. Instead you want to do:
for(ImageDownloader *imageDownloader in [imageDownloaderDictionary allValues])
{
if (imageDownloader.delegate == self)
imageDownloader.delegate = nil;
} //note - I've adjusted naming to match Objective-C style conventions. It fits in better with the framework code now.
Also, I'd say to do this in dealloc instead. I don't know that you'll always get a viewWillDisappear: method before deallocating, on earlier version of iOS (including iOS4) you certainly couldn't guarantee that. And furthermore you don't want to waste time downloading the images again if you come back to that view.

Why gets this initializer argument destroyed?

I have an initializer with two arguments:
-(id) initWithSourceFrame:(CGRect)sourceViewFrame mappedFrame:(CGRect)mappedViewFrame {
CGRect copy = mappedViewFrame;
self = [super init];
if (self) {
// not able to access mappedViewFrame here..
// copy is ok
// doing initialization here...
}
return self;
}
It seemed to get wrong values from the second argument (mappedViewFrame). When looking for the error, I found out that mappedViewFrame gets destroyed (overridden in memory?).
This can be easily observed in the debugger:
Debugger Screenshot on flickr (I cannot post images yet)
The copy is still holding the original values, so using the copy was a workaround in this case. But of course I want to understand why this could happen.
The class is a direct subclass of NSObject, the whole project is a OS X native app.
The first argument was never destroyed. The problem does not relate to the values passed in. I switched them and it was always the second which was corrupted.
For example, I called the initializer with these example arguments (different from those in the debugger screenshots), and the error occured in the same way:
Mapper *mapper = [[Mapper alloc] initWithSourceFrame:CGRectMake(0, 0, 100, 100) mappedFrame:CGRectMake(0,0, 200,200)];
The method declaration:
-(id) initWithSourceFrame:(CGRect) sourceViewFrame mappedFrame:(CGRect) mappedViewFrame;
I'm somewhat new to Objective-C, so I am sorry if I missed something obvious. However, it looks strange that an argument does not keep valid during method invocation.
I stumbled upon a possible solution while I tried to reproduce the error using NSLog instead of the debugger.
The funny thing was, the logged values were correct. Just before the call to NSLog the debugger shows still the garbage output as in the screenshots. But after accessing the value (by NSLog or another programmatic access) the debugger shows the correct values.
So it looks more like a problem with the debugger. The wrong display of values is more likely to appear if some objects are allocated.
Remains the question: Why did my program not work? I now removed the 'copy' variable (from line 2) and access mappedViewFrame directly and everything works as expected. So the same code which didn't work before now works, I think this can be of two reasons:
There was another error in my implementation, which caused the wrong computation results. When introducing the 'copy' variable (after seeing the weird debugger output), I fixed the other error without noticing. Afterwards, I credited the fix to my new copying solution.
An unknown factor caused the variable to be distorted when I first encountered the problem and does not do so now. Well, to be shure I left the copy there, and check if the value remains unchanged. If this will happen, I will post it here. Until then, the first theory is much more likely.
This thing teached me not to trust the debugger, and instead use logging when in doubt, which is a pain. If anybody knows what causes the strange debugger output or how to fix it I would greatly appreceate it. If I have time I will do further look into when this does appear, if it happens just in xcode or is a problem of the gnu debugger in general.
I have tried this quickly in a few minutes. I've never really tried to create a Mac OS X project, just Foundation and iPhone projects, but the idea seems the same. I created a Cocoa Application project "Stack1". I'm assuming you have done the same, because I don't think a Foundation project would support CGRect structs without manually importing CGGeometry.h or something... Anyway, I set up a new Objective C class called "Mapper" and implemented the initWithSourceFrame:mappedFrame: method you specified.
Mapper.h
#import <Cocoa/Cocoa.h>
#interface Mapper : NSObject {
}
-(id) initWithSourceFrame:(CGRect) sourceViewFrame mappedFrame:(CGRect) mappedViewFrame;
#end
Mapper.m
#import "Mapper.h"
#implementation Mapper
-(id) initWithSourceFrame:(CGRect)sourceViewFrame mappedFrame:(CGRect)mappedViewFrame {
NSLog(#"height: %f, width: %f", mappedViewFrame.size.height, mappedViewFrame.size.width);
self = [super init];
NSLog(#"height: %f, width: %f", mappedViewFrame.size.height, mappedViewFrame.size.width);
if (self) {
NSLog(#"height: %f, width: %f", mappedViewFrame.size.height, mappedViewFrame.size.width);
}
return self;
}
#end
I didn't alter Stack1AppDelegate.h.
Stack1AppDelegate.m
#import "Stack1AppDelegate.h"
#import "Mapper.h"
#implementation Stack1AppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
Mapper *mapper = [[Mapper alloc] initWithSourceFrame:CGRectMake(0, 0, 100, 100) mappedFrame:CGRectMake(0,0, 200,200)];
}
#end
All my code did was allocate a Mapper instance as yours did and also use NSLog to print the width and height before [super init], after [super init], and also within the if (self) block.
Here's the log:
2011-01-23 16:09:07.167 Stack1[935:a0f] height: 200.000000, width: 200.000000
2011-01-23 16:09:07.170 Stack1[935:a0f] height: 200.000000, width: 200.000000
2011-01-23 16:09:07.170 Stack1[935:a0f] height: 200.000000, width: 200.000000
I've also uploaded the whole project here:
http://ak.net84.net/files/temp/Stack1.zip