Autorelease and "assign" properties in Objective-C? (On iPhone) - iphone

I have an instance of a UITableView, and a separate class that adheres to the delegate and datasource protocols. I'm doing this like so:
SubjectTableViewHandler *handler = [[[SubjectTableViewHandler alloc] init] retain];
tv.delegate = handler;
tv.dataSource = handler;
[handler autorelease];
I don't want to maintain the handler as an ivar, and if I take off the retain call at the end, when the autorelease happens, it is sent release, then added to the pool, which causes an EXC_BAD_ACCESS. So currently, the retain count is:
(1) At init: 1
(2) At retain: 2
(3) delegate/datasource properties are 'assign', still 2
(4) At autorelease: 1, now in autorelease pool.
But then since the properties are 'assign', they will never be released, the retain count will never hit 0, and the handler will never be deallocated anyway. Is there any more efficient way to accomplish this than maintaining the handler as an ivar and releasing it in the dealloc method?

When you initialize the object using init, you are claiming ownership of it and there is no reason to call retain. You also don't want to call autorelease since that will cause the object to be released at the of the run loop.
Since you need to keep the handler (so that your tableView can call the delegate/dataSource methods) and a reference to the handler after the method returns (so you can release it when you are done showing the tableView), the cleanest approach would be to make it an ivar.

The only solution that I can see is, as you mentioned, to make it an ivar and store, allocate, and deallocate it in parallel with the table.

It all depends on your use of table view.
Common practice is you create a view controller which is a delegate to table and table may be a controller's member.
As another alternative you can inherit from UITableView and make it delegate of itself.
Sometimes it's better to use a singleton delegate.
Also, in table delegate methods first argument is tableview, so one delegate object can service multiple tables.

Related

Set UITextFieldDelegate & release after UITextField dealloc, self contained

I have created a class that implements UITextFieldDelegate so I only need to use [MyDelegateClass setDelegate:textField someConfigparams:...] in order to use it. Inside it will create an object and assign to the textFieldDelegate.
The problem appears when releasing: the setDelegate method in UITextField doesn't retain the object so I can't just autorelease it. I could keep a reference outside to release it but it is ugly and prone to error since I need to do an extra work outside the delegate class. Subclassing UITextField is a bad option since I would need to use always this subclass which could conflict with others.
Is there any way I could release the delegate object when UITextField object is destroyed without relying to code outside MyDelegateClass?
I assume [MyDelegateClass setDelegate:textField someConfigparams:...] creates and instance of MyDelegateClass and assigns that instance as the delegate on the provided textField. What i would do is make the instance of MyDelegateClass a singleton, meaning there is only ever 1 instance.
Basically what will happen in this case is that MyDelegateClass will retain a strong reference to a single static instance of itself and that instance will stay alive for as long as your application stays alive or until you purposefully release it. It is perfectly ok to have one instance of an object be the delegate for many textFields. The memory impact of having a single instance of this class alive always is going to be constant, i.e. not growing, and very small.

ARC delegate issue

I need to download some images from server. So I created a seperate class to handle NSURLConnection delegates.
At the end of didFinishDownloadingData, I called a delegate method like [(id)delegate performSelectorselector(finished:) withObject:receivedData]
I have a view controller called ListImages.
I created the above connection class from ListImages class and assigned connection.delegate = self. After image loaded from server the method -(void)didFinishDownloadingData:(NSData *)data; was called successfully, and I could display that image.
My problem starts now. To handle some common tasks, I created a new class called SharedMethods which is a subclass of NSObject. I allocated connection class as
Connection *conn = [[Connection alloc]init];
conn.delegate = self;
[conn startDownload]; //called a method which starts nsurlconnection.
I am using ARC so not released that object. My applicaion got exception in method, (In Connection class)
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[(id)delegate performSelectorselector(finished:) withObject:receivedData]; //Got an exception in this line
}
The exception was [SharedMethods retain] message send to deallocated object. I dont think I have released anything because I am using ARC.
There was also a problem while callingUIAlerView delegates inside a Class which is a subclass of NSobject. It is not called any how. My doubt is, is there any problem with using a NSObject sublass? Is there anything to consider when using NSObject sublass ?
Thanks in advance.
Using ARC doesn't mean than objects never receive the release method, or that they never get deallocated. It just means that you don't have to make explicit calls to retain and release, and that happens automatically.
The problem here is that your objects are getting deallocated because no one is owning them. Your specific problem is that SharedMethods is being deallocated because it's not getting retained, but I can't show you how exactly that's happening because you didn't post the relevant code.
I can, however, show you that you're not managing your Connection properly, and hopefully that can help you figure out what you're doing wrong with SharedMethods.
So you create Connection with alloc init, which with retain-release code would give it a retain count of 1, but since you're not using ARC anymore that's not really relevant. Unless some other object asserts ownership of the Connection, ARC will automatically insert a call to release to bring the retain count back to 0 (it's kind of like if ARC automatically inserted an autorelease).
Since you don't assign Connection to a strong or retain property, or put it in a collection, no other object is asserting ownership to it. So once execution reaches the end of the scope where the variable conn is defined, it will get released and deallocated.
So in ARC, much like in manual retain-and-release code, you still need to make sure objects are owned by some other object in order for them to stick around. The only difference is that you don't need to manually call retain and release, you just have to think about the object ownership graph—which object is owned by which other object—and make sure that any object you want to stick around is owned by some other object.
So to reiterate, you need make sure that SharedMethods is owned by some other object.

Keeping a class running in the background when another has been started

I will try and explain this as best as possible if I have this code here
ViewTwoController *home = [[ViewTwoController alloc] initWithNibName:#"contentscreen" bundle:nil];
[self presentModalViewController:home animated:YES];
[home release];
I will start a new .m and .h class. What I would like to try and do however is when this is called, have the .m and .h class where it was called from running in the background so I do not lose data.
The best example I can think of is with Android. If you begin a new class, and don't add the finish() statement in the class the call was made from, the previous class runs behind the current class (that was pushed to the front) and maintains all the data it originally had, so if you hit a return button, you will see the information you had moments ago. Is this possible? I can try add more detail if people cannot understand what I am trying to do.
You need to understand a objects life cycle a little better.
An object is brought into existence generally with a 2 part process.
allocation - (grabbing the memory for the object and its members)
initialization - (setting the object up for use)
This can be combined into single step with the +new class method which combines alloc and init.
lets introduce an example class called MyClass and an object of that class called myObject. By convention classes start with uppercase letters and objects start with lowercase letters. So Without further ado, some code:
MyClass * myObject;
this this makes an object pointer, but doesn't allocate any memory for it or direct the pointer to reference anything.
myObject = [[MyClass alloc] init];
this actually creates an instance of MyClass, passes the -init message to it, then assigns the return value of the init message to myObject. At this point the reference count of this object is 1.
myObject can potentially go out of scope but that alone doesn't free the memory that was allocated during the alloc step.
in order to free that memory a release message will need to be passed to the object.
[myObject release];
The effect of release is to decrement the reference count, if the reference count is already 1 then the object will then be passed the -dealloc indicating that it is actually being freed.
back to your question... essentially [self presentModalViewController:home animated:YES]; ends up calling -retain on home, so that it will not be destroyed until you dismiss the modal view controller. In affect when you call release or autorelease you aren't dealloc'ing the object, just telling the object:
"Hey, I don't need you anymore, and if no one else does either then free up all the memory that you grabbed earlier".
Your problem has nothing to do with "class running in the background" but more with how you manage your data.
When you present a modal view controller, its parent (the view controller you presented it from) isn't destroyed (unless you specifically release it, which would probably crash your app later). So if you're wondering whether its still in memory; it is. As for tasks still running, it depends on what those tasks are. For example, you can still send it messages (call methods) and it will gladly receive those messages from your or from a delegate and perform whatever action it has to while it's off-screen.
Hope that helped.
In this case you are presenting new view controller. The main thread will be in the new controller presented. If you want something to run in background in the previous view controller then you can create a background thread. This can be done using [self perfomselectorInThebackground ... ] Or some other methods like GCD. (The main thing is you should not block Main thread)

Deallocating and removing UiButtons

I am trying to make a program that dynamically creates a button using the command:
[UIButton buttonWithType:UIButtonTypeRoundedRect]
But when I use these commands the delete the button I create:
[currentButton removeFromSuperview];
[currentButton dealloc];
[currentButton release];
I receive an error. How would I go about removing and deallocating the memory from a uibutton?
I got this problem long time ago, please notice that
[UIButton buttonWithType:UIButtonTypeRoundedRect]
has autorelease inside, so in your initialisation you need to do retain, like this:
_mybutton = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
and later point, you can do:
[_mybutton release];
Hope helps
:)
In the Objective-C/Cocoa framework, you encounter two different ways to receive objects: ones which you have explicitly allocated memory for (via a constructor) and ones that you have received memory reference to (via a class method).
FooBar *fone = [[FooBar alloc] initWithText:#"Hello, World!"];
In this example, memory is explicitly being allocated for the object by your call, using the alloc method, and then it is being initialized with data using the initWithText method that would have a method header like this:
- (id)initWithText:(NSString *)text;
On the other hand, you also will encounter objects that are created by classes automatically for you. An example of this would be below:
FooBar *ftwo = [FooBar fooBarWithWelcomeText];
In this example, a FooBar object is being returned even though we are not calling alloc to allocate memory for it. There are many different reasons to implement the method like this, but its mainly done to abstract certain details from the code that is using the object. The above example would have a corresponding method header like this:
+ (FooBar *)fooBarWithWelcomeText;
Depending on which approach is used, it changes how you interact with the memory of the object. So for the first example, after allocating the memory for the object you receive it back with a retain count of 1. If you are done using the object, you need to explictly release it with
[fone release];
In the second example, you are receiving an autoreleased object, which will be deallocated once the autoreleasepool is drained. If you want to keep it, you must explicitly retain with:
[ftwo retain];
If you do not wish to retain it, you can just leave it as is and it will be deallocated automatically. You can tell a method uses autorelease by two characteristics: 1) you will not utilize alloc when you receive the object; and 2) there will be a "+" next to the method heading. This means that the method is declared as a class method (similar to Java static methods).
So to finally answer your specific situation, you only need to make sure that the retain count is lowered to 1 (The only object having a reference to it was the autorelease pool). In your example, it would be done like this:
UIButton *currentButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[someView addSubView:currentButton];
// some code later
[currentButton removeFromSuperview];
You do not need the release statements because you never explicitly retained it. When you added the UIButton to another view, it retained the object so it incremented the reference count to 2. By removing it from the view, it is lowered back down to 1 so that when the autorelease pool is flushed, your UIButton will be deallocated. Btw, never call the dealloc method directly. When the retain count of an object is decremented to 0, the dealloc method will automatically be called.
You can't not dealloced the UIButton which you get through buttonWithType from system,if you don't alloced any instance then you are not entitled to call release on that.
In your case, you can use removeFromSuperview but not either dealloc or release .
You can't call dealloc directly on object, this is invoked by the system when you say release on object.
EDITED:
you could create you button using initWithFrame function of UIView. But you will get only the button type UIButtonTypeCustom which is by default, and also can't not change the button type because it's readonly property. So you would get the rounded button by using your some images.
You're not supposed to call dealloc directly. Try removing the dealloc line and see how that works.

Where I should call [object release]?

I've subclassed some UITextField and added some custom properties.
In a UITableViewController, in the ViewDiDLoad I init them, and in the cellForRowAtIndexPath I add them to the cell with [cell.contentView addSubview:customTextField];
Each cell has a different customTextField as all of them are very different.
Where I should call the [customTextField release] ?
After I add them to the cell view ?
If for example I call [self.tableView reloadData] my customTextField are going to be added again to the cell, so maybe I should change my approach in doing this ?
thanks for the orientation ...
regards,
r.
You release an object when you no longer have any interest in it. This happens for many reasons; it might be because you've finished with the object, or have passed the control over the object lifetime to another object. It might be because you're about to replace the object with a fresh instance, it might be because you (the owner object) are about to die.
The last one seems to be relevant in your case. You create these object in viewDidLoad and repeatedly need them (i.e. to add them to cells) until your object is no longer functioning. In this case, just as you create them in viewDidLoad, you can release them in viewDidUnload.
Edit: I should really mention autorelease, but this isn't relevant in this instance. Memory management is best handled with a notion of 'owner' - the person who creates something (or retains it) should be responsible for deleting it (or releaseing in ObjC parlance). autorelease handle some cases where you need to give an object to an alternate owner, having previously owned it yourself (typically via a method return). If you are the creator, you can't just release it before returning it to the new owner, as it will be deleted before the new owner has a chance to stake an interest in it. However, you can't just not release it; it will leak. As such, the system provides a big list of objects that it will release on your behalf at some point in the future. You pass your release responsibility to this list, then return the object to the new owner. This list (the autorelease pool) ensures your release will occur at some point, but gives the new owner a chance to claim the object as theirs before it's released.
In your case, you have a clear interest in owning the objects for the lifetime of your view controller - you need to, at any time, be able to add them to view cells in response to a table data reload. You're only done with them when your view controller dies, so the viewDidUnload (or possibly dealloc) is the only sensible place to release them.
I always release my controls directly after I added them to a view using addSubView. When I work with tables, I also initialize them in the cellForRowAtIndexPath method.
Therefor the object stays alive the shortest time.
Adam Wright explains the theory of this very well, but let me give you some practice. You're thinking about this problem far too hard, and that almost always leads to bugs. There is a simple solution that solves this problem almost every time: Retain all ivars using accessors; don't retain non-ivars.
.h
#interface ... {
UITextField *_customTextField;
}
.m
#property (nonatomic, readwrite, retain) UITextField *customTextField;
...
#synthesize customTextField=_customTextField;
-(void)viewDiDLoad {
self.customTextField = [[[UITextField alloc] init....] autorelease];
}
...
- (void)dealloc {
// I do not recommend accessors in dealloc; but everywhere else I do
[_customTextField release]; _customTextField = nil;
}
Never access your ivars directly, except in dealloc (even that is controversial and some people recommend self.customTextField = nil; in dealloc; there are arguments either way). But never assign your ivars directly. If you will follow this rule, you will find that most of your memory problems go away.
The safest way to handle object ownership is to autorelease the view directly after initialization:
FooTextField* textField = [[[FooTextField alloc] init] autorelease];
[myCell.contentView addSubview:textField];
Adding the text field to a superview (the UITableViewCell's contentView) retains it. This way you don't have to care about releasing the view afterwards.
There seems to be a resentment against autorelease in the iPhone developer community. In my opinion, this resentment is unfounded. Autoreleasing an object adds very little overhead to the program if the objects lives longer than the current pass through the run loop.
Every object in Obj-C has a reference counter (retainCount), and when this counter goes to 0 the object is deallocated. When you allocate and initialize an object, the reference count is set to 1 - but you can retain it as many times you want to.
UITextField *textField = [[UITextField alloc] init]; // Reference counter = 1
[textField retain]; // Reference counter = 2
[textField retain]; // Reference counter = 3
The opposite of retain is release, which subtracts from the reference counter;
...
[textField release]; // Reference counter = 2
[textField release]; // Reference counter = 1
You can always get the reference counter of your objects;
printf("Retain count: %i", [textField retainCount]);
The method addSubview of UIView does retain your passed in sub view - and when it's done with it it releases it. If you need your UITextField later, in another scope (when the UIView is done with it and has released it) - you should not release it after you've added it to the super view. Most of the time you actually don't need to hold on to a reference, so you should release it after you've added it to the super view. If you dont - you can release it in the dealloc method of your scope.
Take a look at UITableView -dequeueReusableCellWithIdentifier: and -initWithStyle:reuseIdentifier:.
In -tableView:cellForRowAtIndexPath:, use -dequeueReusableCellWithIdentifier: and check if the result is nil. If it is, instantiate a new cell with -initWithStyle:reuseIdentifier:.
Send -autorelease to your customTextField upon creation and adding to the respective cell.
You should not add subview in cellForRowAtIndexPath! This will slow down the view as you add a subview each time the cell is displayed. Try using custom UITableViewCell class for that purpose.
Here is a perfect solution of UITableView customization
http://cocoawithlove.com/2009/04/easy-custom-uitableview-drawing.html
works jut perfectly